ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OpenCV 答题卡识别

2021-06-20 19:32:26  阅读:220  来源: 互联网

标签:img cv2 答题卡 OpenCV cnts np 识别 pts method


1、预处理、轮廓检测

import cv2
import numpy as np
# 正确答案
ANSWER_KEY = {0:1,1:4,2:0,3:3,4:1}
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
# 读图
img = cv2.imread("test_01.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
edge = cv2.Canny(blur,75,200)
# 轮廓检测
cnt = cv2.findContours(edge,cv2.RETR_EXTERNAL,
                       cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(contour_img,cnt,-1,(0,255,0),2)
cv_show("contour_img",contour_img)

在这里插入图片描述

2、轮廓排序,透视变化

def order_points(pts):
# 一共四个坐标点
    rect = np.zeros((4,2),dtype="float32")
#     按照顺序找到对应坐标0123分别是 左上,右上,右下,左下
#     计算左上,右下
    s = pts.sum(axis=1)
    rect[0]=pts[np.argmin(s)]
    rect[2]=pts[np.argmax(s)]

#     计算右上,左下
    d = np.diff(pts,axis=1)
    rect[1]=pts[np.argmin(d)]
    rect[3]=pts[np.argmax(d)]
    return rect

def four_point_transform(img,pts):
#     获取输入坐标
    rect = order_points(pts)
    (tl,tr,br,bl) = rect
#     计算输入的w和h值
    widthA = np.sqrt((br[0]-bl[0])**2+(br[1]-bl[1])**2)
    widthB = np.sqrt((tr[0]-tl[0])**2+(tr[1]-tl[1])**2)
    maxWidth = max(int(widthA),int(widthB))
   
    heightA = np.sqrt((tr[0]-br[0])**2+(tr[1]-br[1])**2)
    heightB = np.sqrt((tl[0]-bl[0])**2+(tl[1]-bl[1])**2)
    maxHeight = max(int(heightA),int(heightB))
    
#     变换后对应坐标位置
    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(img,M,(maxWidth,maxHeight))
#     反回变换后的结果
    return warped
def sort_contours(cnts,method="left-to-right"):
    reverse = False
    i = 0
    if method == 'right-to-left' or method =='bottom-to-top':
        reverse =True
    if method == 'top-to-bottom' or method =='bottom-to-top':
        i =1
    boundingBoxes =[cv2.boundingRect(c) for c in cnts]
    (cnts,boundingBoxes) = zip(*sorted(zip(cnts,boundingBoxes),
                        key=lambda b:b[1][i],reverse=reverse))
    return cnts,boundingBoxes

3、寻找圆圈轮廓

def sort_contours(cnts,method="left-to-right"):
    reverse = False
    i = 0
    if method == 'right-to-left' or method =='bottom-to-top':
        reverse =True
    if method == 'top-to-bottom' or method =='bottom-to-top':
        i =1
    boundingBoxes =[cv2.boundingRect(c) for c in cnts]
    (cnts,boundingBoxes) = zip(*sorted(zip(cnts,boundingBoxes),
                        key=lambda b:b[1][i],reverse=reverse))
    return cnts,boundingBoxes
dotCnt =None
if len(cnt)>0:
    cnt = sorted(cnt,key = cv2.contourArea,reverse=True)
    for c in cnt:
        peri = cv2.arcLength(c,True)
        approx = cv2.approxPolyDP(c,0.02*peri,True)
        if len(approx)==4:
            dotCnt=approx            
warp = four_point_transform(gray,dotCnt.reshape(4,2))
# otsu's 阈值处理
thresh = cv2.threshold(warp,0,255,cv2.THRESH_BINARY_INV|
                       cv2.THRESH_OTSU)[1]
thresh_contours = thresh.copy()
# 找到每一个圆圈轮廓
cnt = cv2.findContours(thresh_contours,cv2.RETR_EXTERNAL,
                       cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(thresh_contours,cnt,-1,(0,255,0),2)

questionCnts = []

在这里插入图片描述

4、输出每个轮廓,对比答案

# 遍历
for c in cnt:
#     计算比例和大小
    (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)
#         按照从上到下进行排序
questionCnts = sort_contours(questionCnts,method='top-to-bottom')[0]
# cv2.drawContours(warp,questionCnts,1,(0,255,255),2)  
# 每排有5个选项
for (q,i)in enumerate(np.arange(0,len(questionCnts),5)):
#     排序
    cnts = sort_contours(questionCnts[i:i+5])[0]
    bubbled = None
# 遍历每一个结果
    correct=0
    for (j,c)in enumerate(cnts):
#         使用mask来判断结果
        mask = np.zeros(thresh.shape,dtype='uint8')
        cv2.drawContours(mask,[c],-1,255,-1) 
#         通过计算非零点数量来算是否选择这个答案
        mask = cv2.bitwise_and(thresh,thresh,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(warp,cnts[k],-1,color,2)
cv2.imshow("warp",warp)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

标签:img,cv2,答题卡,OpenCV,cnts,np,识别,pts,method
来源: https://blog.csdn.net/j_lyn/article/details/118046006

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有