sin*_*ium 2 python optimization opencv image-processing
我正在处理扫描文件(身份证、驾照……)。当我对它们进行一些预处理时,我遇到的问题是文档仅占据图像的一小部分,所有其余区域都是空白/噪声空间。出于这个原因,我想开发一个 Python 代码,自动修剪不需要的区域并仅保留文档所在的区域(无需我预先定义每个文档的分辨率)。findContours()使用OpenCV就可以实现这一点。然而,大多数文档(尤其是旧文档)轮廓不清晰,四肢不够清晰,无法被检测到。另外,空白区域中的噪声也可以被检测为轮廓。因此轮廓并不适用于所有情况。
我的想法是:
bitwise_not()OpenCV 的函数将背景与前景分开。在这个级别,我的背景几乎是白色的,文档是黑色的,但包含一些白色间隙。
这是我的代码和一些评论:
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。由于我没有得到版主的答复,我决定将其发布在这里,因为我认为这也是这里的主题。一旦我在其中一个网站上得到答案,我就会删除另一个网站上的问题以避免重复。
这是我的方法,你看一下:
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)