我正在处理扫描的文档(身份证,驾驶执照等)。在对它们进行一些预处理时,我遇到的问题是文档仅占据图像的一小部分,其余所有区域都是空白/噪点空间。因此,我想开发一个Python代码,该代码自动修剪不需要的区域并仅保留文档所在的区域(我没有为每个文档预先定义分辨率) 。好的,可以使用OpenCV中的findContours()
。但是,大多数文件(尤其是旧文件)轮廓不清晰,并且其末端不够清晰,无法被检测到。另外,空白处的噪声也可以检测为轮廓,因此轮廓不能在所有情况下都起作用。
我想到的是:
bitwise_not()
函数以分隔
的背景。在这个级别上,我的背景几乎是白色的,文档是黑色的,但是有些空白。
这是我的代码,带有一些注释:
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
输出:801x623
我使用其他文档测试了此代码,并且效果很好。问题在于处理单个文档要花费很多时间(由于循环并读取图像的每个像素两次:一次是行,第二次是列)。
是否可以进行一些修改以优化代码并减少处理时间?
任何建议都值得欢迎。
谢谢。
编辑:
我忘了提到我已经在Code review Stack Exchange中发布了相同的问题,但没有得到答案。因此,我标记了问题,并要求主持人将其迁移到StakOverflow。而且由于我没有收到主持人的答复,因此我决定将其发布在这里,因为我也认为这也是主题。在其中一个网站上获得答案后,我将在另一个网站上删除我的问题,以避免重复。
答案 0 :(得分:2)
答案 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)
在这段代码中,我既没有义务遍历图像的每个像素,也没有义务保留索引列表。这样更快了!
我确信可以进一步优化此代码,这就是为什么我不接受答案的原因。
谢谢大家。