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

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

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

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

具体代码实现

首先用OpenCV加载图片文件:

cpp 复制代码
Mat srcImage = imread(".\\assets\\pigidcard.png");

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

cpp 复制代码
Mat grayImg;
cv::cvtColor(srcImage, grayImg, COLOR_BGR2GRAY);
Mat binary;
cv::threshold(grayImg, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

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

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

cpp 复制代码
Mat kernel = cv::getStructuringElement(MORPH_RECT, Size(26, 26));
Mat dilation;
cv::dilate(binary, dilation, kernel);

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

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

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

cpp 复制代码
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源代码编译成静态库。)

cpp 复制代码
#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

相关推荐
2401_885665198 分钟前
从神经元到BP反向传播,零基础吃透神经网络底层原理
人工智能·python·深度学习·神经网络·opencv
AI人工智能+33 分钟前
往来港澳通行证识别系统,深度融合计算机视觉与自然语言处理,为“智慧口岸”和“数字政务”提供了强有力的技术支撑
人工智能·深度学习·ocr·往来港澳通行证识别
闻道且行之35 分钟前
Hair Segmentation:MediaPipe 头发分割模块 CMake 独立编译
c++·人工智能·深度学习·神经网络·opencv·计算机视觉
打小就很皮...1 小时前
基于 Python + LangChain + React 实现智能发票识别与验真系统实战
前端·react.js·langchain·ocr·发票识别
weixin_307779131 小时前
从切片迷宫到结构化智能:AI Agent解析PDF的完整范式
图像处理·人工智能·python·自动化·ocr
天天代码码天天1 小时前
用 OpenCV 5 DNN 跑 PP-OCR:一个适合新手学习的 C++ 动态库 + C# 可视化测试项目
opencv·ocr·dnn·opencv5·ppocrv6
王莎莎-MinerU20 小时前
面向大模型工作流的文档解析:从OCR到MinerU的深度技术指南
网络·ocr
ai_coder_ai1 天前
使用ocr实现自动化脚本
运维·自动化·ocr
番石榴AI1 天前
JiaJiaOCR-2.2.0:面向Java ocr的开源库
java·ocr
企业知识库布道者1 天前
从 OCR 到文档结构理解:MinerU-Popo 对 RAG 文档解析链路的补全
人工智能·ocr·私有化部署·知识库·rag·企业知识库