当前位置: 首页 > news >正文

OpenCV小练习:身份证号码识别

目标:针对一张身份证照片,把身份证号码识别出来(转成数字或字符串)。

实现思路:需要将目标拆分成两个子任务:(1) 把身份证号码区域从整张图片中检测/裁剪出来;(2) 将图片中的数字转化成文字。第一个子任务用OpenCV(如何自行编译OpenCV源码?),第二个子任务主要仰仗Tesseract(注:Tesseract是著名的OCR文字识别开源项目)。

使用OpenCV做图像处理的大致过程为:首先要将彩色图像转成灰度图,再进一步做二值化转换。为了把身份证号码区域整个圈出来,需要继续对图像进行“膨胀”处理,使得每个数字的小区域都与相邻数字的小区域连接起来,连成一个大区域。这样处理之后,在用cv::findContours查找轮廓时,就可以根据身份证号码区域的面积和宽高比把它挑选出来了。

具体代码实现

首先用OpenCV加载图片文件:

Mat srcImage = imread(".\\assets\\pigidcard.png");

接着对图像进行灰度化和二值化处理:

Mat grayImg;
cv::cvtColor(srcImage, grayImg, COLOR_BGR2GRAY);
Mat binary;
cv::threshold(grayImg, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

到这一步,图像看起来是这样的:

接着要做“膨胀”处理。这一步非常关键!需要调整下面这个Size类型的内核大小,目标是让身份证号码的这些数字前后相连,形成一个整体的矩形区域。

Mat kernel = cv::getStructuringElement(MORPH_RECT, Size(26, 26));
Mat dilation;
cv::dilate(binary, dilation, kernel);

到这一步,图像看起来是这样的:

实际的轮廓/区域分布是这样的:

然后就是遍历图像中的所有轮廓。我们设定两个条件,当轮廓的面积以及轮廓外边框的宽高比都大于某个值(根据实际情况而定),我们就认为当前这个轮廓就是身份证号码区域,可以把它裁剪出来。

std::vector<std::vector<Point>> contours;
std::vector<Vec4i> hierarchy;
cv::findContours(dilation, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);for (size_t i = 0; i < contours.size(); i++) {double area = cv::contourArea(contours[i]);Rect roi = cv::boundingRect(contours[i]);double aspectRatio = (double)roi.width / roi.height;// 根据实际情况调整这两个阈值if (area > 40000 && aspectRatio > 10) {Mat cropped = binary(roi);imshow("ID Card - number only", cropped);// 继续使用 Tesseract OCR// …break;}
}

上面代码运行的结果:cropped对象是裁剪出来的仅含一串身份证号码的小图片。注意这是一个二值图,而且不是膨胀处理后的图像哦!接着轮到Tesseract登场了,把这个图片中的数字转成字符串。(注:请参考这篇文章自行把Tesseract源代码编译成静态库。)

#include "baseapi.h"
#include "allheaders.h"#pragma comment(lib, "leptonica-1.84.1.lib")
#pragma comment(lib, "tesseract54.lib")// 使用 Tesseract OCR
tesseract::TessBaseAPI tess;
if (tess.Init("tessdata", "eng") == 0) {tess.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);// Tesseract无法识别二值图!转换回RGB图像Mat ocrImg;cv::cvtColor(cropped, ocrImg, COLOR_GRAY2BGR);int bytesPerPixel = GetBytesPerPixel(ocrImg);tess.SetImage((uchar*)ocrImg.data, ocrImg.cols, ocrImg.rows, bytesPerPixel, ocrImg.cols * bytesPerPixel);char* outText = tess.GetUTF8Text();std::cout << "ID numbers: " << outText << std::endl;delete[] outText;tess.End();
}

打完收工!o(* ̄▽ ̄*)ブ

P.S. 完整的代码可以从这里下载:https://github.com/luqiming666/OpenCVMisc。查看OpenCVMiscDlg.cpp 文件中的_DetectIDCard_WithGoodDilation() 函数实现即可。我也上传了Tesseract库文件,但只有Release版。如果要验证OCR效果,需要把OpenCVMisc项目的配置切换到Release + x64,并且在OpenCVMiscDlg.cpp文件头部放开这个宏定义:#define _ENABLE_TESSERACT_

相关文章:

  • linux-----内核(Kernel)与文件系统(File System)
  • 排序---
  • 04:创建PADS Logic软件逻辑库
  • 乾元通渠道商中标湖南省煤业集团公司安全生产预防和应急救援能力建设装备配备采购项目
  • 快速安全部署 Tomcat
  • 电路笔记(PCB): kicad freerouting自动布线
  • UVM仿真的启动(二)—— uvm_phase::m_run_phase()
  • 微信小程序利用canva进行大图片压缩
  • 【人工智能】AI算法系统设计与算法建模的详细阐述
  • leetcode 1957 删除字符使字符串变好
  • 【ubuntu20.4 常用经验分享】
  • [RIS]GRES: Generalized Referring Expression Segmentation
  • JAVA中如何使用反射获取数组元素类型
  • [论文笔记] megatron 大模型超参搜索pipeline
  • 【练习】哈希表的使用
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • [iOS]Core Data浅析一 -- 启用Core Data
  • [译]如何构建服务器端web组件,为何要构建?
  • ESLint简单操作
  • Java 内存分配及垃圾回收机制初探
  • PHP 的 SAPI 是个什么东西
  • python 装饰器(一)
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • vue 配置sass、scss全局变量
  • 笨办法学C 练习34:动态数组
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 微信开放平台全网发布【失败】的几点排查方法
  • 消息队列系列二(IOT中消息队列的应用)
  • UI设计初学者应该如何入门?
  • ​​​【收录 Hello 算法】9.4 小结
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • # 利刃出鞘_Tomcat 核心原理解析(二)
  • # 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (13)DroneCAN 适配器节点(一)
  • (CVPRW,2024)可学习的提示:遥感领域小样本语义分割
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (四) Graphivz 颜色选择
  • (小白学Java)Java简介和基本配置
  • (一)基于IDEA的JAVA基础12
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • (转)德国人的记事本
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .Net环境下的缓存技术介绍
  • .NET上SQLite的连接