总目录
图像处理总目录←点击这里
二十、答题卡识别试卷
20.1、预处理
灰度图
输出灰度图+高斯滤波去噪
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
边缘检测
edged = cv2.Canny(blurred, 75, 200)
20.2、轮廓检测
找到原始图像中边框的四个矩形框的点
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)
20.3、透视变换
得到需要的图像范围
def four_point_transform(image, pts):
# 获取输入坐标点
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 计算输入的w和h值
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
# 变换后对应坐标位置
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 计算变换矩阵
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
# 返回变换后结果
return warped
20.4、阈值处理
处理已经选择的题目
thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
20.5、轮廓检测(小)
每一个圆圈的轮廓检测
(涂答题卡可能涂到外面)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(thresh_Contours, cnts, -1, (0, 0, 255), 3)
20.6、筛选,排序,选项
筛选出符合条件的圆圈(通过宽高,宽高比例筛选)
将符合条件的进行排序(默认乱序)
- 先从上到下找到所有行排序
- 在从左到右对每一行排序
- 根据像素点对比找到答案
# 遍历筛选
for c in cnts:
# 计算比例和大小
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 根据实际情况指定标准
if w >= 20 and h >= 20 and 0.9 <= ar <= 1.1:
questionCnts.append(c)
# 排序
def sort_contours(cnts, method="left-to-right"):
# ...
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
return cnts, boundingBoxes
# 对所有行排序
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]
# 循环,并找答案
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
# 对每一行进行排序
cnts = sort_contours(questionCnts[i:i + 5])[0]
for (j, c) in enumerate(cnts):
mask = np.zeros(thresh.shape, dtype="uint8")
if bubbled is None or total > bubbled[0]:
# j为答案,total为像素点对比(选出最合适的)
bubbled = (total, j)
# ...
第一行跑完结果
由 12345与操作得出最后结果
20.7、结果
答案:BEADB
第4题选错
答案:BEADB
第1、2、4、5题选错
答案:BEADB
第1、4 题选错
答案:BEADB
第1、2、3、5题选错
答案:BEADB
正确
原图
# 正确答案 1 4 0 3 1 ------> B E A D B
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}