识别答题卡小项目

```python

"""

识别答题卡

"""

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

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]))

```

你可能感兴趣的:(识别答题卡小项目)