答题卡的识别

目录

1.问题描述

2.解决思路

3.代码实现

4.相关资料


该博客整编于:https://www.pyimagesearch.com/

1.问题描述

现在,我们需要识别一张简易的答题卡,如图1-1所示。

                                                                                       图1-1 简易答题卡

最终的识别结果如图1-2所示。其中,选对的答案用绿色表示,错选的用红色表示。

那么在答题卡识别的问题中有哪些待续解决的问题呢?我的理解是这样的:

1.答题卡区域的分割问题:想要进行答题卡识别总得先把答题卡区域和环境区域分割出来吧。

2.答题卡纸张背景和答案的分离问题:我们需要的只有答案的区域,因此需要解决答案和答题卡背景的分割问题。

3.轮廓的筛选问题:筛选出我们想要的轮廓,排除那些不需要的轮廓信息。

4.轮廓的排序和定位问题:如何对轮廓进行行和列的定位,这很重要。

5.检测答题者所选择的选项:检测漏选、多选的情况。

2.解决思路

2.1 答题卡区域的分割问题

在这里,由于环境色的一致性,我们使用了canny边缘检测算子,检测出答题卡的边界信息。

分割代码如下:

	Mat answerSheet = imread("answerSheet.png");
	//灰度转化
	Mat gray;
	cvtColor(answerSheet,gray,CV_BGR2GRAY);
	//进行高斯滤波
	Mat blurred;
	GaussianBlur(gray,blurred,Size(3,3),0);
	//进行canny边缘检测
	Mat canny;
	Canny(blurred,canny,75,200);

计算的图像如图2-1:

                                                                         图 2-1 canny算子计算图

2.2 分割答题卡的纸张和答题区域

首先,我们要找到答题卡轮廓区域的边界,利用DP算法计算出轮廓的角点,最后基于透视变化对图像进行矫正,即转化为鸟瞰图。实现的代码如下:

//排序算子
bool sortBy_x( Point &a, Point &b)
{
	return a.x < b.x;
}

bool sortBy_y( Point &a,  Point &b)
{
	return a.y < b.y;
}
	//寻找矩形边界
	vector<vector<Point>> contours;
	findContours(canny, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	vector<Point>result_contour;
	if (contours.size() == 1)
	{
		result_contour = contours[0];
	}
	else
	{
		int max = -1;
		int index = -1;
		for (int i = 0; i < contours.size(); i++)
		{
			int tem = arcLength(contours[i], true);
			if (tem > max)  max = tem;
			index = i;
		}
		result_contour = contours[index];
	}
	//使用DP算法拟合答题卡的几何轮廓,保存点集pts并顺时针排序
	vector<Point> pts;
	approxPolyDP(result_contour,pts,(int)arcLength(result_contour,true)*0.02,true);
	if (pts.size() != 4) return 1;
	sort(pts.begin(), pts.end(), sortBy_x);
	sort(pts.begin(), pts.end(), sortBy_y);
	//进行透视变换
	//1.确定变化尺寸的宽度
	int width;
	int width1 = (pts[0].x - pts[1].x)*(pts[0].x - pts[1].x) + (pts[0].y - pts[1].y)*(pts[0].y - pts[1].y);
	int width2= (pts[2].x - pts[3].x)*(pts[2].x - pts[3].x) + (pts[2].y - pts[3].y)*(pts[2].y - pts[3].y);
	if (width1 > width2) width = sqrt(width1);
	else width = sqrt(width2);
	//2.确定变化尺寸的高度
	int height;
	int height1 = (pts[0].x - pts[3].x)*(pts[0].x - pts[3].x) + (pts[0].y - pts[3].y)*(pts[0].y - pts[3].y);
	int height2 = (pts[2].x - pts[1].x)*(pts[2].x - pts[1].x) + (pts[2].y - pts[1].y)*(pts[2].y - pts[1].y);
	if (height1 > height2) height= sqrt(height1);
	else height = sqrt(height2);
	//3.计算透视变换矩阵
	vector<Point2f> Pts(4);
	Pts[0]=(Point2f(0,0));
	Pts[1]=(Point2f(width-1, 0));
	Pts[2]=(Point2f(width-1, height-1));
	Pts[3]=(Point2f(0, height-1));
	//4.计算透视变换矩阵
	//4.1类型转化
	Mat src = Mat(pts);
	vector<Point2f> Pt;
	src.convertTo(src,CV_32F)
  • 4
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值