问题描述
我有几个图像需要通过使用计算机视觉检测复选框来进行 OMR.
我使用 findContours
仅在扫描文档的复选框上绘制轮廓.但是算法会提取文本的每一个轮廓.
from imutils.perspective importfour_point_transform从 imutils 导入轮廓将 numpy 导入为 np导入 argparse、imutils、cv2、matplotlib导入 matplotlib.pyplot 作为 plt将 matplotlib.image 导入为 mpimg图像 = cv2.imread(1.jpg")灰色 = cv2.cvtColor(图像,cv2.COLOR_BGR2GRAY)模糊 = cv2.GaussianBlur(gray, (5, 5), 0)边缘 = cv2.Canny(模糊,75, 200)im_test = [blurred, cv2.GaussianBlur(gray, (7, 7), 0), cv2.GaussianBlur(gray, (5, 5), 5), cv2.GaussianBlur(gray, (11, 11), 0)]im_thresh = [ cv2.threshold(i, 127, 255, 0) for i in im_test ]im_thresh_0 = [i[1] for i in im_thresh ]im_cnt = [cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0] 用于 im_thresh_0 中的 thresh]im_drawn = [cv2.drawContours(image.copy(), contours, -1, (0,255,0), 1) 用于 im_cnt 中的轮廓]plt.imshow(im_drawn[0])plt.show()
输入图像:
由于我们只想检测复选框,所以想法是使用两种过滤方法将所需的框与单词隔离.在预处理并找到轮廓之后,我们可以遍历每个轮廓并应用过滤器.我们使用
接下来,我们使用阈值面积和纵横比遍历每个检测到的轮廓和过滤器.使用这种方法,检测到所有 52 个复选框.
输出
('checkbox_contours', 52)
为了防止潜在的误报,我们可以添加第三个过滤器以确保每个轮廓有四个点(更高的机会是正方形).如果输入图像来自某个角度,我们可以使用 而不是 Canny 边缘检测来获得二值图像.Otsu 的阈值会自动计算阈值,因此应该会给出更好的结果.从这里我们找到轮廓,使用轮廓近似、纵横比和轮廓区域进行过滤.结果应该是一样的.
导入 cv2# 加载图像,转换为灰度,大津阈值图像 = cv2.imread(1.jpg")灰色 = cv2.cvtColor(图像,cv2.COLOR_BGR2GRAY)thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]# 查找轮廓,使用轮廓近似、纵横比和轮廓面积过滤阈值_最大_面积 = 550threshold_min_area = 100cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)cnts = cnts[0] 如果 len(cnts) == 2 else cnts[1]对于 cnts 中的 c:peri = cv2.arcLength(c, True)approx = cv2.approxPolyDP(c, 0.035 * peri, True)x,y,w,h = cv2.boundingRect(approx)aspect_ratio = w/float(h)面积 = cv2.contourArea(c)如果 len(approx) == 4 and area threshold_min_area 和(aspect_ratio >= 0.9 和 aspect_ratio
I have several images for which I need to do OMR by detecting checkboxes using computer vision.
I'm using findContours
to draw contours only on the checkboxes in scanned document. But the algorithm extracts each and every contours of the text.
from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
import argparse, imutils, cv2, matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
image = cv2.imread("1.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 75, 200)
im_test = [blurred, cv2.GaussianBlur(gray, (7, 7), 0), cv2.GaussianBlur(gray, (5, 5), 5), cv2.GaussianBlur(gray, (11, 11), 0)]
im_thresh = [ cv2.threshold(i, 127, 255, 0) for i in im_test ]
im_thresh_0 = [i[1] for i in im_thresh ]
im_cnt = [cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0] for thresh in im_thresh_0]
im_drawn = [cv2.drawContours(image.copy(), contours, -1, (0,255,0), 1) for contours in im_cnt]
plt.imshow(im_drawn[0])
plt.show()
Input Image:
Since we only want to detect checkboxes, the idea is to use two filtering methods to isolate the desired boxes from the words. After preprocessing and finding the contours, we can iterate through each contour and apply the filters. We use cv2.contourArea()
with minimum and maximum threshold levels and then calculate the aspect ratio using cv2.approxPolyDP()
since a square will have an aspect ratio close to 1.
To detect edges in the image, we can use cv2.Canny()
and then grab contours using cv2.findContours()
which results in this image. Notice how all contours including words and checkboxes were detected.
Next we iterate through each detected contour and filter using the threshold area and the aspect ratio. Using this method, all 52 checkboxes were detected.
Output
To prevent potential false positives, we can add a 3rd filter to ensure that each contour has four points (higher chance it is a square). If the input image was from an angle, we can use a four point transform as a preprocessing step to obtain a birds eye view of the image.
Another input image set
Output
Code
import numpy as np
import imutils, cv2
original_image = cv2.imread("1.jpg")
image = original_image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 120, 255, 1)
cv2.imshow("edged", edged)
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
checkbox_contours = []
threshold_max_area = 250
threshold_min_area = 200
contour_image = edged.copy()
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.035 * peri, True)
(x, y, w, h) = cv2.boundingRect(approx)
aspect_ratio = w / float(h)
area = cv2.contourArea(c)
if area < threshold_max_area and area > threshold_min_area and (aspect_ratio >= 0.9 and aspect_ratio <= 1.1):
cv2.drawContours(original_image,[c], 0, (0,255,0), 3)
checkbox_contours.append(c)
print('checkbox_contours', len(checkbox_contours))
cv2.imshow("checkboxes", original_image)
cv2.waitKey(0)
Edit:
After coming back to this problem, here's a more robust solution. The idea is very similar except we use Otsu's threshold instead of Canny edge detection to obtain the binary image. Otsu's threshold automatically calculates the threshold value so it should give better results. From here we find contours, filter using contour approximation, aspect ratio, and contour area. The result should be the same.
import cv2
# Load image, convert to grayscale, Otsu's threshold
image = cv2.imread("1.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours, filter using contour approximation, aspect ratio, and contour area
threshold_max_area = 550
threshold_min_area = 100
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.035 * peri, True)
x,y,w,h = cv2.boundingRect(approx)
aspect_ratio = w / float(h)
area = cv2.contourArea(c)
if len(approx) == 4 and area < threshold_max_area and area > threshold_min_area and (aspect_ratio >= 0.9 and aspect_ratio <= 1.1):
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.imshow("image", image)
cv2.imshow("thresh", thresh)
cv2.waitKey()
这篇关于使用 OpenCV 查找复选框轮廓的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!