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

How to remove whitespace from an image in OpenCV?

我有下面的图片,其中有文字,文字下方有很多白色 space。我想裁剪白色 space 使其看起来像第二张图片。

裁剪图像

这是我所做的

>>> 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]

正如许多人在评论中提到的那样,最好的方法是反转图像,使黑色文本变为白色,找到图像中的所有非零点,然后确定最小跨度边界框是什么。您可以使用此边界框最终裁剪图像。查找轮廓非常昂贵,这里不需要 - 特别是因为您的文本是轴对齐的。您可以使用 cv2.findNonZero and cv2.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

注意:由于隐私原因删除了输出图像

Opencv 将图像读取为 numpy 数组,直接使用 numpy 更简单(scikit-image 也是如此)。一种可能的方法是将图像读取为灰度图像或转换为灰度图像,然后执行 row-wise 和 column-wise 操作,如下面的代码片段所示。当所有像素都为 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

和输出:

这也行得通:

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()