当前位置:   article > 正文

答题卡的识别_答题卡识别算法

答题卡识别算法

目录

1.问题描述

2.解决思路

3.代码实现

4.相关资料


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

1.问题描述

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

                                                                                       图1-1 简易答题卡

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

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

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

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

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

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

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

2.解决思路

2.1 答题卡区域的分割问题

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

分割代码如下:

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

计算的图像如图2-1:

                                                                         图 2-1 canny算子计算图

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

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

  1. //排序算子
  2. bool sortBy_x( Point &a, Point &b)
  3. {
  4. return a.x < b.x;
  5. }
  6. bool sortBy_y( Point &a, Point &b)
  7. {
  8. return a.y < b.y;
  9. }
  1. //寻找矩形边界
  2. vector<vector<Point>> contours;
  3. findContours(canny, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
  4. vector<Point>result_contour;
  5. if (contours.size() == 1)
  6. {
  7. result_contour = contours[0];
  8. }
  9. else
  10. {
  11. int max = -1;
  12. int index = -1;
  13. for (int i = 0; i < contours.size(); i++)
  14. {
  15. int tem = arcLength(contours[i], true);
  16. if (tem > max) max = tem;
  17. index = i;
  18. }
  19. result_contour = contours[index];
  20. }
  21. //使用DP算法拟合答题卡的几何轮廓,保存点集pts并顺时针排序
  22. vector<Point> pts;
  23. approxPolyDP(result_contour,pts,(int)arcLength(result_contour,true)*0.02,true);
  24. if (pts.size() != 4) return 1;
  25. sort(pts.begin(), pts.end(), sortBy_x);
  26. sort(pts.begin(), pts.end(), sortBy_y);
  27. //进行透视变换
  28. //1.确定变化尺寸的宽度
  29. int width;
  30. 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);
  31. 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);
  32. if (width1 > width2) width = sqrt(width1);
  33. else width = sqrt(width2);
  34. //2.确定变化尺寸的高度
  35. int height;
  36. 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);
  37. 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);
  38. if (height1 > height2) height= sqrt(height1);
  39. else height = sqrt(height2);
  40. //3.计算透视变换矩阵
  41. vector<Point2f> Pts(4);
  42. Pts[0]=(Point2f(0,0));
  43. Pts[1]=(Point2f(width-1, 0));
  44. Pts[2]=(Point2f(width-1, height-1));
  45. Pts[3]=(Point2f(0, height-1));
  46. //4.计算透视变换矩阵
  47. //4.1类型转化
  48. Mat src = Mat(pts);
  49. vector<Point2f> Pt;
  50. src.convertTo(src,CV_32F)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/768268
推荐阅读
相关标签
  

闽ICP备14008679号