关于二维码识别,我们一般都是用的 Zxing 或者 Zbar ,但它们的识别率其实不是很高,有些情况下是失灵的,比如下面这两张图:

使用开源库 Zxing 扫描以上两张二维码,有一张死活不识别。使用微信是可以的,大家可以用支付宝试试(不行),那碰到这种情况到底该怎么办呢?哈哈,这次终于有用武之地了,我们琢磨着来优化一把。

我们在微信公众号都用过这么一个功能,长按一张图片,如果该图片包含有二维码,会弹出识别图中二维码,如果该图片不含有二维码,则不会弹出识别二维码这个选项。说到这里我们大概应该知晓了,识别二维码其实分为两步,**第一步是发现截取二维码区域,第二步是识别截取到的二维码区域。**那么 zxing 和支付宝到底是哪一步出了问题呢?首先我们来看一下第一步发现截取二维码区域。

上图是一张常用的二维码事例图,有三个比较重要的区域,分别是左上,右上和左下,我们只要能找到这三个特定的区域,就能判定图片中包含有二维码。接下来我们来分析一下思路:

1. 对其进行轮廓查找
2. 对查找的到的轮廓进行初步过滤
3. 判断是否符合二维码的特征规则
4. 截取二维码区域
5. 识别二维码

//  判断 X 方向上是否符合规则
bool isXVerify(const Mat& qrROI){... 代码省略// 判断 x 方向从左到右的像素比例// 黑:白:黑:白:黑 = 1:1:3:1:1
}//  判断 Y 方向上是否符合规则
bool isYVerify(const Mat& qrROI){... 代码省略// y 方向上也可以按照 isXVerify 方法判断// 但我们也可以适当的写简单一些// 白色像素 * 2 < 黑色像素 && 黑色像 < 4 * 白色像素
}int main(){Mat src = imread("C:/Users/hcDarren/Desktop/android/code1.png");if (!src.data){printf("imread error!");return -1;}imshow("src", src);// 对图像进行灰度转换Mat gary;cvtColor(src, gary, COLOR_BGR2GRAY);// 二值化threshold(gary, gary, 0, 255, THRESH_BINARY | THRESH_OTSU);imshow("threshold", gary);// 1. 对其进行轮廓查找vector<vector<Point> > contours;findContours(gary, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);for (int i = 0; i < contours.size(); i++){// 2. 对查找的到的轮廓进行初步过滤double area = contourArea(contours[i]);// 2.1 初步过滤面积 7*7 = 49if (area < 49){continue;}RotatedRect rRect = minAreaRect(contours[i]);float w = rRect.size.width;float h = rRect.size.height;float ratio = min(w, h) / max(w, h);// 2.2 初步过滤宽高比大小if (ratio > 0.9 && w< gary.cols/2 && h< gary.rows/2){Mat qrROI = warpTransfrom(gary, rRect);// 3. 判断是否符合二维码的特征规则if (isYVerify(qrROI) && isXVerify(qrROI)) {drawContours(src, contours, i, Scalar(0, 0, 255), 4);}}}imshow("src", src);imwrite("C:/Users/hcDarren/Desktop/android/code_result.jpg", src);waitKey(0);return 0;
}

**代码是非常简单的,关键是我们要善于学会去分析,多多培养解决问题的能力,只要知道实现思路,其他一切都不是问题了。**那么有意思的就来了,当扫描第二张图的时候,我们发现死活都识别不了。那么细心的同学可能明白了,我们上面的代码是按照正方形的特征来识别的,而第二张图是圆形的特征,因此 Zxing 无法识别也是正常的,因为咱们在写代码的时候根本没考虑这么个情况。那么我们怎么才能做到识别圆形的特征呢?考验我们的时候到了,我们能想到三种解决方案:

1. 再写一套识别圆形特征的代码
2. 借鉴人脸识别的方案,采用训练样本的方式识别
3. 换一种检查方案,只写一套代码

人脸识别在下期文章中会写到,训练样本的方式比较麻烦,如果之前没接触过,那么需要一定的时间成本,但这种方案应该是最好的。再写一套圆形识别的代码,感觉维护困难,作为一个有灵魂的工程师总觉得别扭。那这里我们就采用第三种方案了,其实知识点也就那么多,还是那句话多培养我们分析解决问题的能力

