Python+Opencv答题卡识别用例详解

使用Python3和Opencv识别一张标准的答题卡。大致的过程如下:

1.读取图片

2.利用霍夫圆检测,检测出四个角的黑圆位置,从确定四个角的位置

3.利用透视变换和四个角的位置,矫正图片(直接用的网上的图片,没有拍照,所以这一步没有实现)

4.裁剪四个边框,获取边框上小黑格的位置

5.根据小黑格的位置确定每个涂卡区域的位置

6.将答题卡腐蚀和膨胀,遍历所有的格子的区域,计算每个区域内像素值为0的个数,若数量达到某个值,那么就确认这个格子是被黑笔涂过,并记录该位置的题目选项。

具体的实现

一、读取图片,用是imread函数。

二、利用霍夫圆检测位置,这里注意的是HoughCircles的param参数,调的不准,所以检测出来还有其他的干扰圆,最后可以通过设置半径阈值,将四个角的圆筛选出来。最后这个函数返回四个圆的位置和半径。

#检测图中的圆,并返回每个圆的位置和半径
def detect_circles_demo(image):
    temp = image.copy()
    rows,cols,channels = temp.shape
    print(rows,cols)
    location_vcol = []
    dst = cv.pyrMeanShiftFiltering(image,10,100)
    cimage = cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
    #不同的图片,Parmal的值是不一样的
    circles = cv.HoughCircles(cimage,cv.HOUGH_GRADIENT,1,20,param1= 27,param2=30,minRadius=0,maxRadius=0)
    circles = np.uint16(np.around(circles))
    for i in circles[0,:]:
        if i[2] < 20 and i[2] > 10:
            cv.circle(image,(i[0],i[1]),i[2],(0,0,255),2)
            location_vcol.append((i[0],i[1],i[2]))
    # 画出图中圆的位置
    cv.imshow("image",image)
    return location_vcol

三、先将图片整体二直化,再在二直化的图片将四个边框裁剪下来,直接根据四个边角的位置向外或者向里增加或者较少一个半径,确定边框矩形的对角位置。

#获取四个边角的位置
location_RT = location_vcol[0]
location_RB = location_vcol[1]
location_LT = location_vcol[2]
location_LB = location_vcol[3]
 
# 先将原图二值化,注意阈值的取范围,再将二值化图片转换成BGR,方便后面标出小方格的位置
gray = cv.cvtColor(temp, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY_INV)
 Matimage = cv.cvtColor(binary, cv.COLOR_GRAY2BGR)
 cv.imshow("Matimage",Matimage)
 
#将图片的四条边裁剪出来
 roilLeft = Matimage[location_LT[1] - location_LT[2]:location_LB[1] + location_LB[2],location_LT[0] -location_LT[2]:location_LB[0] + location_LB[2]]
 roilRight = Matimage[location_RT[1] - location_RT[2]:location_RB[1] + location_RB[2],location_RT[0] - location_RT[2]:location_RB[0]+location_RB[2]]
 roilTop = Matimage[location_LT[0] - location_LT[2]:location_RT[1]+location_RT[2],location_LT[0] - location_LT[2]:location_RT[0] +location_RT[2]]
 roilBottom = Matimage[location_RB[1]-location_RB[2]:location_RB[1] + location_RB[2],location_LB[0] -location_LB[2]:location_RB[0]+location_RB[2]]
 
# 展示四条边的图片
cv.imshow("left",roilLeft)
cv.imshow("right", roilRight)
cv.imshow("top", roilTop)
cv.imshow("bottom", roilBottom)

四、根据裁剪的边框确定边框上小白格的位置。每个方位的计算方式是一样的。每个函数返回的是小格子在源图中的位置。

先来说底部的。这个过程是把截图看成一个坐标系,以行长为横坐标,列高为中坐标,计算统计每个横坐标下,有多少个点是白色的,并将该数值存放于列表vcol中。根据这种算法,能够得出这个列表中应该是rows个元素,而且每个元素的值不超过列高,不低于0。

遍历vcol列表,如果某一个位置为0,后一个位置不为0,那么这个位置就是小白格的起始位置。并记录起始位置在原图中的位置,方便后期遍历整个涂卡区域。

其他的三个方向是同样的道理,

