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

答题卡自动评卷程序

image

原理简述

首先运用轮廓检测 透视变换 得到一张答题卡
再运用轮廓检测 过滤 得到选项轮廓
运用选项轮廓进行掩码操作 根据面积 来计算 是否为正确选项
计算分数 画出正确选项 显示分数

代码部分

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()
posted @ 2022-04-17 21:40  cc学习之路  阅读(279)  评论(0)    收藏  举报