#################################################################
【纸上得来终觉浅,绝知此事要躬行】
B站视频
新课件:https://pan.baidu.com/s/1frWHqCVGR2VTn5QBtW4lPA 提取码:xh02
老课件:https://pan.baidu.com/s/1Wi31FxSPBqWiuJX9quX-jA 提取码:bbfg
################################################################
答题卡识别效果
【大致思路】:先进行仿射变换去除背景(只留试卷部分),二值化,圆形轮廓检测,遍历每一行选项,统计非零像素,记录填充选项(即非零像素最多的轮廓区域),与正确答案进行比对,正确则correct数+1,得到总成绩
1-4 基础操作+透视变换
1-4是基础操作, 3是做近似变换, 取最大的那个轮廓,最有可能是图像最大外围的轮廓
3的近似变换 和 4的透视变换原理 可以参考我的 OCR文档扫描实战博客
# 1.预处理
image = cv2.imread(args["image"])
contours_img = image.copy()
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',edged)
# 2.轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
cv_show(‘contours_img’,contours_img)
docCnt = None
# 3.确保检测到了
if len(cnts) > 0:
# 根据轮廓大小进行排序
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
<span class="token comment"># 遍历每一个轮廓</span>
<span class="token keyword">for</span> c <span class="token keyword">in</span> cnts<span class="token punctuation">:</span>
<span class="token comment"># 近似</span>
peri <span class="token operator">=</span> cv2<span class="token punctuation">.</span>arcLength<span class="token punctuation">(</span>c<span class="token punctuation">,</span> <span class="token boolean">True</span><span class="token punctuation">)</span>
approx <span class="token operator">=</span> cv2<span class="token punctuation">.</span>approxPolyDP<span class="token punctuation">(</span>c<span class="token punctuation">,</span> <span class="token number">0.02</span> <span class="token operator">*</span> peri<span class="token punctuation">,</span> <span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token comment"># 准备做透视变换</span>
<span class="token keyword">if</span> <span class="token builtin">len</span><span class="token punctuation">(</span>approx<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">4</span><span class="token punctuation">:</span>
docCnt <span class="token operator">=</span> approx
<span class="token keyword">break</span>
# 4.执行透视变换
warped = four_point_transform(gray, docCnt.reshape(4, 2))
cv_show(‘warped’,warped)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
5-6 阈值处理+轮廓检测
5.Otsu’s 阈值处理
THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
在我的信用卡数字识别案例中出现也有应用(第三、五部分)
6.然后怎么区分涂和没涂的圆?
这里不用霍夫变换,因为有些涂完后 会突出边界,如下
# 5.Otsu's 阈值处理
thresh = cv2.threshold(warped, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)
thresh_Contours = thresh.copy()
# 6.找到每一个圆圈轮廓
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(thresh_Contours,cnts,-1,(0,0,255),3)
cv_show(‘thresh_Contours’,thresh_Contours)
questionCnts = []
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
7-8 筛选答题圈
7.遍历所有圆圈轮廓(包括干扰项) 筛选出答题区域的圆,其轮廓存于questionCnts
无论是圆形还是矩形的答题卡,都是规则的形状,比例相同.
所以这里要人工设定圆圈外接矩形的长宽比例
参考信用卡数字识别 (第四部分)
8.按照从上到下(从左到右)进行排序
参考信用卡数字识别 (第二部分)
# 7.遍历所有圆圈轮廓(包括干扰项) 筛选出答题区域的圆 for c in cnts: # 计算比例和大小 (x, y, w, h) = cv2.boundingRect(c) ar = w / float(h)
<span class="token comment"># 根据实际情况指定标准 -- 过滤操作</span> <span class="token keyword">if</span> w <span class="token operator">>=</span> <span class="token number">20</span> <span class="token operator">and</span> h <span class="token operator">>=</span> <span class="token number">20</span> <span class="token operator">and</span> ar <span class="token operator">>=</span> <span class="token number">0.9</span> <span class="token operator">and</span> ar <span class="token operator"><=</span> <span class="token number">1.1</span><span class="token punctuation">:</span> questionCnts<span class="token punctuation">.</span>append<span class="token punctuation">(</span>c<span class="token punctuation">)</span>
# 8.按照从上到下进行排序
questionCnts = sort_contours(questionCnts,
method=“top-to-bottom”)[0]
correct = 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
9.每行的5个选项 分别比对正确答案
答题圈的轮廓questionCnts长度应为25, 间隔5, 可以遍历5次, 则
q 取0 1 2 3 4,共5行;
i 表示从第几个轮廓开始:0,5,10,15,20. 即每行的第一个轮廓
# 9.每行的5个选项 分别比对正确答案 for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)): # 9.1排序 cnts = sort_contours(questionCnts[i:i + 5])[0] bubbled = None
<span class="token comment"># 9.2 遍历每一个结果</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>j<span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>cnts<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment"># 9.2.1 使用mask来判断结果</span> mask <span class="token operator">=</span> np<span class="token punctuation">.</span>zeros<span class="token punctuation">(</span>thresh<span class="token punctuation">.</span>shape<span class="token punctuation">,</span> dtype<span class="token operator">=</span><span class="token string">"uint8"</span><span class="token punctuation">)</span> cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>mask<span class="token punctuation">,</span> <span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">#-1表示填充</span> cv_show<span class="token punctuation">(</span><span class="token string">'mask'</span><span class="token punctuation">,</span>mask<span class="token punctuation">)</span> <span class="token comment"># 9.2.2 通过计算非零点数量来算是否选择这个答案</span> mask <span class="token operator">=</span> cv2<span class="token punctuation">.</span>bitwise_and<span class="token punctuation">(</span>thresh<span class="token punctuation">,</span> thresh<span class="token punctuation">,</span> mask<span class="token operator">=</span>mask<span class="token punctuation">)</span> total <span class="token operator">=</span> cv2<span class="token punctuation">.</span>countNonZero<span class="token punctuation">(</span>mask<span class="token punctuation">)</span> <span class="token comment"># 9.2.3 通过阈值判断</span> <span class="token keyword">if</span> bubbled <span class="token keyword">is</span> <span class="token boolean">None</span> <span class="token operator">or</span> total <span class="token operator">></span> bubbled<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">:</span> bubbled <span class="token operator">=</span> <span class="token punctuation">(</span>total<span class="token punctuation">,</span> j<span class="token punctuation">)</span> <span class="token comment"># 9.3 获取正确答案</span> color <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">)</span> k <span class="token operator">=</span> ANSWER_KEY<span class="token punctuation">[</span>q<span class="token punctuation">]</span> <span class="token comment"># 9.4 对比答案 并 判断正确</span> <span class="token keyword">if</span> k <span class="token operator">==</span> bubbled<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span> color <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> correct <span class="token operator">+=</span> <span class="token number">1</span> <span class="token comment"># 9.5 绘图</span> cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>warped<span class="token punctuation">,</span> <span class="token punctuation">[</span>cnts<span class="token punctuation">[</span>k<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> color<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
9.1 确保每一行的顺序为A B C D E
9.2 同样这么一行里,这5个框有什么不同.
j 取每个选项0 1 2 3 4
9.2.1 使用mask来判断结果
初始化一个 跟透视变换后的图 一样大小的mask(全黑)
然后在mask上, 画出当前遍历的这个(圆圈)轮廓c, 画成白色
- cv2.drawContours (传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度)
参考OpenCV基本操作
补充一点:线条厚度 为负值或CV_FILLED 表示填充轮廓内部
一行遍历5个选项,5行一共25个选项,这展示前两行的遍历结果,后三行同理…
9.2.2 与操作
一张图片 跟 一张相同大小的黑白图片 进行与操作,则只保留图片的白色区域
-
cv2.bitwise_and(src1, src2, dst=None, mask=None)
对图像(灰度图像或彩色图像均可)每个像素值进行二进制“与”操作,
1&1=1,1&0=0,0&1=0,0&0=0函数返回值: 调用时若无mask参数 则返回src1 & src2,若存在mask参数,则返回src1 & src2 & mask
src1:输入原图1
src2:输入原图2, src1与src2可以相同也可以不相同,可以是灰度图像也可以是彩色图像
dst:输出矩阵,和输入矩阵一样的尺寸和类型 若存在参数时:src1 & src2 或者 src1 & src2 & mask
mask:可以是单通道8bit灰度图像,也可以是矩阵,一般为二值化后的图像,指定要更改的输出数组的元素 -
cv2.countNonZero统计非零像素点个数
9.2.3 依次判断5个选项的哪个非零值最大(即哪个被填充上了)
total > bubbled[0] 比它大的才保留到bubbled
bubbled 保留最大的选项( 即填充上的选项 ) j
9.3 k = ANSWER_KEY[q] 是第几题(行)的正确答案
9.4 若k = bubbled[1],判断正确,correct+=1
.
10.打印正确率
# 10.打印正确率
score = (correct / 5.0) * 100
print("[INFO] score: {:.2f}%".format(score))
cv2.putText(warped, "{:.2f}%".format(score), (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
cv2.imshow("Original", image)
cv2.imshow("Exam", warped)
cv2.waitKey(0)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
.
.
* 测试其他答题卡效果
结束语
虽然代码有点长,但大部分内容都是前面几个项目有用到的知识。可见遇到不同场景的举一反三能力多重要啦
扩展
有兴趣的同学们 可以尝试一下矩形答题卡的识别
完整代码
一个py文件,3个函数
# 导入工具包 import numpy as np import argparse import cv2 def order_points(pts): # 一共4个坐标点 rect = np.zeros((4, 2), dtype = "float32")
<span class="token comment"># 按顺序找到对应坐标0123分别是 左上,右上,右下,左下</span> <span class="token comment"># 计算左上,右下</span> s <span class="token operator">=</span> pts<span class="token punctuation">.</span><span class="token builtin">sum</span><span class="token punctuation">(</span>axis <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span> rect<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> pts<span class="token punctuation">[</span>np<span class="token punctuation">.</span>argmin<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">]</span> rect<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> pts<span class="token punctuation">[</span>np<span class="token punctuation">.</span>argmax<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token comment"># 计算右上和左下</span> diff <span class="token operator">=</span> np<span class="token punctuation">.</span>diff<span class="token punctuation">(</span>pts<span class="token punctuation">,</span> axis <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span> rect<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> pts<span class="token punctuation">[</span>np<span class="token punctuation">.</span>argmin<span class="token punctuation">(</span>diff<span class="token punctuation">)</span><span class="token punctuation">]</span> rect<span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span> <span class="token operator">=</span> pts<span class="token punctuation">[</span>np<span class="token punctuation">.</span>argmax<span class="token punctuation">(</span>diff<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token keyword">return</span> rect
def four_point_transform(image, pts):
# 获取输入坐标点
rect = order_points(pts)
(tl, tr, br, bl) = rect
<span class="token comment"># 计算输入的w和h值</span>
widthA <span class="token operator">=</span> np<span class="token punctuation">.</span>sqrt<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>br<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">-</span> bl<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>br<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">-</span> bl<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
widthB <span class="token operator">=</span> np<span class="token punctuation">.</span>sqrt<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>tr<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">-</span> tl<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>tr<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">-</span> tl<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
maxWidth <span class="token operator">=</span> <span class="token builtin">max</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">(</span>widthA<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">(</span>widthB<span class="token punctuation">)</span><span class="token punctuation">)</span>
heightA <span class="token operator">=</span> np<span class="token punctuation">.</span>sqrt<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>tr<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">-</span> br<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>tr<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">-</span> br<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
heightB <span class="token operator">=</span> np<span class="token punctuation">.</span>sqrt<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>tl<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">-</span> bl<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>tl<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">-</span> bl<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">**</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
maxHeight <span class="token operator">=</span> <span class="token builtin">max</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">(</span>heightA<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">(</span>heightB<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment"># 变换后对应坐标位置</span>
dst <span class="token operator">=</span> np<span class="token punctuation">.</span>array<span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">[</span>maxWidth <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">[</span>maxWidth <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> maxHeight <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> maxHeight <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> dtype <span class="token operator">=</span> <span class="token string">"float32"</span><span class="token punctuation">)</span>
<span class="token comment"># 计算变换矩阵</span>
M <span class="token operator">=</span> cv2<span class="token punctuation">.</span>getPerspectiveTransform<span class="token punctuation">(</span>rect<span class="token punctuation">,</span> dst<span class="token punctuation">)</span>
warped <span class="token operator">=</span> cv2<span class="token punctuation">.</span>warpPerspective<span class="token punctuation">(</span>image<span class="token punctuation">,</span> M<span class="token punctuation">,</span> <span class="token punctuation">(</span>maxWidth<span class="token punctuation">,</span> maxHeight<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment"># 返回变换后结果</span>
<span class="token keyword">return</span> 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
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
‘’‘下面为主函数’’’
if name == “main”:
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", “–image”, required=True,
help=“path to the input image”)
args = vars(ap.parse_args())
<span class="token comment"># 正确答案</span>
ANSWER_KEY <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span><span class="token number">0</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">}</span>
<span class="token comment"># 1.预处理</span>
image <span class="token operator">=</span> cv2<span class="token punctuation">.</span>imread<span class="token punctuation">(</span>args<span class="token punctuation">[</span><span class="token string">"image"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
contours_img <span class="token operator">=</span> image<span class="token punctuation">.</span>copy<span class="token punctuation">(</span><span class="token punctuation">)</span>
gray <span class="token operator">=</span> cv2<span class="token punctuation">.</span>cvtColor<span class="token punctuation">(</span>image<span class="token punctuation">,</span> cv2<span class="token punctuation">.</span>COLOR_BGR2GRAY<span class="token punctuation">)</span>
blurred <span class="token operator">=</span> cv2<span class="token punctuation">.</span>GaussianBlur<span class="token punctuation">(</span>gray<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
cv_show<span class="token punctuation">(</span><span class="token string">'blurred'</span><span class="token punctuation">,</span>blurred<span class="token punctuation">)</span>
edged <span class="token operator">=</span> cv2<span class="token punctuation">.</span>Canny<span class="token punctuation">(</span>blurred<span class="token punctuation">,</span> <span class="token number">75</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span>
cv_show<span class="token punctuation">(</span><span class="token string">'edged'</span><span class="token punctuation">,</span>edged<span class="token punctuation">)</span>
<span class="token comment"># 2.轮廓检测</span>
cnts <span class="token operator">=</span> cv2<span class="token punctuation">.</span>findContours<span class="token punctuation">(</span>edged<span class="token punctuation">.</span>copy<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cv2<span class="token punctuation">.</span>RETR_EXTERNAL<span class="token punctuation">,</span>
cv2<span class="token punctuation">.</span>CHAIN_APPROX_SIMPLE<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>
cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>contours_img<span class="token punctuation">,</span>cnts<span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">255</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span>
cv_show<span class="token punctuation">(</span><span class="token string">'contours_img'</span><span class="token punctuation">,</span>contours_img<span class="token punctuation">)</span>
docCnt <span class="token operator">=</span> <span class="token boolean">None</span>
<span class="token comment"># 3.确保检测到了</span>
<span class="token keyword">if</span> <span class="token builtin">len</span><span class="token punctuation">(</span>cnts<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">:</span>
<span class="token comment"># 根据轮廓大小进行排序</span>
cnts <span class="token operator">=</span> <span class="token builtin">sorted</span><span class="token punctuation">(</span>cnts<span class="token punctuation">,</span> key<span class="token operator">=</span>cv2<span class="token punctuation">.</span>contourArea<span class="token punctuation">,</span> reverse<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token comment"># 遍历每一个轮廓</span>
<span class="token keyword">for</span> c <span class="token keyword">in</span> cnts<span class="token punctuation">:</span>
<span class="token comment"># 近似</span>
peri <span class="token operator">=</span> cv2<span class="token punctuation">.</span>arcLength<span class="token punctuation">(</span>c<span class="token punctuation">,</span> <span class="token boolean">True</span><span class="token punctuation">)</span>
approx <span class="token operator">=</span> cv2<span class="token punctuation">.</span>approxPolyDP<span class="token punctuation">(</span>c<span class="token punctuation">,</span> <span class="token number">0.02</span> <span class="token operator">*</span> peri<span class="token punctuation">,</span> <span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token comment"># 准备做透视变换</span>
<span class="token keyword">if</span> <span class="token builtin">len</span><span class="token punctuation">(</span>approx<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">4</span><span class="token punctuation">:</span>
docCnt <span class="token operator">=</span> approx
<span class="token keyword">break</span>
<span class="token comment"># 4.执行透视变换</span>
warped <span class="token operator">=</span> four_point_transform<span class="token punctuation">(</span>gray<span class="token punctuation">,</span> docCnt<span class="token punctuation">.</span>reshape<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
cv_show<span class="token punctuation">(</span><span class="token string">'warped'</span><span class="token punctuation">,</span>warped<span class="token punctuation">)</span>
<span class="token comment"># 5.Otsu's 阈值处理</span>
thresh <span class="token operator">=</span> cv2<span class="token punctuation">.</span>threshold<span class="token punctuation">(</span>warped<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span>
cv2<span class="token punctuation">.</span>THRESH_BINARY_INV <span class="token operator">|</span> cv2<span class="token punctuation">.</span>THRESH_OTSU<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>
cv_show<span class="token punctuation">(</span><span class="token string">'thresh'</span><span class="token punctuation">,</span>thresh<span class="token punctuation">)</span>
thresh_Contours <span class="token operator">=</span> thresh<span class="token punctuation">.</span>copy<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment"># 6.找到所有轮廓</span>
cnts <span class="token operator">=</span> cv2<span class="token punctuation">.</span>findContours<span class="token punctuation">(</span>thresh<span class="token punctuation">.</span>copy<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cv2<span class="token punctuation">.</span>RETR_EXTERNAL<span class="token punctuation">,</span>
cv2<span class="token punctuation">.</span>CHAIN_APPROX_SIMPLE<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>
cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>thresh_Contours<span class="token punctuation">,</span>cnts<span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">255</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span>
cv_show<span class="token punctuation">(</span><span class="token string">'thresh_Contours'</span><span class="token punctuation">,</span>thresh_Contours<span class="token punctuation">)</span>
questionCnts <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token comment"># 7.遍历所有圆圈轮廓(包括干扰项) 筛选出答题区域的圆</span>
<span class="token keyword">for</span> c <span class="token keyword">in</span> cnts<span class="token punctuation">:</span>
<span class="token comment"># 计算比例和大小</span>
<span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> w<span class="token punctuation">,</span> h<span class="token punctuation">)</span> <span class="token operator">=</span> cv2<span class="token punctuation">.</span>boundingRect<span class="token punctuation">(</span>c<span class="token punctuation">)</span>
ar <span class="token operator">=</span> w <span class="token operator">/</span> <span class="token builtin">float</span><span class="token punctuation">(</span>h<span class="token punctuation">)</span>
<span class="token comment"># 根据实际情况指定标准 -- 过滤操作</span>
<span class="token keyword">if</span> w <span class="token operator">>=</span> <span class="token number">20</span> <span class="token operator">and</span> h <span class="token operator">>=</span> <span class="token number">20</span> <span class="token operator">and</span> ar <span class="token operator">>=</span> <span class="token number">0.9</span> <span class="token operator">and</span> ar <span class="token operator"><=</span> <span class="token number">1.1</span><span class="token punctuation">:</span>
questionCnts<span class="token punctuation">.</span>append<span class="token punctuation">(</span>c<span class="token punctuation">)</span>
<span class="token comment"># 8.按照从上到下进行排序</span>
questionCnts <span class="token operator">=</span> sort_contours<span class="token punctuation">(</span>questionCnts<span class="token punctuation">,</span>
method<span class="token operator">=</span><span class="token string">"top-to-bottom"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
correct <span class="token operator">=</span> <span class="token number">0</span>
<span class="token comment"># 9.每行的5个选项 分别比对正确答案</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>q<span class="token punctuation">,</span> i<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>np<span class="token punctuation">.</span>arange<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token builtin">len</span><span class="token punctuation">(</span>questionCnts<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token comment"># 9.1排序</span>
cnts <span class="token operator">=</span> sort_contours<span class="token punctuation">(</span>questionCnts<span class="token punctuation">[</span>i<span class="token punctuation">:</span>i <span class="token operator">+</span> <span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
bubbled <span class="token operator">=</span> <span class="token boolean">None</span>
<span class="token comment"># 9.2 遍历每一个结果</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>j<span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>cnts<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token comment"># 9.2.1 使用mask来判断结果</span>
mask <span class="token operator">=</span> np<span class="token punctuation">.</span>zeros<span class="token punctuation">(</span>thresh<span class="token punctuation">.</span>shape<span class="token punctuation">,</span> dtype<span class="token operator">=</span><span class="token string">"uint8"</span><span class="token punctuation">)</span>
cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>mask<span class="token punctuation">,</span> <span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">#-1表示填充</span>
<span class="token comment"># cv_show('mask',mask)</span>
<span class="token comment"># 9.2.2 通过计算非零点数量来算是否选择这个答案</span>
mask <span class="token operator">=</span> cv2<span class="token punctuation">.</span>bitwise_and<span class="token punctuation">(</span>thresh<span class="token punctuation">,</span> thresh<span class="token punctuation">,</span> mask<span class="token operator">=</span>mask<span class="token punctuation">)</span>
total <span class="token operator">=</span> cv2<span class="token punctuation">.</span>countNonZero<span class="token punctuation">(</span>mask<span class="token punctuation">)</span>
<span class="token comment"># 9.2.3 通过阈值判断</span>
<span class="token keyword">if</span> bubbled <span class="token keyword">is</span> <span class="token boolean">None</span> <span class="token operator">or</span> total <span class="token operator">></span> bubbled<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
bubbled <span class="token operator">=</span> <span class="token punctuation">(</span>total<span class="token punctuation">,</span> j<span class="token punctuation">)</span>
<span class="token comment"># 9.3 获取正确答案</span>
color <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">)</span>
k <span class="token operator">=</span> ANSWER_KEY<span class="token punctuation">[</span>q<span class="token punctuation">]</span>
<span class="token comment"># 9.4 对比答案 并 判断正确</span>
<span class="token keyword">if</span> k <span class="token operator">==</span> bubbled<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
color <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
correct <span class="token operator">+=</span> <span class="token number">1</span>
<span class="token comment"># 9.5 绘图</span>
cv2<span class="token punctuation">.</span>drawContours<span class="token punctuation">(</span>warped<span class="token punctuation">,</span> <span class="token punctuation">[</span>cnts<span class="token punctuation">[</span>k<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> color<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>
<span class="token comment"># 10.打印正确率</span>
score <span class="token operator">=</span> <span class="token punctuation">(</span>correct <span class="token operator">/</span> <span class="token number">5.0</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">100</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"[INFO] score: {:.2f}%"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>score<span class="token punctuation">)</span><span class="token punctuation">)</span>
cv2<span class="token punctuation">.</span>putText<span class="token punctuation">(</span>warped<span class="token punctuation">,</span> <span class="token string">"{:.2f}%"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>score<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
cv2<span class="token punctuation">.</span>FONT_HERSHEY_SIMPLEX<span class="token punctuation">,</span> <span class="token number">0.9</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span>
cv2<span class="token punctuation">.</span>imshow<span class="token punctuation">(</span><span class="token string">"Original"</span><span class="token punctuation">,</span> image<span class="token punctuation">)</span>
cv2<span class="token punctuation">.</span>imshow<span class="token punctuation">(</span><span class="token string">"Exam"</span><span class="token punctuation">,</span> warped<span class="token punctuation">)</span>
cv2<span class="token punctuation">.</span>waitKey<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178