#计算底部小黑格的位置
def sure_bottom(roilBottom,src,a):
    rows, cols, channels = roilBottom.shape
    src_rows,src_cols,src_channels = src.shape
    itemp = 0
    vcol = []
    bottom_vcol = []
    for i in range(0, cols):
        for j in range(0, rows):
            if roilBottom[j, i][0] == 255 and roilBottom[j, i][1] == 255 and roilBottom[j, i][2] == 255:
                itemp = itemp + 1
        vcol.append(itemp)
        itemp = 0
    for i in range(vcol.__len__()):
        if 0 == vcol[i] and vcol[i + 1] > 0:
            cv.line(roilBottom, (i, 0), (i, rows), (0, 0, 255), 2)
            cv.line(src, (i + a, src_rows), (i + a, src_rows - rows), (0, 255, 0), 2)
            bottom_vcol.append(i + a)
    cv.imshow("src-1", roilBottom)
    return bottom_vcol
 
#计算右部小黑格的位置
def sure_Right(roilRight,src,a):
    rows, cols, channels = roilRight.shape
    src_rows, src_cols, src_channels = src.shape
    itemp = 0
    vcol = []
    right_vcol = []
    for i in range(0, rows):
        for j in range(0, cols):
            if roilRight[i, j][0] == 255 and roilRight[i, j][1] == 255 and roilRight[i, j][2] == 255:
                itemp = itemp + 1
        vcol.append(itemp)
        itemp = 0
    print(vcol)
    for i in range(vcol.__len__()):
        if vcol[i] == 0 and vcol[i + 1] > 0:
            cv.line(roilRight, (0, i), (cols, i), (0, 0, 255), 2)
            cv.line(src, (src_cols - cols, i + a ), (src_cols, i + a), (0, 0, 255), 2)
            right_vcol.append(i + a)
    cv.imshow("src", src)
    return right_vcol
 
#计算左边小黑格的位置
def sure_Left(roilLeft,src,a):
    rows, cols, channels = roilLeft.shape
    print(rows, cols)
    itemp = 0
    vcol = []
    left_vcol = []
    for i in range(0, rows):
        for j in range(0, cols):
            if roilLeft[i, j][0] == 255 and roilLeft[i, j][1] == 255 and roilLeft[i, j][2] == 255:
                itemp = itemp + 1
        vcol.append(itemp)
        itemp = 0
    for i in range(vcol.__len__()):
        if vcol[i] == 0 and vcol[i + 1] > 0:
            cv.line(roilLeft, (0, i), (cols, i), (0, 0, 255), 2)
            cv.line(src, (16, i + a), (16 + cols, i + a), (0, 0, 255), 2)
            left_vcol.append(i + a)
    cv.imshow("src", src)
    return left_vcol
 
# 确定顶部小黑格的位置
def sure_Top(roilTop,src,a):
    rows, cols, channels = roilTop.shape
    src_rows, src_cols, src_channels = src.shape
    itemp = 0
    vcol = []
    top_vcol = []
    for i in range(0, cols):
        for j in range(0, rows):
            if roilTop[j, i][0] == 255 and roilTop[j, i][1] == 255 and roilTop[j, i][2] == 255:
                itemp = itemp + 1
        vcol.append(itemp)
        itemp = 0
    for i in range(vcol.__len__()):
        if vcol[i] == 0 and vcol[i + 1] > 0:
            cv.line(roilTop, (i, 0), (i, rows), (0, 0, 255), 2)
            cv.line(src, (i + a, 12), (a + i,48), (0, 0, 255), 2)
            top_vcol.append(i + a)
    cv.imshow("src", src)
    return top_vcol
 
#获取原图位置
    bottom_vcol = sure_bottom(roilBottom,temp,location_LB[0] -location_LB[2])
    right_vcol = sure_Right(roilRight,temp,location_RT[1] - location_RT[2])
    left_vcol = sure_Left(roilLeft,temp,location_LT[1] - location_LT[2])
    top_vcol = sure_Top(roilTop,temp,location_LT[0] - location_LT[2])

五、在原图中全出每个小格子。在此之前要将图片做个预处理,腐蚀和膨胀。这个程序先圈出来的是答题区域的小格子,所以i和j 的范围分别是底部格子数和右边格子数。

先将每个格子的区域找出来,然后遍历这个格子的全部像素,判断是否为0 ,如果为0,那么就isum+1,最后遍历结束,看看isum的个数有没有达到总像素个数的10%,如果达到,那么就可以判定这个区域是被涂过的,记录这个区域对应的题号和选项,以备后面计分。

