OpenCV---xx3(答题卡选择题自动判卷)
答题卡自动评卷程序

原理简述
首先运用轮廓检测 透视变换 得到一张答题卡
再运用轮廓检测 过滤 得到选项轮廓
运用选项轮廓进行掩码操作 根据面积 来计算 是否为正确选项
计算分数 画出正确选项 显示分数
代码部分
import sys
import cv2 as cv
import numpy as np
# 该函数用于透视变换
def train_img(img, point):
points = []
sort_x = sorted(point, key=lambda x: x[0][0])
left_points = sort_x[:2]
right_points = sort_x[2:]
left_points = sorted(left_points, key=lambda x: x[0][1])
right_points = sorted(right_points, key=lambda x: x[0][1])
# 左上
points.append(left_points[0][0])
# 左下
points.append(left_points[1][0])
# 右上
points.append(right_points[0][0])
# 右下
points.append(right_points[1][0])
points = np.array(points, dtype='float32')
w1 = np.sqrt(
(left_points[0][0][0] - right_points[0][0][0]) ** 2 + (left_points[0][0][1] - right_points[0][0][1]) ** 2)
w2 = np.sqrt(
(left_points[1][0][0] - right_points[1][0][0]) ** 2 + (left_points[1][0][1] - right_points[1][0][1]) ** 2)
width = max(w1, w2)
h1 = np.sqrt(
(left_points[0][0][0] - left_points[1][0][0]) ** 2 + (left_points[0][0][1] - left_points[1][0][1]) ** 2)
h2 = np.sqrt(
(right_points[0][0][0] - right_points[1][0][0]) ** 2 + (right_points[0][0][1] - right_points[1][0][1]) ** 2)
height = max(h1, h2)
dst = np.array([[0, 0], [0, height - 1], [width - 1, 0], [width - 1, height - 1]], dtype='float32')
M = cv.getPerspectiveTransform(points, dst)
warp = cv.warpPerspective(img, M, (int(width), int(height)))
warp = cv.resize(warp, (450, 600))
warp_gray = cv.cvtColor(warp, cv.COLOR_BGR2GRAY)
return warp_gray, warp
# 该函数用于对y和x方向进行排序
def my_sort(conts):
# 对y进行排序
conts = sorted(conts, key=lambda x: np.mean(x[:, 0, 1]))
for i in range(0, len(conts), 5):
conts[i:i + 5] = sorted(conts[i:i + 5], key=lambda x: np.mean(x[:, 0, 0]))
return conts
if __name__ == '__main__':
# 拿到卷面轮廓 并进行透视变换
img = cv.imread('imgs/test_03.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gauss = cv.GaussianBlur(gray, (5, 5), 10, None, 10)
canny = cv.Canny(gauss, 70, 200)
print(img.shape)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
close = cv.morphologyEx(canny, cv.MORPH_CLOSE, kernel)
con, _ = cv.findContours(close, 0, 2)
print(con[0].shape)
warp_gray = None
warp = None
if len(con) > 0:
con = sorted(con, key=cv.contourArea, reverse=True)
# 拿到轮廓并且进行透视变换
res_con = con[0]
# approx = cv.boundingRect(res_con)
# print(approx)
# res_con = [[[approx[0], approx[1]]], [[approx[0] + approx[2], approx[1]]],
# [[approx[0], approx[1] + approx[3]]], [[approx[0] + approx[2], approx[1] + approx[3]]]]
# res_con = np.array(res_con)
# print(res_con.shape)
# cv.drawContours(img, res_con, -1, (255, 0, 0), 8)
# cv.imshow('img',img)
lens = cv.arcLength(res_con, True)
approx = cv.approxPolyDP(res_con, lens * 0.02, True)
# cv.drawContours(img, approx, -1, (255, 0, 0), 8)
# cv.imshow('img', img)
print(len(approx))
cv.waitKey(0)
warp_gray, warp = train_img(img, approx)
if warp_gray is None:
print('当前无符合的答题卡')
sys.exit()
else:
# cv.imshow('warp', warp_gray)
_, thresh = cv.threshold(warp_gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
# cv.imshow('thresh', thresh)
cnts, _ = cv.findContours(thresh, 0, 2)
# cv.drawContours(warp, cnts, -1, (255, 0, 0), 8)
# cv.imshow('ww', warp)
# 再次得到答题卡选项轮廓
res_conts = []
for c in cnts:
(x, y, w, h) = cv.boundingRect(c)
ar = w / float(h)
if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:
res_conts.append(c)
# for i in res_conts:
# cv.drawContours(warp,i,-1,(0,255,0),8)
# cv.imshow('xx',warp)
# 对选项进行排序
res_conts = my_sort(res_conts)
res_res = []
res_temp = []
res_count = 0
# 利用答案生成掩码 用于获取 写入的答案
for i in res_conts:
mask = np.zeros(warp_gray.shape, dtype='uint8')
# 该函数也可以用来生成掩码 -1表示填充
cv.drawContours(mask, [i], -1, 255, -1)
# cv.imshow('mask', mask)
mask = cv.bitwise_and(thresh, thresh, None, mask)
# cv.imshow('mask2', mask)
# 计算掩码操作后的白色面积
total = cv.countNonZero(mask)
# mask = cv.bitwise_and(thresh,thresh,mask)
if res_count % 5 == 0 and res_count != 0:
res_res.append(res_temp)
res_temp = []
res_count = 0
res_temp.append((total, res_count))
res_count += 1
# print(res_res)
cv.waitKey(0)
res_res.append(res_temp)
# 用来判断用户的答案
res_res_count = [i.index(max(i, key=lambda x: x[0])) for i in res_res]
print(res_res_count)、
# 正确的答案列表
right_res = [2, 3, 0, 1, 1]
error = 0
error_list = []
# 比对并计算分数
for i in range(len(right_res)):
if right_res[i] != res_res_count[i]:
error += 1
error_list.append(i)
else:
error_list.append(None)
score = 100 - 100 * error / len(error_list)
print(score)
# 画出正确的答案
right_conts = []
right_index = [2,8,10,16,21]
for j in right_index:
right_conts.append(res_conts[j])
# conss = []
# for i in range(len(right_res)):
# conss.append(res_conts[i][right_res[i]])
#
# # print(res_ccc)
# print(conss)
cv.drawContours(warp, right_conts, -1, (0, 255, 0), 8)
str_score = 'score:{}'.format(score)
cv.putText(warp,str_score,(30,30),cv.FONT_HERSHEY_SIMPLEX,1,(0,0,255))
cv.imshow('ww', warp)
cv.waitKey(0)
cv.destroyAllWindows()

浙公网安备 33010602011771号