答题卡识别判卷

1,预处理

灰度图,高斯去噪,边缘检测

复制代码
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blurred=cv2.GaussianBlur(gray,(5,5),0)
# cv_show(blurred,"blurred")
edged=cv2.Canny(blurred,75,200)
# cv_show(edged,"canny")

2,找到答题卡

进行轮廓检测,近似轮廓

复制代码
#轮廓检测
cnts=cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),2)
cv_show(contours_img,"contours")

#近似轮廓
docCnt=None
if len(cnts)>0:
    cnts=sorted(cnts,key=cv2.contourArea,reverse=True)

    for c in cnts:
        per=cv2.arcLength(c,True)
        approx=cv2.approxPolyDP(c,0.02*per,True)

        #准备透视变换
        if len(approx)==4:
            docCnt=approx
            break

3,矫正答题卡

进行透视变换,获取近似四边形的四个顶点,对应好四个顶点的位置,将四边形的中最长的长宽作为矩形的长宽

复制代码
def oreder_points(pts):
    rect=np.zeros((4,2),dtype="float32")

    s=pts.sum(axis=1)
    rect[0]=pts[np.argmin(s)]
    rect[2]=pts[np.argmax(s)]

    diff=np.diff(pts,axis=1)
    rect[1]=pts[np.argmin(diff)]
    rect[3]=pts[np.argmax(diff)]

    return rect

def four_point_transform(image,pts):
    rect=oreder_points(pts)
    (tl,tr,br,bl)=rect

    widthA=np.sqrt(((tl[0]-tr[0])**2)+((tl[1]-tr[1])**2))
    widthB=np.sqrt(((bl[0]-br[0])**2)+((bl[1]-br[1])**2))
    maxwidth=max(int(widthB),int(widthA))


    heightA=np.sqrt(((bl[0]-tl[0])**2)+((bl[1]-tl[1])**2))
    heightB=np.sqrt(((br[0]-tr[0])**2)+((br[1]-tr[1])**2))
    maxheight=max(int(heightB),int(heightA))

    dst=np.array([
        [0,0],
        [maxwidth-1,0],
        [maxwidth-1,maxheight-1],
        [0,maxheight-1]
    ],dtype="float32")

    #计算变换矩阵
    M=cv2.getPerspectiveTransform(rect,dst)
    warped=cv2.warpPerspective(image,M,(maxwidth,maxheight))

    return warped

4,找到涂卡区域

阈值处理,查找轮廓,筛选轮廓

复制代码
#阈值处理
thresh=cv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]
# cv_show(thresh,"thresh")

thresh_contours=thresh.copy()
cnts=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
# cv2.drawContours(warped,cnts,-1,(0,0,0),2)
# cv_show(warped,"thresh_contours")

questionCnts=[]
for c in cnts:
    (x,y,w,h)=cv2.boundingRect(c)
    ar=w/float(h)

    if w>=20 and h>=20 and ar>=0.9 and ar<=1.1:
        questionCnts.append(c)

cv2.drawContours(warped,questionCnts,-1,(0,0,0),2)
cv_show(warped,"questionContours")

5,判断对错

对轮廓进行上下排序,确保每题的选项,对每题的轮廓进行左右排序,确保选项的位置正确,将全黑掩码与每个选项轮廓进行填充以此获得每个选项的掩码,将此掩码与选项轮廓进行与操作,然后计算白像素数量,白像素最多的是考生的作答选项,最后对比正确答案

注意:需要将投射变换后的图像转为彩色图才能显示出绘制的颜色

复制代码
color_warped=cv2.cvtColor(warped,cv2.COLOR_GRAY2BGR)
questionCnts=sort_contours(questionCnts,method="top-to-bottom")[0]
correct=0
for (q,i) in enumerate(np.arange(0,len(questionCnts),5)):
    cnts=sort_contours(questionCnts[i:i+5])[0]
    bubbled=None

    for (j,c) in enumerate(cnts):
        mask=np.zeros(thresh.shape,dtype="uint8")
        cv2.drawContours(mask,[c],-1,255,-1)#-1表示填充
        cv_show(mask,"mask")
        #此时的mask是每一个选项的白圈
        mask=cv2.bitwise_and(thresh,thresh,mask=mask)
        cv_show(mask,"mask")
        total=cv2.countNonZero(mask)

        if bubbled is None or total>bubbled[0]:
            bubbled=(total,j)


    color=(0,0,255)
    k=ANSWER_KEY[q]

    if k==bubbled[1]:
        color=[0,255,0]
        correct+=1

    cv2.drawContours(color_warped,[cnts[k]],-1,color,3)

相关推荐
吠品2 分钟前
Nginx负载均衡配置与线上故障排查的一些经验
java·开发语言·数据库
边境悍匪7 分钟前
蜗牛学苑 Java 智能体学习 Day44|Vue 前端项目搭建 思维导图复盘
java·开发语言·spring boot·学习·阿里云
Python图像识别9 分钟前
10-【2027毕设】YOLO11PCB缺陷检测识别系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·深度学习·yolo·毕业设计·毕设
szephyr12 分钟前
用 Python 写自动化脚本:定时任务、文件批处理、自动出报表
python·自动化·pandas·脚本·定时任务
wuyk55516 分钟前
Python实战项目01:简易记事本系统
开发语言·python
凤城老人22 分钟前
从零手写开源私有云盘|Flask+Vue3+Electron全栈网盘,单容器一键部署
python·docker·electron·vue
Zentceh34 分钟前
海洋牧场夜间监测:AI全彩夜视+水下成像集成方案
图像处理·人工智能·科技·计算机视觉·车载系统·无人机·智能家居
lpfasd12334 分钟前
配置文件格式对比 与 AI Coding 的选择逻辑
python·flask·numpy
2601_9628857237 分钟前
如何用 Python 把 A 股行情批量导出到 Excel/CSV?(多股票多 Sheet)
python
君顾11 小时前
本地城市社区家政物业联动系统源码:架构设计与派单联动实战
java·开发语言·健身房