dst = sure_if_fill(temp)
    for i in range(0,20):
        for j in range(10,26):
            rect = dst[bottom_vcol[i]:bottom_vcol[i] + 9,right_vcol[i]:right_vcol[j] + 3]
            rect_up = (bottom_vcol[i],right_vcol[j])
            rect_down = (bottom_vcol[i] + 9,right_vcol[j] + 3)
 
            # 判断ROI区域是否被填充
            isum = 0
            for ii in range(rect.shape[0]):
                for jj in range(rect.shape[1]):
                    if dst[ii,jj] == 0:
                        isum = isum + 1
            # if isum > 0.1 * rect.shape[0] * rect.shape[1]:
            cv.rectangle(temp,rect_down,rect_up,(0,255,0),2)
            isum = 0
    cv.imshow("dst",temp)
 
 
#检查空格是否被填充
def sure_if_fill(image):
    temp = image.copy()
    gray = cv.cvtColor(temp,cv.COLOR_BGR2GRAY)
    ret,binary = cv.threshold(gray,100,255,cv.THRESH_BINARY)
    kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
    dst = cv.erode(binary,kernel= kernel)
    cv.imshow("dst",dst)
    return dst

这是腐蚀后的图片

这是圈出所有答题区域的图片

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • python OpenCV实现答题卡识别判卷

    本文实例为大家分享了python OpenCV实现答题卡识别判卷的具体代码,供大家参考,具体内容如下 完整代码: #导入工具包 import numpy as np import argparse import imutils import cv2 # 设置参数 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default="./images/test_03.png"

  • python利用opencv如何实现答题卡自动判卷

    目录 1.设定答题卡模板 2.读取答题卡图像并对图像进行灰度化处理 3.高斯模糊图像去噪点 4.使用大津法二值分割图像 5.使用开运算去噪点 6.使用canny边缘检测算法 7.筛选答题区域轮廓,透视变换矫正目标区域 使用摄像头实时判卷部分 总结 1.设定答题卡模板 该图像为答题卡的答题区域,黑色边框是为了能够在各种环境中轻易的检测,左部分和上部分的黑色矩形,是为能够定位到答题选项的坐标而设置,同时题目数量为20×3共60道选择题,在进行批改试卷之前,需要手动输入该次考试的正确答案作为模板来对识

  • 使用 OpenCV-Python 识别答题卡判卷功能

    任务 识别用相机拍下来的答题卡,并判断最终得分(假设正确答案是B, E, A, D, B) 主要步骤 轮廓识别--答题卡边缘识别 透视变换--提取答题卡主体 轮廓识别--识别出所有圆形选项,剔除无关轮廓 检测每一行选择的是哪一项,并将结果储存起来,记录正确的个数 计算最终得分并在图中标注 分步实现 轮廓识别--答题卡边缘识别 输入图像 import cv2 as cv import numpy as np # 正确答案 right_key = {0: 1, 1: 4, 2: 0, 3: 3, 4

  • Python+Opencv答题卡识别用例详解

    使用Python3和Opencv识别一张标准的答题卡.大致的过程如下: 1.读取图片 2.利用霍夫圆检测,检测出四个角的黑圆位置,从确定四个角的位置 3.利用透视变换和四个角的位置,矫正图片(直接用的网上的图片,没有拍照,所以这一步没有实现) 4.裁剪四个边框,获取边框上小黑格的位置 5.根据小黑格的位置确定每个涂卡区域的位置 6.将答题卡腐蚀和膨胀,遍历所有的格子的区域,计算每个区域内像素值为0的个数,若数量达到某个值,那么就确认这个格子是被黑笔涂过,并记录该位置的题目选项. 具体的实现 一.

  • 基于Python的身份证验证识别和数据处理详解

    根据GB11643-1999公民身份证号码是特征组合码,由十七位数字本体码和一位数字校验码组成,排列顺序从左至右依次为: 六位数字地址码八位数字出生日期码三位数字顺序码一位数字校验码(数字10用罗马X表示) 校验系统: 校验码采用ISO7064:1983,MOD11-2校验码系统(图为校验规则样例) 用身份证号的前17位的每一位号码字符值分别乘上对应的加权因子值,得到的结果求和后对11进行取余,最后的结果放到表2检验码字符值..换算关系表中得出最后的一位身份证号码 代码: # coding=ut

  • Python用sndhdr模块识别音频格式详解

    本文主要介绍了Python编程中,用sndhdr模块识别音频格式的相关内容,具体如下. sndhdr模块 功能描述:sndhdr模块提供检测音频类型的接口. 唯一一个API sndhdr模块提供了sndhdr.what(filename)和sndhdr.whathdr(filename)两个函数.但实际上它们的功能是一样的.(不知道多写一个的意义何在,what函数在内部调用了whathdr函数并把数据完完整整地返回) 在之前的版本,whathdr函数返回元组类型的数据,在Python3.5版本之

  • 浅谈Python Opencv中gamma变换的使用详解

    伽马变换就是用来图像增强,其提升了暗部细节,简单来说就是通过非线性变换,让图像从暴光强度的线性响应变得更接近人眼感受的响应,即将漂白(相机曝光)或过暗(曝光不足)的图片,进行矫正. 伽马变换的基本形式如下: 大于1时,对图像的灰度分布直方图具有拉伸作用(使灰度向高灰度值延展),而小于1时,对图像的灰度分布直方图具有收缩作用(是使灰度向低灰度值方向靠拢). #分道计算每个通道的直方图 img0 = cv2.imread('12.jpg') hist_b = cv2.calcHist([img0],

  • python自动化报告的输出用例详解

    1.设计简单的用例 2.设计用例 以TestBaiduLinks.py命名 # coding:utf-8 from selenium import webdriver import unittest class BaiduLinks(unittest.TestCase): def setUp(self): base_url = 'https://www.baidu.com' self.driver = webdriver.Chrome() self.driver.implicitly_wait(

  • Python OpenCV对图像进行模糊处理详解流程

    其实我们平时在深度学习中所说的卷积操作,在 opencv 中也可以进行,或者说是类似操作.那么它是什么操作呢?它就是图像的模糊(滤波)处理. 均值滤波 使用 opencv 中的cv2.blur(src, ksize)函数.其参数说明是: src: 原图像 ksize: 模糊核大小 原理:它只取内核区域下所有像素的平均值并替换中心元素.3x3 标准化的盒式过滤器如下所示: 特征:核中区域贡献率相同. 作用:对于椒盐噪声的滤除效果比较好. # -*-coding:utf-8-*- ""&q

  • Python OpenCV特征检测之特征匹配方式详解

    目录 前言  一.暴力匹配器 二.FLANN匹配器 前言  获得图像的关键点后,可通过计算得到关键点的描述符.关键点描述符可用于图像的特征匹配.通常,在计算图A是否包含图B的特征区域时,将图A称做训练图像,将图B称为查询图像.图A的关键点描述符称为训练描述符,图B的关键点描述符称为查询描述符. 一.暴力匹配器 暴力匹配器使用描述符进行特征比较.在比较时,暴力匹配器首先在查询描述符中取一个关键点的描述符,将其与训练描述符中的所有关键点描述符进行比较,每次比较后会给出一个距离值,距离最小的值对应最佳

  • Python OpenCV图像处理之图像滤波特效详解

    目录 1分类 2邻域滤波 2.1线性滤波 2.2非线性滤波 3频域滤波 3.1低通滤波 3.2高通滤波 1 分类 图像滤波按图像域可分为两种类型: 邻域滤波(Spatial Domain Filter),其本质是数字窗口上的数学运算.一般用于图像平滑.图像锐化.特征提取(如纹理测量.边缘检测)等,邻域滤波使用邻域算子——利用给定像素周围像素值以决定此像素最终输出的一种算子 频域滤波(Frequency Domain Filter),其本质是对像素频率的修改.一般用于降噪.重采样.图像压缩等. 按

  • Python+OpenCV实现阈值分割的方法详解

    目录 一.全局阈值 1.效果图 2.源码 二.滑动改变阈值(滑动条) 1.效果图 2.源码 三.自适应阈值分割 1.效果图 2.源码 3.GaussianBlur()函数去噪 四.参数解释 一.全局阈值 原图: 整幅图采用一个阈值,与图片的每一个像素灰度进行比较,重新赋值: 1.效果图 2.源码 import cv2 import matplotlib.pyplot as plt #设定阈值 thresh=130 #载入原图,并转化为灰度图像 img_original=cv2.imread(r'

  • Python OpenCV实现图片预处理的方法详解

    目录 一.图片预处理 1.1 边界填充(padding) 1.2 融合图片(mixup) 1.3 图像阈值 二.滤波器 2.1 均值滤波器 2.2 方框滤波器 2.3 高斯滤波器 2.4 中值滤波 2.5 所有滤波器按照上述顺序输出 一.图片预处理 1.1 边界填充(padding) 方法 : cv2.copyMakeBorder BORDER_REPLICATE:复制法,也就是复制最边缘像素. BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abc

随机推荐