如何从 OpenCV 中的图像中删除空格?

Posted

技术标签:

【中文标题】如何从 OpenCV 中的图像中删除空格?【英文标题】:How to remove whitespace from an image in OpenCV? 【发布时间】:2018-09-29 03:34:16 【问题描述】:

我有以下图像,其中包含文本和文本下方的大量空白。我想裁剪空白区域,使其看起来像第二张图像。

裁剪图像

这就是我所做的

>>> img = cv2.imread("pg13_gau.jpg.png")
>>> gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
>>> edged = cv2.Canny(gray, 30,300)
>>> (img,cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
>>> cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:10]

【问题讨论】:

只裁剪boundingRect的黑色像素 boundingRect 采用轮廓。我应该使用findContour 找到轮廓吗?如何获得左上角黑色像素和右下角黑色像素? boundingRect 采用点向量...无需查找轮廓。 C++ 代码将只是:std::vector<cv::Point> pts; cv::findNonZero(~gray); cv::Rect roi = cv::boundingRect(pts); cv::Mat1b crop = img(roi); Python 不会有太大不同 从HERE查看第7节cv2.boundingRect() 我执行了以下操作,但生成的图像不是裁剪后的图像:img = cv2.imread("ws.png"); gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY); coords = cv2.findNonZero(gray); rekt = cv2.boundingRect(coords) cv2.imwrite("rekt.png",rekt) 【参考方案1】:

正如许多人在 cmets 中提到的那样,最好的方法是反转图像,使黑色文本变为白色,找到图像中的所有非零点,然后确定最小跨度边界框是多少。您可以使用此边界框最终裁剪图像。查找轮廓非常昂贵,在这里不需要 - 特别是因为您的文本是轴对齐的。您可以结合使用cv2.findNonZerocv2.boundingRect 来满足您的需求。

因此,这样的事情会起作用:

import numpy as np
import cv2

img = cv2.imread('ws.png') # Read in the image and convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255*(gray < 128).astype(np.uint8) # To invert the text to white
coords = cv2.findNonZero(gray) # Find all non-zero points (text)
x, y, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box
rect = img[y:y+h, x:x+w] # Crop the image - note we do this on the original image
cv2.imshow("Cropped", rect) # Show it
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("rect.png", rect) # Save the image

上面的代码正是我一开始所说的。我们在图像中读取,但我们也转换为灰度,因为您的图像由于某种原因是彩色的。棘手的部分是第三行代码,我将阈值设置为低于 128 的强度,以便深色文本变为白色。然而,这会产生一个二进制图像,所以我转换为uint8,然后按 255 缩放。这实质上是反转文本。

接下来,给定这张图片,我们找到所有带有cv2.findNonZero 的非零坐标,最后我们将其放入cv2.boundingRect,这将为您提供边界框的左上角以及宽度和高度。我们终于可以使用它来裁剪图像了。请注意,我们在原始图像而不是倒置图像上执行此操作。我们简单地使用 NumPy 数组索引来为我们进行裁剪。

最后,我们显示图像以表明它可以工作,并将其保存到磁盘。


我现在得到了这张图片:


对于第二张图片,一个好的做法是去掉一些右边框和下边框。我们可以通过首先将图像裁剪到该图像来做到这一点。接下来,该图像包含一些非常小的噪声像素。我建议使用非常小的内核进行形态学打开,然后重做我们上面讨论的逻辑。

因此:

import numpy as np
import cv2

img = cv2.imread('pg13_gau_preview.png') # Read in the image and convert to grayscale
img = img[:-20,:-20] # Perform pre-cropping
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255*(gray < 128).astype(np.uint8) # To invert the text to white
gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, np.ones((2, 2), dtype=np.uint8)) # Perform noise filtering
coords = cv2.findNonZero(gray) # Find all non-zero points (text)
x, y, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box
rect = img[y:y+h, x:x+w] # Crop the image - note we do this on the original image
cv2.imshow("Cropped", rect) # Show it
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("rect.png", rect) # Save the image

注意:输出图片因隐私原因被删除

【讨论】:

@Anthony 您必须先删除一些右边框以及一些沿臀部的像素,然后才能使用。如果存在像您在图像右侧看到的那样的杂散斑点,则该代码不起作用。在选择边界框之前先尝试对图像执行此操作:img = img[:-20,:-20]。这将删除行和列的最后 20 个像素。 啊,我明白了,因为那时图像中会有两部分黑色。有没有办法以编程方式删除它,以便裁剪按需要进行? @Anthony 是的,你绝对可以去除边框像素。在 MATLAB 中,有一个名为 imclearborder 的函数可以删除任何连接到边框的像素。 OpenCV 没有这个功能,但我在之前提供的答案中自己写了一个:***.com/questions/24731810/…。请注意,这可能需要大量计算,因为该过程是查找图像中的所有轮廓,然后检查是否有任何轮廓接触图像中的任何边界像素。然后我将这些轮廓的区域涂成黑色。 @DanMašek 我知道。我现在对此无能为力,但第二张图片的底部有一个非常小的黑点。如果我们在反转后找到所有非零像素,则将包含这个黑点,因此包含这组点的边界框将拉伸以包含这个嘈杂的黑点。我只有在下载并在 GIMP 上查看后才能看到它。在进行裁剪之前,我用形态学删除了它。 您可以将其标记为版主将其删除,或直接向 Imgur 发送 removal request。【参考方案2】:

Opencv 将图像读取为 numpy 数组,直接使用 numpy 会简单得多(scikit-image 也是如此)。一种可能的方法是将图像读取为灰度或转换为灰度图像,然后执行逐行和逐列操作,如下面的代码 sn-p 所示。当所有像素为pixel_value(本例中为白色)时,这将删除列和行。

def crop_image(filename, pixel_value=255):
    gray = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
    crop_rows = gray[~np.all(gray == pixel_value, axis=1), :]
    cropped_image = crop_rows[:, ~np.all(crop_rows == pixel_value, axis=0)]
    return cropped_image

和输出:

【讨论】:

【参考方案3】:

这也可以:

from PIL import Image, ImageChops

img = Image.open("pUq4x.png")
pixels = img.load()

print (f"original: img.size[0] x img.size[1]")
xlist = []
ylist = []
for y in range(0, img.size[1]):
    for x in range(0, img.size[0]):
        if pixels[x, y] != (255, 255, 255, 255):
            xlist.append(x)
            ylist.append(y)
left = min(xlist)
right = max(xlist)
top = min(ylist)
bottom = max(ylist)

img = img.crop((left-10, top-10, right+10, bottom+10))
img.show()


【讨论】:

不是最优的,可以在for循环里面保存一个max和min的记录。 能否将数组转换为 NumPy 并使用 np.minnp.max?在 Python 中用于图像处理的双 for 循环不是首选。

以上是关于如何从 OpenCV 中的图像中删除空格?的主要内容,如果未能解决你的问题,请参考以下文章

从OpenCV中的图像中删除选定的元素

如何使用opencv删除图像中的船尾迹?

使用 python/opencv/深度学习从图像中删除给定位置的徽标/水印

使用 OpenCV 的图像处理从图像中删除背景文本和噪声

如何从网络摄像头 OpenCV 裁剪圆形图像并删除背景

如何使用opencv从图像中删除小的彩色像素