使用 opencv python 从表单中检测复选框
detect checkboxes from a form using opencv python
给定一个牙科表格作为输入,需要使用图像处理找到表格中存在的所有复选框。我已经在下面回答了我目前的做法。有没有更好的方法来找到低质量文档的复选框?
示例输入:
这是我们可以解决问题的一种方法,
import cv2
import numpy as np
image=cv2.imread('path/to/image.jpg')
### binarising image
gray_scale=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
th1,img_bin = cv2.threshold(gray_scale,150,225,cv2.THRESH_BINARY)
定义垂直和水平内核
lineWidth = 7
lineMinWidth = 55
kernal1 = np.ones((lineWidth,lineWidth), np.uint8)
kernal1h = np.ones((1,lineWidth), np.uint8)
kernal1v = np.ones((lineWidth,1), np.uint8)
kernal6 = np.ones((lineMinWidth,lineMinWidth), np.uint8)
kernal6h = np.ones((1,lineMinWidth), np.uint8)
kernal6v = np.ones((lineMinWidth,1), np.uint8)
检测水平线
img_bin_h = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1h) # bridge small gap in horizonntal lines
img_bin_h = cv2.morphologyEx(img_bin_h, cv2.MORPH_OPEN, kernal6h) # kep ony horiz lines by eroding everything else in hor direction
寻找垂直线
## detect vert lines
img_bin_v = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1v) # bridge small gap in vert lines
img_bin_v = cv2.morphologyEx(img_bin_v, cv2.MORPH_OPEN, kernal6v)# kep ony vert lines by eroding everything else in vert direction
合并垂直线和水平线以获得块。添加一层膨胀以去除小间隙
### function to fix image as binary
def fix(img):
img[img>127]=255
img[img<127]=0
return img
img_bin_final = fix(fix(img_bin_h)|fix(img_bin_v))
finalKernel = np.ones((5,5), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,finalKernel,iterations=1)
对二值图像应用连通分量分析以获得所需的块。
ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
### skipping first two stats as background
for x,y,w,h,area in stats[2:]:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
你也可以用contours来解决这个问题。
# Reading the image in grayscale and thresholding it
Image = cv2.imread("findBox.jpg", 0)
ret, Thresh = cv2.threshold(Image, 100, 255, cv2.THRESH_BINARY)
现在执行膨胀和腐蚀两次以连接方框内的虚线。
kernel = np.ones((3, 3), dtype=np.uint8)
Thresh = cv2.dilate(Thresh, kernel, iterations=2)
Thresh = cv2.erode(Thresh, kernel, iterations=2)
在带有cv2.RETR_TREE标志的图像中查找轮廓,以获取具有父子关系的所有轮廓。 For more info on this.
Contours, Hierarchy = cv2.findContours(Thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
现在检测到图像中的所有框以及所有字母。我们必须消除检测到的字母、非常小的轮廓(由于噪声),以及那些内部包含较小框的框。
为此,我 运行 一个 for 循环迭代所有检测到的轮廓,并使用这个循环我为 3 个不同列表中的每个轮廓保存 3 个值。
- 第一个值:轮廓面积(轮廓包围的像素数)
- 第二个值:Contour 的 bounding rectangle 信息。
- 第三个值:等高线面积与其外接矩形面积之比。
Areas = []
Rects = []
Ratios = []
for Contour in Contours:
# Getting bounding rectangle
Rect = cv2.boundingRect(Contour)
# Drawing contour on new image and finding number of white pixels for contour area
C_Image = np.zeros(Thresh.shape, dtype=np.uint8)
cv2.drawContours(C_Image, [Contour], -1, 255, -1)
ContourArea = np.sum(C_Image == 255)
# Area of the bounding rectangle
Rect_Area = Rect[2]*Rect[3]
# Calculating ratio as explained above
Ratio = ContourArea / Rect_Area
# Storing data
Areas.append(ContourArea)
Rects.append(Rect)
Ratios.append(Ratio)
过滤掉不需要的轮廓:
- 获取面积小于 3600(此图像的阈值)且比率 >= 0.99 的等高线的索引。
该比率定义轮廓与其边界矩形的重叠范围。在这种情况下,所需的轮廓是矩形的,它们的这个比率预计为“1.0”(0.99 用于保持小噪声的阈值)。
BoxesIndices = [i for i in range(len(Contours)) if Ratios[i] >= 0.99 and Areas[i] > 3600]
- 现在最终轮廓是那些在索引“BoxesIndices”处没有子轮廓的轮廓(这将提取最里面的轮廓),如果它们有子轮廓,那么这个子轮廓不应该是轮廓之一在索引“BoxesIndices”处。
FinalBoxes = [Rects[i] for i in BoxesIndices if Hierarchy[0][i][2] == -1 or BoxesIndices.count(Hierarchy[0][i][2]) == 0]
给定一个牙科表格作为输入,需要使用图像处理找到表格中存在的所有复选框。我已经在下面回答了我目前的做法。有没有更好的方法来找到低质量文档的复选框?
示例输入:
这是我们可以解决问题的一种方法,
import cv2
import numpy as np
image=cv2.imread('path/to/image.jpg')
### binarising image
gray_scale=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
th1,img_bin = cv2.threshold(gray_scale,150,225,cv2.THRESH_BINARY)
定义垂直和水平内核
lineWidth = 7
lineMinWidth = 55
kernal1 = np.ones((lineWidth,lineWidth), np.uint8)
kernal1h = np.ones((1,lineWidth), np.uint8)
kernal1v = np.ones((lineWidth,1), np.uint8)
kernal6 = np.ones((lineMinWidth,lineMinWidth), np.uint8)
kernal6h = np.ones((1,lineMinWidth), np.uint8)
kernal6v = np.ones((lineMinWidth,1), np.uint8)
检测水平线
img_bin_h = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1h) # bridge small gap in horizonntal lines
img_bin_h = cv2.morphologyEx(img_bin_h, cv2.MORPH_OPEN, kernal6h) # kep ony horiz lines by eroding everything else in hor direction
寻找垂直线
## detect vert lines
img_bin_v = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1v) # bridge small gap in vert lines
img_bin_v = cv2.morphologyEx(img_bin_v, cv2.MORPH_OPEN, kernal6v)# kep ony vert lines by eroding everything else in vert direction
合并垂直线和水平线以获得块。添加一层膨胀以去除小间隙
### function to fix image as binary
def fix(img):
img[img>127]=255
img[img<127]=0
return img
img_bin_final = fix(fix(img_bin_h)|fix(img_bin_v))
finalKernel = np.ones((5,5), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,finalKernel,iterations=1)
对二值图像应用连通分量分析以获得所需的块。
ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
### skipping first two stats as background
for x,y,w,h,area in stats[2:]:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
你也可以用contours来解决这个问题。
# Reading the image in grayscale and thresholding it
Image = cv2.imread("findBox.jpg", 0)
ret, Thresh = cv2.threshold(Image, 100, 255, cv2.THRESH_BINARY)
现在执行膨胀和腐蚀两次以连接方框内的虚线。
kernel = np.ones((3, 3), dtype=np.uint8)
Thresh = cv2.dilate(Thresh, kernel, iterations=2)
Thresh = cv2.erode(Thresh, kernel, iterations=2)
在带有cv2.RETR_TREE标志的图像中查找轮廓,以获取具有父子关系的所有轮廓。 For more info on this.
Contours, Hierarchy = cv2.findContours(Thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
现在检测到图像中的所有框以及所有字母。我们必须消除检测到的字母、非常小的轮廓(由于噪声),以及那些内部包含较小框的框。
为此,我 运行 一个 for 循环迭代所有检测到的轮廓,并使用这个循环我为 3 个不同列表中的每个轮廓保存 3 个值。
- 第一个值:轮廓面积(轮廓包围的像素数)
- 第二个值:Contour 的 bounding rectangle 信息。
- 第三个值:等高线面积与其外接矩形面积之比。
Areas = []
Rects = []
Ratios = []
for Contour in Contours:
# Getting bounding rectangle
Rect = cv2.boundingRect(Contour)
# Drawing contour on new image and finding number of white pixels for contour area
C_Image = np.zeros(Thresh.shape, dtype=np.uint8)
cv2.drawContours(C_Image, [Contour], -1, 255, -1)
ContourArea = np.sum(C_Image == 255)
# Area of the bounding rectangle
Rect_Area = Rect[2]*Rect[3]
# Calculating ratio as explained above
Ratio = ContourArea / Rect_Area
# Storing data
Areas.append(ContourArea)
Rects.append(Rect)
Ratios.append(Ratio)
过滤掉不需要的轮廓:
- 获取面积小于 3600(此图像的阈值)且比率 >= 0.99 的等高线的索引。 该比率定义轮廓与其边界矩形的重叠范围。在这种情况下,所需的轮廓是矩形的,它们的这个比率预计为“1.0”(0.99 用于保持小噪声的阈值)。
BoxesIndices = [i for i in range(len(Contours)) if Ratios[i] >= 0.99 and Areas[i] > 3600]
- 现在最终轮廓是那些在索引“BoxesIndices”处没有子轮廓的轮廓(这将提取最里面的轮廓),如果它们有子轮廓,那么这个子轮廓不应该是轮廓之一在索引“BoxesIndices”处。
FinalBoxes = [Rects[i] for i in BoxesIndices if Hierarchy[0][i][2] == -1 or BoxesIndices.count(Hierarchy[0][i][2]) == 0]