本文实例为大家分享了opencv实现答题卡识别的具体代码,供大家参考,具体内容如下
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
""" 识别答题卡 """ import cv2 import numpy as np def showImg(img_name, img): cv2.imshow(img_name, img) cv2.waitKey() cv2.destroyAllWindows() def get_max_rect(sorted_cnts): for cnt in sorted_cnts: # 轮廓近似 possible_cnts = [] epsilon = 0.1 * cv2.arcLength(cnt, True ) approx = cv2.approxPolyDP(cnt, epsilon, True ) if len (approx) = = 4 : possible_cnts.append(cnt) possible_cnts = sorted (possible_cnts, key = lambda x: cv2.arcLength(x, True )) return possible_cnts def get_max_bounding_rect(possible_cnts): # for cnt in possible_cnts: # x, y, w, h = cv2.boundingRect(cnt) sorted_cnts = sorted (possible_cnts, key = lambda cnt: cv2.boundingRect(cnt)[ 2 ] * cv2.boundingRect(cnt)[ 3 ], reverse = True ) print (sorted_cnts[ 0 ]) def show_countour(img, cnt): img_copy = img.copy() cv2.drawContours(img_copy, cnt, - 1 , ( 0 , 255 , 0 ), 3 ) showImg( "img_copy" , img_copy) # 读取答题卡图片,并显示 answer_sheet_img = cv2.imread( "t1.jpg" ) print (answer_sheet_img.shape) showImg( "answer_sheet_img" , answer_sheet_img) # 高斯滤波,去除噪音 blur = cv2.GaussianBlur(answer_sheet_img,( 5 , 5 ), 0 ) showImg( "blur" , blur) # 图像转灰度值 sheet_gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY) showImg( "sheet_gray" , sheet_gray) # 二值化 retval, sheet_threshold = cv2.threshold(sheet_gray, 177 , 255 , cv2.THRESH_BINARY) # print(type(sheet_threshold), sheet_threshold) showImg( "sheet_threshold" , sheet_threshold) # 边界检测 edges = cv2.Canny(sheet_threshold, 100 , 200 ) showImg( "edges" , edges) # print(type(edges)) # 寻找轮廓 copy_edges = edges.copy() img_copy = answer_sheet_img.copy() img, cnts, hierarchy = cv2.findContours(copy_edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img_copy, cnts, - 1 , ( 0 , 0 , 255 ), 1 ) showImg( "img_copy" , img_copy) # 对所有轮廓加一个外接矩形,找最大的外接矩形 max_area_index = None area = 0 for index, cnt in enumerate (cnts): x, y, w, h = cv2.boundingRect(cnt) if w * h > area: max_area_index = index show_countour(answer_sheet_img, cnts[max_area_index]) # 仿射,拿到答题卡主要部位 x, y, w, h = cv2.boundingRect(cnts[max_area_index]) # 最大的边界 cv2.rectangle(answer_sheet_img, (x, y),(x + w, y + h), ( 0 , 0 , 255 ), 2 ) showImg( "answer_sheet_img" , answer_sheet_img) pts1 = np.float32([[x,y], [x + w, y], [x + w, y + h]]) pts2 = np.float32([[ 0 , 0 ], [w, 0 ], [w, h]]) M = cv2.getAffineTransform(pts1, pts2) sheet_threshold_copy = sheet_threshold.copy() dst = cv2.warpAffine(sheet_threshold_copy, M, (w, h)) showImg( "dst" , dst) print (answer_sheet_img.shape) part_sheet_img = answer_sheet_img[y:y + h, x:x + w] showImg( "part_sheet_img" , part_sheet_img) # 对答案区域灰度,二值,找轮廓 part_answer_gray = cv2.cvtColor(part_sheet_img, cv2.COLOR_BGR2GRAY) # 灰度 ret, threshold_answer = cv2.threshold(part_answer_gray, 175 , 255 , cv2.THRESH_BINARY) showImg( "threshold_answer" , threshold_answer) img, answer_cnts, x = cv2.findContours(threshold_answer, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) part_sheet_img_copy = part_sheet_img.copy() cv2.drawContours(part_sheet_img_copy, answer_cnts, - 1 , ( 0 , 0 , 255 ), 1 ) showImg( "dst_copy" , part_sheet_img_copy) # 对所有轮廓找外接矩形,想过滤掉不合适的矩形 print ( "画矩形" ) answer_filter_cnts = [] answer_circles = [] img_ = part_sheet_img.copy() for cnt in answer_cnts: x, y, w, h = cv2.boundingRect(cnt) if 30 <w< 40 and 30 <h< 40 : print (x, y, w, h) circle_x = int (x + w / 2 ) circle_y = int (y + h / 2 ) r = int ((w + h) / 4 ) answer_circles.append((circle_x, circle_y, r)) answer_filter_cnts.append(cnt) answer_filter_cnts = np.array(answer_filter_cnts) cv2.drawContours(img_, answer_filter_cnts, - 1 , ( 0 , 0 , 255 ), 1 ) # cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2) showImg( "img_" , img_) print ( "geshu" , len (answer_circles)) # 从answer_circles中取25个 mask_dict = { 1 :[], 2 :[], 3 :[], 4 :[], 5 :[]} # 一共不一定是25个圆,将圆按照题目行分类, sorted_y_answer_circles = sorted (answer_circles, key = lambda circle: circle[ 1 ]) print ( "sorted_y_answer_circles" , sorted_y_answer_circles) set_num = 1 for index, circle in enumerate (sorted_y_answer_circles): if index = = 0 : mask_dict[ 1 ].append(circle) else : if circle[ 1 ] - sorted_y_answer_circles[index - 1 ][ 1 ] > 30 : set_num + = 1 mask_dict[set_num].append(circle) else : mask_dict[set_num].append(circle) print ( "mask_dict" , mask_dict) for k, mask_circle_list in mask_dict.items(): # 对每一个题目,保留五个答案,多余的舍去 if len (mask_circle_list) = = 5 : sorted_x_mask_circle_list = sorted (mask_circle_list, key = lambda x:x[ 0 ]) mask_dict[k] = sorted_x_mask_circle_list else : sorted_x_mask_circle_list = sorted (mask_circle_list, key = lambda x: x[ 0 ]) sorted_x_mask_circle_list_5 = [] for i, c in enumerate (sorted_x_mask_circle_list): if i = = 0 : sorted_x_mask_circle_list_5.append(c) else : if abs (c[ 0 ] - sorted_x_mask_circle_list[i - 1 ][ 0 ]) < 10 : pass else : sorted_x_mask_circle_list_5.append(c) mask_dict[k] = sorted_x_mask_circle_list_5 print ( "mask_dict" , mask_dict) # mask_dict 分好组的按照顺序的圈圈 # 做掩码 mask_img = np.zeros_like(part_sheet_img, dtype = 'uint8' ) # 全黑图 showImg( "threshold_answer" , threshold_answer) threshold_answer = np.array(threshold_answer) # mask_dict = sorted(mask_dict, key=lambda x: mask_dict.keys()) all_scores = [] # 所有答案处的评分 for exercise_num, circle_mask_list in mask_dict.items(): # 对于每一题 score_list = [] # 每一题的每个选项的评分,涂黑的为选择的,值越接近0, 评分较低 for circle_mask in circle_mask_list: mask_img_copy = cv2.cvtColor(mask_img, cv2.COLOR_BGR2GRAY) # 做一个当前圆的掩码: cv2.circle(mask_img_copy, (circle_mask[ 0 ], circle_mask[ 1 ]), circle_mask[ 2 ], ( 255 , 255 , 255 ), - 1 ) print (threshold_answer.shape, mask_img_copy.shape) mask_img_ = cv2.bitwise_and(threshold_answer, threshold_answer, mask = mask_img_copy) score = mask_img_. sum () score_list.append(score) # showImg("mask_img_", mask_img_) all_scores.append(score_list) all_score_np = np.array(all_scores) s = np.argmin(all_score_np, axis = 1 ) # 找评分最低处即为选择项 answer_dict = { 0 : "A" , 1 : "B" , 2 : "C" , 3 : "D" , 4 : "E" } for index, v in enumerate (s): print ( "第%s题的答案是%s" % (index + 1 , answer_dict[v])) |
效果图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/qq_36008031/article/details/104464227