文章目录

  • 一、简单介绍
  • 二、步骤回顾
    • 2.1 图像处理
      • 2.1.1 图像的二值化
      • 2.1.2 ROI(region of interest,感兴趣区域)的分割
        • a. 选择题部分分割
        • b. 数字部分分割
    • 2.2 图像识别
      • 2.2.1 选择题识别
      • 2.2.2 数字识别
  • 三、运行结果
  • 四、总结

一、简单介绍

最近在整理学习笔记,发现以前做的一个机读卡识别程序还挺有意思的,用的是C++和OpenCV,算法比较简单,主要是回顾一下图片识别的一些基本思路,顺便做一下算法的优化。

PS:以前的C++代码风格真的惨不忍睹,而且没有注释,23333

这是这个程序需要识别的机读卡:

二、步骤回顾

2.1 图像处理

2.1.1 图像的二值化

一般情况我们需要识别的图片基本都是三通道的彩色图像。

而图像的处理一般都会需要讲这些图像进行二值化的处理,让整个图像呈现出明显的只有黑和白的视觉效果,便于进行图像的分割和识别。

现将图片先转换为灰度图,然后再进行二值化处理:

bool loadImage(const char * src_path, cv::Mat& binary_img)
{// 原图以灰度图形式加载// 以及加载异常的处理cv::Mat src_img = cv::imread(src_path);if (src_img.empty()){std::cout << "IMAGE LOAD FAILDED!"<< std::endl;return false;}cv::Mat src_gary_img;cv::cvtColor(src_img, src_gary_img, CV_BGR2GRAY);// 自适应阈值的二值化处理// 将灰度图转换为二值图cv::adaptiveThreshold(src_gary_img, binary_img,255, 0, 1, 101, 10);return true;
}

这是处理的效果:

2.1.2 ROI(region of interest,感兴趣区域)的分割

这张图片的ROI就是数字信息和选择题答案的区域,我们需要将其单独分割出来。

对于这张图片而言就是寻找最大连通域并且切割出来:

cv::Mat getMaxContourRect(const cv::Mat& src)
{// 查找该图片的所有连通域std::vector<std::vector<cv::Point>>contours;cv::findContours(src, contours,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);// 寻找最大连通域std::vector<cv::Point> max_contours;double max_area_value = 0.0;for (auto iter = contours.begin(); iter != contours.end(); iter++){double area = cv::contourArea(*iter);if (area > max_area_value){max_area_value = area;max_contours = *iter;}}// 根据最大连通域切割矩形框cv::Rect max_contour_rect = cv::boundingRect(max_contours);cv::Mat roi_box = src(max_contour_rect);return roi_box;
}

分割后的效果:

a. 选择题部分分割

将分割后的图像反色处理后,再次提取最大连通域即可定位到选择题方框。

接着再次反色让背景变成黑色,然后对得到的图像进行简单的裁剪,降低识别误差。

ps:二值图中白色为1,黑色为0。查找连通域是根据的是白色区域查找,所以需要反色操作。

 // 定位答题区域,即最大连通域区域cv::Mat roi_box = getMaxContourRect(src);// 反色处理定位选择题区域,然后再做反色便于提取选项cv::Mat choice_box = getMaxContourRect(~roi_box);choice_box = ~choice_box;// 裁剪为最佳定位样式choice_box = choice_box(cv::Rect(0, 15, choice_box.cols, choice_box.rows / 2 - 22));

分割效果:

