基于opencv的答题卡应用

答题卡处理对象:
在这里插入图片描述
识别如上的答题卡。在一个矩形方框内,每行五个选项ABCDE,每行代表一题,共5题。
下面展示 实现代码

# -*- coding:utf-8 -*-
from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
import imutils
import cv2 as cv


ANSWER_KEY_SCORE = {0: 1, 1: 3, 2: 0, 3: 5, 4: 2}

ANSWER_KEY = {0: "A", 1: "B", 2: "C", 3: "D", 4: "E"}

# 加载一个图片到opencv中
img = cv.imread(r'D:\123456\Inked0806_LI.jpg')

#cv.imshow("orgin",img)

#转化成灰度图片
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

#cv.imshow("gray",gray)

gaussian_bulr = cv.GaussianBlur(gray, (5, 5), 0)  # 高斯模糊

#cv.imshow("gaussian",gaussian_bulr)

edged=cv.Canny(gaussian_bulr,75,178) # 边缘检测,灰度值小于2参这个值的会被丢弃,大于3参这个值会被当成边缘,在中间的部分,自动检测

#cv.imshow("edged",edged)

# 寻找轮廓
cts, hierarchy = cv.findContours( edged.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

# 给轮廓加标记,便于我们在原图里面观察,注意必须是原图才能画出红色,灰度图是没有颜色的
cv.drawContours(img, cts, -1, (0,0,255), 3)

# 按面积大小对所有的轮廓排序
list=sorted(cts,key=cv.contourArea,reverse=True)

print("寻找轮廓的个数:",len(cts))
#cv.imshow("draw_contours",img)

# 正确题的个数
correct_count=0

for c in list:
    # 周长,第1个参数是轮廓,第二个参数代表是否是闭环的图形
    peri=0.01*cv.arcLength(c,True)
    # 获取多边形的所有定点,如果是四个定点,就代表是矩形
    approx=cv.approxPolyDP(c,peri,True)
    # 打印定点个数
    print("顶点个数:",len(approx))

    if len(approx)==4: #矩形
        # 透视变换提取原图内容部分
        ox_sheet = four_point_transform(img, approx.reshape(4, 2))
        # 透视变换提取灰度图内容部分
        tx_sheet = four_point_transform(gray, approx.reshape(4, 2))

        #cv.imshow("ox", ox_sheet)
        #cv.imshow("tx", tx_sheet)
        # 使用ostu二值化算法对灰度图做一个二值化处理
        ret,thresh2 = cv.threshold(tx_sheet, 0, 255,cv.THRESH_BINARY_INV | cv.THRESH_OTSU)

        cv.namedWindow("ostu", 0)
        cv.resizeWindow("ostu", 500, 500)
        cv.imshow("ostu", thresh2)

        # 继续寻找轮廓
        r_cnt, r_hierarchy = cv.findContours(thresh2.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

        print("找到轮廓个数:",len(r_cnt))

        # 使用红色标记所有的轮廓
        # cv.drawContours(ox_sheet,r_cnt,-1,(0,0,255),2)

        # 把所有找到的轮廓,给标记出来

        questionCnts = []
        for cxx in r_cnt:
            # 通过矩形,标记每一个指定的轮廓
            x, y, w, h = cv.boundingRect(cxx)
            ar = w / float(h)


            if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:
                # 使用红色标记,满足指定条件的图形
                # cv.rectangle(ox_sheet, (x, y), (x + w, y + h), (0, 0, 255), 2)
                # 把每个选项,保存下来
                questionCnts.append(cxx)

        #cv.imshow("ox_1", ox_sheet)
        # 按坐标从上到下排序
        questionCnts = contours.sort_contours(questionCnts, method="top-to-bottom")[0]

        # 使用np函数,按5个元素,生成一个集合
        for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):

            # 获取按从左到右的排序后的5个元素
            cnts = contours.sort_contours(questionCnts[i:i + 5])[0]

            bubble_rows=[]

            # 遍历每一个选项
            for (j, c) in enumerate(cnts):

                # 生成一个大小与透视图一样的全黑背景图布
                mask = np.zeros(tx_sheet.shape, dtype="uint8")
                # 将指定的轮廓+白色的填充写到画板上,255代表亮度值,亮度=255的时候,颜色是白色,等于0的时候是黑色
                cv.drawContours(mask, [c], -1, 255, -1)
                # 做两个图片做位运算,把每个选项独自显示到画布上,为了统计非0像素值使用,这部分像素最大的其实就是答案
                mask = cv.bitwise_and(thresh2, thresh2, mask=mask)
                # cv.imshow("c" + str(i), mask)
                # 获取每个答案的像素值
                total = cv.countNonZero(mask)
                # 存到一个数组里面,tuple里面的参数分别是,像素大小和答案的序号值
                # print(total,j)
                bubble_rows.append((total,j))



            bubble_rows=sorted(bubble_rows,key=lambda x: x[0],reverse=True)
            # 选择的答案序号
            choice_num=bubble_rows[0][1]
            print("答案:{} 数据: {}".format(ANSWER_KEY.get(choice_num),bubble_rows))


            fill_color=None

            # 如果做对就加1
            if ANSWER_KEY_SCORE.get(q) == choice_num:
                fill_color = (0, 255, 0)   #正确 绿色
                correct_count = correct_count+1
            else:
                fill_color = (0, 0, 255)   #错误 红色

            cv.drawContours(ox_sheet, cnts[choice_num], -1, fill_color, 2)

        cv.namedWindow("answer_flagged", 0)
        cv.resizeWindow("answer_flagged", 500, 500)
        cv.imshow("answer_flagged", ox_sheet)

        text1 = "total: " + str(len(ANSWER_KEY)) + ""

        text2 = "right: " + str(correct_count)

        text3 = "score: " + str(correct_count*1.0/len(ANSWER_KEY)*100)+""

        font = cv.FONT_HERSHEY_SIMPLEX
        cv.putText(ox_sheet, text1 + "  " + text2+"  "+text3, (10, 30), font, 0.5, (0, 0, 255), 2)

        cv.namedWindow("score", 0)
        cv.resizeWindow("score", 500, 500)
        cv.imshow("score", ox_sheet)

        break




cv.waitKey(0)



效果图如下:
在这里插入图片描述

发布了5 篇原创文章 · 获赞 0 · 访问量 444
展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览