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

时间:2019-01-29 22:51:28

标签: python opencv image-processing optimization

我正在处理扫描的文档(身份证,驾驶执照等)。在对它们进行一些预处理时,我遇到的问题是文档仅占据图像的一小部分,其余所有区域都是空白/噪点空间。因此,我想开发一个Python代码,该代码自动修剪不需要的区域并仅保留文档所在的区域(我没有为每个文档预先定义分辨率) 。好的,可以使用OpenCV中的findContours()。但是,大多数文件(尤其是旧文件)轮廓不清晰,并且其末端不够清晰,无法被检测到。另外,空白处的噪声也可以检测为轮廓,因此轮廓不能在所有情况下都起作用。
我想到的是:

  1. 读取图像并将其转换为灰度。
  2. 从OpenCV应用bitwise_not()函数以分隔 的背景。
  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)

这里是一个例子:我使用互联网上的图片来避免任何机密性问题
这里要注意的是,图像质量比我研究的示例要好得多(空白处不包含噪点)。
输入:1920x1080
Input
输出:801x623
Output

我使用其他文档测试了此代码,并且效果很好。问题在于处理单个文档要花费很多时间(由于循环并读取图像的每个像素两次:一次是行,第二次是列)。
是否可以进行一些修改以优化代码并减少处理时间?

任何建议都值得欢迎。
谢谢。

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

2 个答案:

答案 0 :(得分:2)

这是我的方法,请检查一下:

increment:$_SESSION['count']

enter image description here

enter image description here

enter image description here

答案 1 :(得分:0)

@Ha Bom交换意见后,我得到了一个更优化的解决方案,在该解决方案中,我按照他的建议使用了findContour。这是我结束的代码:

import cv2 
import numpy as np
def func(indir, filename, outdir):
    img = cv2.imread(indir + filename)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    bit = cv2.bitwise_not(gray)
    bit = bit[50:bit.shape[0] -50, 50:bit.shape[1] - 50]
    amtImage = cv2.adaptiveThreshold(bit, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 35, 15)
    kernel = np.ones((5,5),np.uint8)
    dilation = cv2.dilate(amtImage,kernel,iterations = 2)
    kernel = np.ones((25,25),np.uint8)
    erosion = cv2.erode(dilation, kernel, iterations = 10)
    bit = cv2.bitwise_not(erosion)
    _, contours, hierarchy = cv2.findContours(bit,  cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    if (contours != 0):
        c = max(contours, key = cv2.contourArea)
        x,y,w,h = cv2.boundingRect(c)
        print(x, y, w, h)
    final = img[max(0, (y - 50)):(y + h) + min(250, (img.shape[0] - (y + h)) * 10 // 11), max(0, (x - 50)):(x + w) + min(250, (img.shape[1] - (x + w)) * 10 // 11)]
    cv2.imwrite(outdir + filename, final)

在这段代码中,我既没有义务遍历图像的每个像素,也没有义务保留索引列表。这样更快了!
我确信可以进一步优化此代码,这就是为什么我不接受答案的原因。 谢谢大家。