从扫描图像中修剪空白噪声空间的更快方法

sin*_*ium 2 python optimization opencv image-processing

我正在处理扫描文件(身份证、驾照……)。当我对它们进行一些预处理时,我遇到的问题是文档仅占据图像的一小部分,所有其余区域都是空白/噪声空间。出于这个原因,我想开发一个 Python 代码,自动修剪不需要的区域并仅保留文档所在的区域(无需我预先定义每个文档的分辨率)。findContours()使用OpenCV就可以实现这一点。然而,大多数文档(尤其是旧文档)轮廓不清晰,四肢不够清晰,无法被检测到。另外,空白区域中的噪声也可以被检测为轮廓。因此轮廓并不适用于所有情况。
我的想法是:

  1. 读取图像并将其转换为灰度。
  2. 应用bitwise_not()OpenCV 的函数将背景与前景分开。
  3. 应用自适应平均阈值来消除尽可能多的噪声(并最终美化背景)。

在这个级别,我的背景几乎是白色的,文档是黑色的,但包含一些白色间隙。

  1. 所以我应用了侵蚀来填补文档部分的空白。
  2. 读取图像的每一行,如果其中20%包含黑色,则保留它,如果是白色,则删除它。并对图像的每一列执行相同的操作。
  3. 根据黑线和列的索引的最小值和最大值裁剪图像。

这是我的代码和一些评论:

import cv2
import numpy as np

def crop(filename):
    #Read the image
    img = cv2.imread(filename)
    #Convert to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    #Separate the background from the foreground
    bit = cv2.bitwise_not(gray)
    #Apply adaptive mean thresholding
    amtImage = cv2.adaptiveThreshold(bit, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 35, 15)
    #Apply erosion to fill the gaps
    kernel = np.ones((15,15),np.uint8)
    erosion = cv2.erode(amtImage,kernel,iterations = 2)
    #Take the height and width of the image
    (height, width) = img.shape[0:2]
    #Ignore the limits/extremities of the document (sometimes are black, so they distract the algorithm)
    image = erosion[50:height - 50, 50: width - 50]
    (nheight, nwidth) = image.shape[0:2]
    #Create a list to save the indexes of lines containing more than 20% of black.
    index = []
    for x in range (0, nheight):
        line = []

        for y in range(0, nwidth):
            line2 = []
            if (image[x, y] < 150):
                line.append(image[x, y])
        if (len(line) / nwidth > 0.2):  
            index.append(x)
    #Create a list to save the indexes of columns containing more than 15% of black.
    index2 = []
    for a in range(0, nwidth):
        line2 = []
        for b in range(0, nheight):
            if image[b, a] < 150:
                line2.append(image[b, a])
        if (len(line2) / nheight > 0.15):
            index2.append(a)

    #Crop the original image according to the max and min of black lines and columns.
    img = img[min(index):max(index) + min(250, (height - max(index))* 10 // 11) , max(0, min(index2)): max(index2) + min(250, (width - max(index2)) * 10 // 11)]
    #Save the image
    cv2.imwrite('res_' + filename, img)
Run Code Online (Sandbox Code Playgroud)

这是一个例子:我使用了来自互联网的图像,以避免任何保密问题。
这里要注意的是,图像质量比我处理的示例要好得多(空白不包含噪声)。
输入: 1920x1080
输入
输出: 801x623
输出

我用不同的文档测试了这段代码,效果很好。问题在于处理单个文档需要花费大量时间(因为循环并读取图像的每个像素两次:一次使用行,第二次使用列)。
是否可以做一些修改来优化代码并减少处理时间?

任何建议都非常受欢迎。
谢谢。

编辑:
我忘了提及,我已经在Code review Stack Exchange中发布了相同的问题,但我没有得到答案。所以我标记了这个问题并要求版主将其迁移到 StakOverflow。由于我没有得到版主的答复,我决定将其发布在这里,因为我认为这也是这里的主题。一旦我在其中一个网站上得到答案,我就会删除另一个网站上的问题以避免重复。

Ha *_*Bom 6

这是我的方法,你看一下:

import cv2
import numpy as np

img = cv2.imread("1.png")

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#Separate the background from the foreground
bit = cv2.bitwise_not(gray)

nonzero = np.nonzero(bit)

minx = min(nonzero[1])
maxx = max(nonzero[1])

miny = min(nonzero[0])
maxy = max(nonzero[0])

res = img[miny:maxy,minx:maxx].copy()

cv2.rectangle(img,(minx,miny),(maxx,maxy),(0,0,255),2)

cv2.imshow('img',img)
cv2.imshow('bit',bit)

cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

在此输入图像描述

在此输入图像描述