哈喽大家好,这里是滑稽研究所。看过我们图像处理系列的朋友,应该知道识别答题卡那期文章。其中利用opencv框架,完美的实现了答题卡填涂区域的识别。在后台有小伙伴想要我完善一下判断选项对错并打分的功能,本期我们就来实现一下。
那么我们来复习一下往期的代码原理。我们需要对图片素材进行灰度化处理、透视变换、轮廓检测、腐蚀膨胀处理、区域分割、边框计算、区域计算。实际上我们是通过像素面积的过滤、填涂区域优化和获取选项坐标来完成答题卡的识别的。
素材:
那么在获取到答题卡的填涂区域之后就好办了。我们首先分隔答题卡,去除干扰项,然后把不同的区域打上标签。我们的答题卡是自上而下排序的。那么我们获取到的填涂项的x坐标即横坐标就派上了用场。选项A~E一定是占据了五个不同的区域。我们已经为不同区域打上了标签。剩下的就是交给我们的if判断语句了。这时我们已经为填涂项赋上了实际的意义。即从像素坐标转换成了具有实际意义的选项。
那y坐标就没有用了吗?非也。经过上面的处理我们只是得到了填涂区域对应的选项。但是我们还没有进行排序。大家知道无序的选项是没有意义的。而刚刚我们说了该答题卡的题号顺序是自上而下的。因为我们遍历选项时,是同时得到x、y坐标的,因此我们可以保证得到的坐标是配对的。
其中横纵坐标分别填入两个list中,然后使用zip方法合并list。这时我们再按照每个list的第二个元素也就是纵坐标进行由小到大的排序,就可以得到正确的顺序。
这时我们才真正获取到了需要的数据。即考生填涂的选项顺序,我们再新建一个list放正确的答案,与考生的答案进行对比,经计算得出考生的正确率,并给出分数。
好,思路清晰,上代码!
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
|
import cv2 import numpy as np path = './test_01.png' img = cv2.imread(path) imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) imgBlur = cv2.GaussianBlur(imgGray,( 3 , 3 ), 1 ) imgCanny = cv2.Canny(imgBlur, 100 , 120 ) cv2.imshow( "O" , imgCanny) imgContour = img.copy() cnts = cv2.findContours(imgCanny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[ 0 ] for cnt in cnts: area = cv2.contourArea(cnt) # 这个输出各个轮廓的面积 #print(area) # if area > = 500 : cv2.drawContours(imgContour, cnt, - 1 , ( 255 , 0 , 0 ), 3 ) peri = cv2.arcLength(cnt, True ) # 找出轮廓的突变值 approx = cv2.approxPolyDP(cnt, 0.02 * peri, True ) # approx找到的是一个轮廓有几个突变值,有几个角就会有几个突变值 # 返回的是一个list,输出他的长度,就可以知道到底有几个角 #print(approx) a1,a2,a3,a4 = list (approx[ 0 ][ 0 ]), list (approx[ 1 ][ 0 ]), list (approx[ 2 ][ 0 ]), list (approx[ 3 ][ 0 ]) #cv2.imshow("Canny Image",imgContour) mat1 = np.array([a1,a2,a3,a4],dtype = np.float32) #透视变换 #计算矩形宽高 width = 402 #int(((a4[0]-a1[0])+(a3[0]-a2[0]))/2) height = 518 #int(((a2[1]-a1[1])+(a3[1]-a4[1]))/2) #计算还原后的坐标 new_a1 = [ 0 , 0 ] new_a2 = [ 0 ,height] new_a3 = [width,height] new_a4 = [width, 0 ] mat2 = np.array([new_a1,new_a2,new_a3,new_a4],dtype = np.float32) #计算变换矩阵 mat3 = cv2.getPerspectiveTransform(mat1,mat2) #进行透视变换 res = cv2.warpPerspective(imgCanny,mat3,(width,height)) res1 = cv2.warpPerspective(img,mat3,(width,height)) imgxx = cv2.cvtColor(res1,cv2.COLOR_BGR2GRAY) binary = cv2.threshold(imgxx, 0 , 255 ,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU )[ 1 ] #变换完成 #cv2.imshow("Output",res1) cntss = cv2.findContours(res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[ 0 ] for cnt1 in cntss: area1 = cv2.contourArea(cnt1) # 这个输出各个轮廓的面积 #print(area) # if area1 > = 1500 and area1< = 1700 : #把圆的轮廓画成黑色 cv2.drawContours(binary, cnt1, - 1 , ( 0 , 0 , 0 ), 10 ) kernel = np.ones(( 5 , 5 ), np.uint8) imgDialation = cv2.dilate(binary, kernel, iterations = 1 ) cv2.imshow( "Out" , imgDialation) cntsss = cv2.findContours(imgDialation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[ 0 ] l1 = [] l2 = [] l3 = [ 'B' , 'E' , 'A' , 'D' , 'B' ] for cnt2 in cntsss: area2 = cv2.contourArea(cnt2) #print(area) if area2 < = 1200 and 800 < = area2: #cv2.drawContours(res1, cnt, -1, (0, 255, 0), 5) #轮廓长 peri = cv2.arcLength(cnt2, True ) # 找出轮廓的突变值 approx1 = cv2.approxPolyDP(cnt2, 0.02 * peri, True ) x, y, w, h = cv2.boundingRect(approx1) #外接矩形 #print(x+w//2,y+h//2) m = x + w / / 2 n = y + h / / 2 l1.append(m) l2.append(n) #拼接两个一维列表,使x,y坐标配对。 mix1 = list ( zip (l1,l2)) #按列表第二个元素升序,即按y值由小到大排列。 #这是我们得到的答案为正确顺序。 mix1.sort(key = lambda x: x[ 1 ]) if 400 >x> 80 and 50 <y< 350 : cv2.rectangle(res1, (x, y), (x + w, y + h), ( 0 , 0 , 255 ), 2 ) #圆心 # (图像,x.y位置,半径,颜色,轮廓粗细) cv2.circle(res1, (x + w / / 2 ,y + h / / 2 ), 1 , ( 255 , 0 , 0 ), 5 ) l4 = [] for i in mix1: if 75 < i[ 0 ] < 130 : print ( "A" ) l4.append( 'A' ) elif 130 < i[ 0 ] < 185 : print ( "B" ) l4.append( 'B' ) elif 185 < i[ 0 ] < 240 : print ( "C" ) l4.append( 'C' ) elif 240 < i[ 0 ] < 295 : print ( "D" ) l4.append( 'D' ) elif 295 < i[ 0 ] < 350 : print ( "E" ) l4.append( 'E' ) print ( '正确答案:' ,l3) print ( '考生答案' ,l4) h = 0 for i in range ( 0 , len (l3)): if l3[i] = = l4[i]: h = h + 1 print ( '得分:' , str (h / 5 * 100 ) + '分' ) cv2.imshow( "cc Image" ,res1) cv2.imshow( "dd Image" ,binary) cv2.waitKey( 0 ) |
运行结果:
以上为两个图片素材的运行结果,我们只放出其中一部分。剩余的素材大家自行实验。
可以看到,程序成功的识别了考生填涂的答题卡,并给出了考生答案、正答案和考生最后的得分。
综上功能实现,任务完成。大家学会了吗?
以上就是Python识别答题卡并给出分数的详细内容,更多关于Python识别答题卡的资料请关注服务器之家其它相关文章!
原文链接:https://blog.csdn.net/weixin_45067072/article/details/118072028