使用OpenCV检测复选框是否被选中

4
我正在尝试查找复选框([Checked Checkbox])1

是否被选中。到目前为止,我尝试使用pyimagesearch中所用的方法。我想检查图像中是否有任何三角形。如果至少有一个三角形,则意味着复选框已被选中。我发现的问题是,有时会检测到外部矩形而非三角形。我应该如何忽略矩形并仅检测三角形?

1
我之前解决过这个任务。我有一个包含已检查/未检查复选框的数据集,并基于这些数据构建了分类器。我尝试过随机森林和KNN两种算法,它们都给出了几乎完美的结果,并且速度也相当快。数据集很小,每个类别大约只有40个样本。 - Dmitry
谢谢,我已经有一个CNN的解决方案了。我想用OpenCV来实现它。 - rawwar
1
我猜图像中包含未勾选的矩形或带有对角线的矩形,这种情况下,您可以检查像素密度并使用某些预定义的阈值进行分类。否则,您可以分析轮廓层次结构进行分类。 - flamelite
@flamelite,谢谢你。我想我可以使用轮廓层次结构,我正在探索它。如果你已经足够了解它,能否帮助我理解如何将其用于我的问题。 - rawwar
1
@ChauLoi 很抱歉回复晚了。如果我没记错的话,我只使用了原始像素,并将它们输入分类器中。 - Dmitry
显示剩余4条评论
2个回答

9
我认为你甚至不需要使用层次信息,只需计算轮廓数量并用于对图像进行分类。 我提供了一个简单的Python实现来查看单个轮廓。
OpenCV中的轮廓层次结构表示在这里已经解释Contour Hierachy
img = cv2.imread(r'D:/Image/cross.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,bw = cv2.threshold(gray,220,255,cv2.THRESH_BINARY_INV)
_,contours,hierarchy = cv2.findContours(bw, cv2.RETR_CCOMP,1)
cntLen = 10
ct = 0 #number of contours
for cnt in contours:
    if len(cnt) > cntLen: #eliminate the noises
        ct += 1
        newimg = img.copy()
        cv2.drawContours(newimg,[cnt],0,(0,0,255),2)
        cv2.imshow('Win', newimg)
        cv2.waitKey(0)
print('Total contours: ',ct)

嘿,我想知道你是否有更好的方法,因为这种方法对我的情况不好用。请查看我的作品:https://dev59.com/Pbzpa4cB1Zd3GeqPGDCa - Chau Loi

4

我最终需要做的事情与这个非常相似,需要确定大约2500封信封上是否有支票。使用此处的答案,我能够以约10%的误差率完成它。我最终采取了以下步骤:

首先,我通过一个PixelDensity脚本对我的“未检查”样本进行处理。我正在检查“白色”像素的数量:

import cv2
import numpy as np
from pathlib import Path
from os import listdir
import functools

whitePixelAverage = []

path = "./"
included_extensions = ['png','PNG']

allFiles = [f for f in listdir(path) if any(f.endswith(ext) for ext in included_extensions)] # Get all files in current directory

length = len(allFiles)

for i in range(length):
    img = cv2.imread(allFiles[i], cv2.IMREAD_GRAYSCALE)
    imgCrop = img[655:713,692:767] # Select your ROI here, I allow for a bit of deviation between image scans
    n_white_pix = np.sum(imgCrop == 255)
    print('number of white pixels: ', n_white_pix)
    whitePixelAverage.append(n_white_pix)
print(whitePixelAverage)
print("Average: ", functools.reduce(lambda x, y: x + y,whitePixelAverage)/len(whitePixelAverage))
print("Lowest: ", min(whitePixelAverage))
print("highest: ", max(whitePixelAverage))

t = input(":") # Wait for input to close

从这个选择中,我最终选择了我的“最低”值,并将其阈值设为3%(在我的情况下约为117)。

现在,我做同样的事情,但这次是用“已选”框的样本。这些的白色像素密度应该平均较低。

对于我的已选框,我使用平均值。我最终将已选框的平均值与未选框的最低值+阈值进行比较:

from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
import imutils
import cv2
from pathlib import Path
from os import listdir
import os
import shutil
import functools

path = "./testSet/"
checkedP = "./testSet/checked/"
uncheckedP = "./testSet/unchecked/"
included_extensions = ['png','PNG']

THRESHOLD = 117

allFiles = [f for f in listdir(path) if any(f.endswith(ext) for ext in included_extensions)] # Get all files in current directory

length = len(allFiles)

for i in range(length):
    img = cv2.imread(path+allFiles[i])
    imgCrop = img[655:713,692:767]
    gray = cv2.cvtColor(imgCrop, cv2.COLOR_BGR2GRAY)
    ret,bw = cv2.threshold(gray,220,255,cv2.THRESH_BINARY_INV)
    _,contours,hierarchy = cv2.findContours(bw, cv2.RETR_CCOMP,1)
    cntLen = 1
    ct = 0 #number of contours
    for cnt in contours:
        if len(cnt) > cntLen: #eliminate the noises
            ct += 1
    print('Total contours: ',ct)
    if ct >= 3:
        print("Checked!")
        shutil.move(path + allFiles[i], checkedP + allFiles[i])
        img = cv2.imread(checkedP+allFiles[i], cv2.IMREAD_GRAYSCALE)
        imgCrop = img[655:713,692:767]
        n_white_pix = np.sum(imgCrop == 255)
        if (n_white_pix > 3747+THRESHOLD):
            shutil.move(checkedP + allFiles[i], checkedP+"Unconfident/")
        averageWhitePixels.append(n_white_pix)
    else:
        print("Not so checked")
        shutil.move(path + allFiles[i], uncheckedP + allFiles[i])
        img = cv2.imread(uncheckedP+allFiles[i], cv2.IMREAD_GRAYSCALE)
        imgCrop = img[655:713,692:767]
        n_white_pix = np.sum(imgCrop == 255)
        if (n_white_pix < 3747+THRESHOLD):
            shutil.move(uncheckedP + allFiles[i], uncheckedP+"Unconfident/")

除了flamelite的答案外,比较像素密度,我能够在2500个不同的样本中将错误率从10%降低到约2.47%。

对于我的代码有多么不优雅,我感到非常抱歉。这是我的第一个答案,请告诉我是否有任何问题。很可能还有很多用于故障排除的东西留在那里。

希望这有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接