b. 数字部分分割
// 定位选择题上方数字信息
cv::Mat roi_img = getMaxContourRect(src);
cv::Mat info_box = roi_img(cv::Rect(0, 10, roi_img.cols, roi_img.rows / 4 - 10));
std::vector<std::vector<cv::Point>> contours, num_box;
cv::findContours(info_box, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// 获取两个数字框
for (auto iter = contours.begin(); iter != contours.end(); iter++)
{double area = cv::contourArea(*iter);if (area > 100)num_box.push_back(*iter);
}
std::vector<cv::Mat> num;
for (auto iter = num_box.begin(); iter != num_box.end(); iter++)
{cv::Rect rect = cv::boundingRect(*iter);cv::Mat result = info_box(rect);num.push_back(result);
}

分割效果:

2.2 图像识别

2.2.1 选择题识别

根据选择题的分割效果,可以明显的看出涂抹了选项的选项部分连通域大小明显的突出。

所以这里可以很轻易的得到40道选择题的涂抹区域的相对位置。

将选择题部分分割为10x20的矩阵,每个位置对于一个区域信息,从而得到涂抹结果。

代码实现:

// 定位涂抹区间的连通域,根据涂抹区域大小决定
std::vector<std::vector<cv::Point>> contours;
cv::findContours(choice_box, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
std::vector<std::vector<cv::Point>> answer_contours;
for (auto iter = contours.begin(); iter != contours.end(); iter++)
{double area = cv::contourArea(*iter);// 20 ~ 60 大小的区间为涂抹区域if (area > 20 && area < 60){answer_contours.push_back(*iter);}
}
// 存储中心点区域
std::vector<cv::Moments> contours_moments;
for (auto iter = answer_contours.begin(); iter != answer_contours.end(); iter++)
{cv::Moments countour_moments = moments(*iter, false);contours_moments.push_back(countour_moments);
}
// 计算中心矩
std::vector<cv::Point2f> moments_point;
for (auto iter = contours_moments.begin(); iter != contours_moments.end(); iter++)
{cv::Point2f point = cv::Point2f((*iter).m10 / (*iter).m00, (*iter).m01 / (*iter).m00);moments_point.push_back(point);
}
// TODO: 根据中心点位置计算题号和答案
// ......
}

2.2.2 数字识别

数字识别相较于选择题识别更加复杂一点,需要进行模板匹配。

PS: 通过深度学习的方法实现效果会更好。

这里我使用的是较为简单一点的模板识别,这是匹配用得模板:

按照选择题识别的方法,将0~9的数字放在一个图像上,通过OpenCV提供的匹配函数定位识别区域中心的位置。为了使识别更精准,现将待匹配的数字图像提取其最大连通域进行匹配。下面是代码实现的过程:

int numMatch(cv::Mat& src, int method)
{// 模板简单处理cv::Mat model = cv::imread("model.jpg", CV_BGR2GRAY);// 调整模板至便于匹配的大小cv::resize(model, model, cv::Size(src.cols * 10, src.rows));// 取数字部分连通域,排除干扰因素src = getMaxContourRect(~src);src = ~src;// 单独识别1,因为1的连通域最小,长宽比大if (src.rows / src.cols > 2)return 1;// 带识别图像处理(单通道变三通道)cv::Mat three_ch = cv::Mat::zeros(src.rows, src.cols, CV_8UC3);std::vector<cv::Mat>channels;for (int i = 0; i < 3; i++)channels.push_back(src);cv::merge(channels, three_ch);// 用matchTemplate()函数匹配数字cv::Mat result;cv::matchTemplate(model, three_ch, result, method);cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());// 通过匹配的区域的中心点坐标定位数字double minVal, maxVal;cv::Point minLoc, maxLoc;cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());int ans = (minLoc.x + three_ch.cols / 2) / (model.cols / 10);return ans;
}

三、运行结果

试卷号: 073008
手机号: 15764713111
选择题答案:
01:A 02:B 03:C 04:D 05:A
06:A 07:B 08:C 09:D 10:B
11:B 12:B 13:C 14:D 15:A
16:A 17:B 18:C 19:B 20:A
21:A 22:B 23:C 24:D 25:A
26:A 27:B 28:C 29:D 30:A
31:A 32:B 33:C 34:D 35:B
36:A 37:B 38:B 39:D 40:A
请按任意键继续. . .

四、总结

这个程序存在一些缺陷,即识别过于单一化,识别精度不够。对机读卡图片的要求很高,更适用于机器固定扫描的图片。

项目很简单,主要是我学习OpenCV基础后一些简单的实际应用。

基于OpenCV的简单机读卡识别相关推荐

  1. opencv+python机读卡识别

    向AI转型的程序员都关注了这个号???????????? 机器学习AI算法工程   公众号:datayx 1.预处理 这次的机读卡识别项目来源暑期培训,主要包括内容一张手机拍摄的机读卡位置定位,识别其 ...

  2. opencv+python机读卡识别(一)预处理

    2019独角兽企业重金招聘Python工程师标准>>> 第二部分图像切割:https://my.oschina.net/u/3268732/blog/1236344 第三部分选择题识 ...

  3. python识别图像数字诊断模块_opencv+python 机读卡识别

    长按上图识别二维码报名济南源创会 摘要: 通过随意一张机读卡的照片,识别其中选择题题号,选项,以及相关数字识别.这个系列的解决方案不止一种,调参的方法也是各种各样,反正能够满足需求就极好了 1.预处理 ...

  4. python 答题卡识别_opencv+python机读卡识别整合版

    稍微整理了一下这个系列的一二三四章,可能看着更舒服吧--这个系列的解决方案不止一种,调参的方法也是各种各样,反正能够满足需求就极好了 1.预处理 这次的机读卡识别项目来源暑期培训,主要包括内容一张手机 ...

  5. python 答题卡识别_opencv+python机读卡识别(初级版)

    最近在进一步学习Python,在网上发现有使用opencv进行机读卡识别的, 就根据大神的文章,跟着学习,自己写了一个机读卡识别, 文章一:opencv+python机读卡识别整合版 文章二:pyth ...

  6. python 答题卡识别_opencv+python机读卡识别(最终版)

    本文是对之前编写的机读卡进行完善, 只记录相关代码,不介绍具体编写流程, 具体流程:opencv+python机读卡识别(进阶版) 完善相关机读卡的适配,记录相关调试函数以及使用方法. # -*- c ...

  7. OpenCV机读卡识别

    简单介绍 编写一个基于OpenCV的小程序,用于识别下图所示机读卡. 步骤回顾 图像处理 图像二值化 图像识别离不开图像的处理.用相机拍摄的机读卡基本都是三通道的彩色图像,而这里需要用到的处理方法就是 ...

  8. opencv+python机读卡识别(四)百度API进行数字识别

    2019独角兽企业重金招聘Python工程师标准>>> 第一部分预处理:https://my.oschina.net/u/3268732/blog/1236298 第二部分图像切割: ...

  9. python中的opencv读取数字_opencv+python 机读卡识别之试错(一)模板匹配的数字识别...

    图像来源于第四部分的数字,用任意截图工具截取部分图像当作模板,比如这样: 将模板与图像对比,这个方法根据matchTemplate函数只能选出整幅图里最匹配的图像,并不能找出所有,若想找出所有,必须不 ...

最新文章

  1. 15行代码AC_ 【蓝桥杯】兴趣小组(解题报告+思考)
  2. dede问答模块 那个php文件相对重要,DEDE5.7 问答模板的修改技巧
  3. 从JDK 6升级到JDK 7过程中遇到的一个问题(卸载rpm)
  4. 测试——设计思维之获取反馈
  5. 微信公众账号 token 验证失败 解决办法
  6. 苹果推出iCloud照片转移服务 能轻松转到谷歌相册
  7. java 数字的进制转换
  8. LPC1768的USB-相关结构体定义
  9. [Algorithm] Fibonacci Sequence - Anatomy of recursion and space complexity analysis
  10. Javascript命名禁区有哪些
  11. 编译原理完整学习笔记(一):引论
  12. 汇编语言 王爽 第四版 第二章 检测点2.1
  13. 萌新做点小玩意儿DAY-2 五子棋AI拓展思想
  14. VFP_全面控制EXCEL
  15. 百度推广关键字质量度优化指南
  16. 杭州车牌摇号规则详细内容
  17. eclipse安装tomcat时只有locahost,不显示server name
  18. 山东大学——结算中的贸易单据
  19. Python 图_系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径算法
  20. nodejs插件knex 日志打印

热门文章

  1. J酒店上海中心:云端艺邸 数字典藏
  2. windows主机如何登录阿里云服务器
  3. [2016物联网博览会]阿里王坚:万物互联网让数据真正发挥价值
  4. H.264系列文章(三)——帧内预测
  5. USB学习入门(四)------众里寻他千百度(linux)
  6. wszystkie hoopery Buty Adidas Crazy BYW sklep
  7. Android音频架构
  8. WindowsDBCA建库
  9. 优质的草图大师素材 草图66!
  10. jQuery生成动态表格