任务
识别用相机拍下来的答题卡,并判断最终得分(假设正确答案是B, E, A, D, B)
主要步骤
- 轮廓识别——答题卡边缘识别
- 透视变换——提取答题卡主体
- 轮廓识别——识别出所有圆形选项,剔除无关轮廓
- 检测每一行选择的是哪一项,并将结果储存起来,记录正确的个数
- 计算最终得分并在图中标注
分步实现
轮廓识别——答题卡边缘识别
输入图像
import cv2 as cv
import numpy as np
# 正确答案
right_key = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}
# 输入图像
img = cv.imread('./images/test_01.png')
img_copy = img.copy()
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cvshow('img-gray', img_gray)
图像预处理
# 图像预处理
# 高斯降噪
img_gaussian = cv.GaussianBlur(img_gray, (5, 5), 1)
cvshow('gaussianblur', img_gaussian)
# canny边缘检测
img_canny = cv.Canny(img_gaussian, 80, 150)
cvshow('canny', img_canny)
轮廓识别——答题卡边缘识别
# 轮廓识别——答题卡边缘识别
cnts, hierarchy = cv.findContours(img_canny, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(img_copy, cnts, -1, (0, 0, 255), 3)
cvshow('contours-show', img_copy)
透视变换——提取答题卡主体
对每个轮廓进行拟合,将多边形轮廓变为四边形
docCnt = None
# 确保检测到了
if len(cnts) > 0:
# 根据轮廓大小进行排序
cnts = sorted(cnts, key=cv.contourArea, reverse=True)
# 遍历每一个轮廓
for c in cnts:
# 近似
peri = cv.arcLength(c, True)
# arclength 计算一段曲线的长度或者闭合曲线的周长;
# 第一个参数输入一个二维向量,第二个参数表示计算曲线是否闭合
approx = cv.approxPolyDP(c, 0.02 * peri, True)
# 用一条顶点较少的曲线/多边形来近似曲线/多边形,以使它们之间的距离<=指定的精度;
# c是需要近似的曲线,0.02*peri是精度的最大值,True表示曲线是闭合的
# 准备做透视变换