识别答题卡小项目

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

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352