我们仔细观察,他们其实还是有很多共同点,我们对其进行轮廓筛选的时候会发现,都是一个大轮廓里面套两个小轮廓。具体流程如下:

1. 对其进行轮廓查找
2. 对查找的到的轮廓进行初步过滤
3. 判断是否是一个大轮廓套两个小轮廓且符合特征规则(面积比例判断)
4. 截取二维码区域
5. 识别二维码

extern "C"
JNIEXPORT jobject JNICALL
Java_com_darren_ndk_day76_MainActivity_clipQrBitmap(JNIEnv *env, jobject instance, jobject bitmap) {Mat src;cv_helper::bitmap2mat(env, bitmap, src);// 对图像进行灰度转换Mat gary;cvtColor(src, gary, COLOR_BGR2GRAY);// 二值化threshold(gary, gary, 0, 255, THRESH_BINARY | THRESH_OTSU);// 1. 对其进行轮廓查找vector<Vec4i> hierarchy;vector<vector<Point> > contours;vector<vector<Point> > contoursRes;/*参数说明:https://blog.csdn.net/guduruyu/article/details/69220296输入图像image必须为一个2值单通道图像contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。mode表示轮廓的检索模式CV_RETR_EXTERNAL 表示只检测外轮廓CV_RETR_LIST 检测的轮廓不建立等级关系CV_RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。CV_RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demomethod为轮廓的近似办法CV_CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1CV_CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。*/findContours(gary, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));int tCC = 0; // 临时用来累加的子轮廓计数器int pId = -1;// 父轮廓的 indexfor (int i = 0; i < contours.size(); i++) {if (hierarchy[i][2] != -1 && tCC == 0) {pId = i;tCC++;} else if (hierarchy[i][2] != -1) {// 有父轮廓tCC++;} else if (hierarchy[i][2] == -1) {// 没有父轮廓tCC = 0;pId = -1;}// 找到了两个子轮廓if (tCC >= 2) {contoursRes.push_back(contours[pId]);tCC = 0;pId = -1;}}// 找到过多的符合特征轮廓,对其进行筛选if (contoursRes.size() > FEATURE_NUMBER) {contoursRes = filterContours(gary, contoursRes);}// 没有找到符合的条件if (contoursRes.size() < FEATURE_NUMBER) {return NULL;}for (int i = 0; i < contoursRes.size(); ++i) {drawContours(src, contoursRes, i, Scalar(255, 0, 0), 2);}// 裁剪二维码,交给 zxing 或者 zbar 处理即可cv_helper::mat2bitmap(env, src, bitmap);return bitmap;
}

开发中我们最喜欢做的就是拿过来直接用,但最好还是明白其中的原理,因为我们无法断定开发中会出什么幺蛾子。像微信这样的大厂自然得自己这一套,其实好的框架能够拿过来优化优化,个人认为就已经差不多了。当然以上写法在某些特定场景下,可能还是会存在些许漏洞,这就靠我们不断的去琢磨优化了。

视频地址:https://pan.baidu.com/s/1m7Epc4TVNs8fSXi2ifXkhQ
视频密码:5g3z

