如何使用OpenCV检测/查找复选框轮廓 [英] How to detect/find checkbox contours using OpenCV

查看:103
本文介绍了如何使用OpenCV检测/查找复选框轮廓的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几张图像需要通过计算机视觉检测复选框来执行OMR.

I have several images for which i need to do OMR by detecting checkboxes using computer vision.

我正在使用findContours仅在扫描文档中的复选框上绘制轮廓.但是算法会提取文本的每个轮廓.

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()

输入图像:

推荐答案

由于我们只想检测复选框,所以我们的想法是使用两种过滤方法从单词中分离出所需的复选框.在预处理并找到轮廓之后,我们可以遍历每个轮廓并应用滤镜.我们使用具有最小和最大阈值的 cv2.contourArea() 级别,然后使用 cv2.approxPolyDP() ,因为正方形的长宽比接近1.

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.

接下来,我们使用阈值区域和宽高比对每个检测到的轮廓进行迭代并进行过滤.使用此方法,检测到所有52个复选框.

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.

输出

('checkbox_contours',52)

('checkbox_contours', 52)

为了防止潜在的误报,我们可以添加第三个过滤器,以确保每个轮廓都有四个点(更大的机会是正方形).如果输入图像是从一个角度来看的,我们可以使用

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.

另一个输入图像集

输出

('checkbox_contours',2)

('checkbox_contours', 2)

代码

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)

回到此问题后,这里是一个更可靠的解决方案.这个想法非常相似,除了我们使用

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检测/查找复选框轮廓的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