NDK 开发实战 - 微信公众号二维码检测相关推荐

  1. 如何给CSDN博客添加微信公众号二维码或自定义栏目

    1. 前言 在浏览CSDN博客的过程中,我们可以发现有些博主的主页有微信公众号二维码等一些个人栏目信息.这可以让其他浏览博客的游客和作者进行有效的沟通,同时也可以在博客里对自己的微信公众号作宣传.我这 ...

  2. thinkphp5+php微信公众号二维码扫码关注推广二维码事件实现

    thinkphp5微信公众号二维码扫码关注推广二维码事件实现, 给出实现的全部方法: 获取二维码,让微信公众号跳转到以下代码的code方法即可,用户点击二维码后,通过二维码获取关注用户信息及二维码推广 ...

  3. 微信公众号二维码怎么生成?好用的生成方法介绍

    微信公众号二维码怎么生成?从事新媒体行业的小伙伴们应该都对微信公众号不陌生吧,公众号是一个企业对外宣传的重要方式之一.通常情况下我们都会将公众号的二维码放置在文章的文末.以便于读者扫码关注公众号.那么 ...

  4. 查看获取别人的微信公众号二维码

    亲测可用,若有疑问请私信 方法一:通过微信号获取公众号/订阅号二维码 这个方法需要知道公众号/订阅号的微信号,具体操作步骤如下: 2.打开浏览器(电脑或手机浏览器均可),访问以下链接地址: https ...

  5. 分享编程技术的微信公众号二维码

    微信公众号二维码 大家好,以下是我的微信公众号二维码,平时会在公众号里分享和总结一些技术心得,欢迎骚扰交流.

  6. 获取任意微信公众号二维码方法

    [获取任意微信公众号二维码方法] 复制下面链接在浏览器里打开,iOS微信打开会报错. https://open.weixin.qq.com/qr/code?username=PlayerYK 将以上链 ...

  7. 在next主题添加微信公众号二维码

    在侧边栏添加微信公众号二维码 首先,当然是准备一张微信公众号二维码.有两种添加方式,添加到侧边栏或者添加到推文的结尾处.我的next主题是7.x版本的,使用的主题是Gemini,设置的侧栏显示方式是一 ...

  8. 如何给CSDN博客添加个人微信公众号二维码或自定义栏目

    公众号查看文章更清晰 在使用CSDN的过程中,可以看到有些博主的主页有个人微信二维码,微信公众号二维码等一些个人栏目信息.这对作者而言,可以让其他浏览博客的游客和作者进行更有效的沟通,也可以在这里对自 ...

  9. 微信公众号二维码查看攻略

    微信公众号二维码查看攻略 通过下面的方法可以在知道微信号的前提下进行对应微信企业号二维码的查看,该方法仅限于企业微信号. 个人微信二维码查看: 手机微信界面→头像右侧二维码小图标→二维码名片 :点击查 ...

  10. 关注微信公众号二维码可以带参数吗?

    2019独角兽企业重金招聘Python工程师标准>>> 需求是这样的,用户可以分享自己的微信二维码,别人扫码进入,能监测到.功能怎么实现呢? 通过微号帮平台可以实现,微信公众平台接口 ...

最新文章

  1. 信息解码(Message Decoding)ACM题目
  2. Python使用matplotlib绘图并去除颜色样条colorbar实战:remove colorbar from figure in matplotlib
  3. 高性能NIO框架Netty入门篇
  4. 【Spring学习笔记】之【2.3Ioc配置使用】
  5. 沉没成本(要不要买房)
  6. LeetCode——Word Break
  7. 搭建SSM框架的配置文件
  8. 操作系统 第二章 进程管理
  9. 匿名函数与内置函数(python3入门)
  10. TensorFlow GAN项目程序回顾2020.12.03
  11. k8s安装nginx部署前端页面_Kubernetes(k8s)部署并测试nginx service
  12. 华为机试:提取不重复的整数
  13. Linux查看可执行文件依赖库
  14. bzoj3991 [SDOI2015]寻宝游戏 set
  15. telnet 和 ping的区别
  16. 会计计算机功能,会计软件具有什么功能
  17. print()和println()的区别
  18. 分酒问题与JOJ 1200 Jugs
  19. 获取当前所在地的天气
  20. 所有的时间伟大的好莱坞电影大片

热门文章

  1. GEE:LandTrendr时间序列曲线拟合
  2. node.js中express框架的使用
  3. activemq学习记录(二)(使用p2p模式和使用发布订阅模式去生产以及消费数据)
  4. 统一社会信用代码18位数字分别代表什么含义
  5. adventure项目案例分析
  6. 从逻辑学中的偷换概念谈起
  7. 2022年全新PHP程序开发在线工单管理系统源码,售后工单系统
  8. git 上上下下左左右右AABB
  9. 【vue-router源码】四、createRouter源码解析
  10. 如何用计算机算工资,个人工资计算器避税