OpenCV身份證離線識別技術的主要技術就是通過OpenCV找到身份證號碼區域,然后通過OCR進行數字識別該區域的截圖即可得到身份證號碼,本地ORC使用tess-two來完成,Tesseract是C++實作的OCR引擎,在Android中使用不是很方便,需要封裝JavaAPI才能在Android平臺中進行呼叫,然而tess-two已經幫我們做好了這些事情,通過集成tess-two就可以很方便的完成文字識別,
總體思路

影像的預處理
1、無損壓縮
首先要處理的問題就是圖片的大小不一樣,因為每臺設備的的像素或者說每個圖片的大小本身都不一樣,處理程序也會有所差異,所以首先解決的問題就是大小統一,先通過無損壓縮把圖片處理為大小一致的影像,根據經驗值(或者說這是處理證件類的通用手法),先把影像處理為640×400的大小,
2、灰度化
現在大部分的彩色影像都是采用RGB顏色模式,處理影像的時候,要分別對RGB三種分量進行處理,實際上RGB并不能反映影像的形態特征,只是從光學的原理上進行顏色的調配,影像灰度化處理可以作為影像處理的預處理步驟,為之后的影像分割、影像識別和影像分析等上層操作做準備,
其實可以仔細想想,如果是處理一張RGB影像的話,一個像素點需要同時處理3個值,灰度化之后只需要處理一個值,如果是對比的話,一個RGB像素點就有256×256×256種可能,但是如果是對比灰度圖的像素點,則只有256種可能,65536倍的速度提升,所以很多時候做其他影像處理之前,先轉化為灰度圖,
影像灰度化處理有分量法、最大值法、平均值法、加權平均法,其中用得較多的是加權平均法,由于人眼對綠色的敏感最高,對藍色敏感最低,因此,按下式對RGB三分量進行加權平均能得到較合理的灰度影像:
$$
\operatorname{Gray}(i, j)=0.299 * R(i, j)+0.578 * G(i, j)+0.114 * B(i, j)
$$
3、影像二值化
通過以上對彩色圖片進行灰度化以后,把獲取到的灰度影像進行二值化處理,對于二值化,其目的是將目標用戶背景分類,為后續車道的識別做準備,灰度影像二值化最常用的方法是閾值法,他利用影像中目標與背景的差異,把影像分別設定為兩個不同的級別,選取一個合適的閾值,以確定某像素是目標還是背景,從而獲得二值化的影像,比如以100為閾值對影像進行二值化操作:
$$
f(i, j) = \left{\begin{array}{cc}
0, & (\text { gray }< = 100) \
255, & (\text { gray }>100)
\end{array}\right.
$$
4、膨脹與腐蝕
膨脹與腐蝕屬于影像處理中最基本的形態學運算,形態學操作就是基于形狀的一系列影像處理操作,OpenCV為進行影像的形態學變換提供了快捷且方便的函式,主要用于噪聲消除、分割出獨立的影像元素、在影像中連接相鄰的元素、尋找影像中的明顯的極大值區域或極小值區域、求出影像的梯度,
簡單理解,膨脹就是求區域最大值的操作,腐蝕就是求區域最小值的操作,在處理身份證的時候,我們希望把身份證號碼等數字區域連接在一起,即在影像中連接相鄰的元素,所以需要使用膨脹處理,就跟蒸饅頭的酵母粉一樣,可以是我們想要的元素膨脹并且黏合在一起,
5、輪廓檢測與影像分割
通過影像的膨脹操作,身份證號碼區域已經被連接在一起了,目前需要做的事情就是檢測出該區域的輪廓,使用拉普拉斯算子可以完成這個操作,OpenCV內部也提供了findContours函式做輪廓檢測,
那么如何分割出身份證號碼區域呢?其實有一個非常簡單的思路,由于身份證號碼是一串不換行的數字,寬高比通常是大于9:1的,而且是位于最后一行的,如果有其他的部分的寬高比大于9:1但是卻不是位于最后,那么也不能認為是身份證號碼,只有坐標是最底部,而且寬高比滿足大于9:1的條件才可以,
VS2022 + OpenCV4.5.4
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#define DEFAULT_CARD_WIDTH 640
#define DEFAULT_CARD_HEIGHT 400
#define FIX_IDCARD_SIZE Size(DEFAULT_CARD_WIDTH, DEFAULT_CARD_HEIGHT)
#define FIX_TEMPLATE_SIZE Size(153, 28)
using namespace std;
using namespace cv;
int main() {
std::cout << "Hello, World!" << std::endl;
Mat src = imread("src.png");
imshow("src", src);
//處理身份證
Mat src_img = src;
//1、無損壓縮 640*400 (通用卡片類的處理方式)
resize(src_img, src_img, FIX_IDCARD_SIZE);
imshow("dst", src_img);
Mat dst_img;
//2、灰度化
Mat dst;
cvtColor(src_img, dst, COLOR_BGR2GRAY);
imshow("gray", dst);
//3、二值化(降噪)
threshold(dst, dst, 100, 255, THRESH_BINARY);
imshow("threshold", dst);
// 4.1 腐蝕、膨脹
Mat erodeElement = getStructuringElement(MORPH_RECT, Size(20, 10));
erode(dst, dst, erodeElement);
imshow("erode", dst);
//4、輪廓檢測,把所有的連續的閉包用矩形包起來
/*
* 一個矩形用兩個點表示,contours就包含了很多矩形
*/
vector<vector<Point>> contours;
vector<Rect> rects;
findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
for (size_t i = 0; i < contours.size(); i++)
{
// 基于兩點構建矩形
Rect rect = boundingRect(contours.at(i));
// 繪制矩形
rectangle(dst, rect, Scalar(0, 0, 255));
imshow("contours", dst);
// 對符合條件的圖片進行篩選,寬高比大于1:9的
if(rect.width > rect.height*9)
{
cout << "找到了" << endl;
rects.push_back(rect);
rectangle(dst, rect, Scalar(0, 0, 255));
// 還需要再次矯正
//dst_img = src_img(rect);
}
}
// imshow("dst_Img", dst_img);
// 如果只找到了一個矩形,說明這個就是,如果多個就找出縱坐標最低的矩形
if(rects.size() == 1)
{
Rect rect = rects.at(0);
dst_img = src_img(rect);
}else
{
int lowPoint = 0;
Rect finalRect;
for (size_t i = 0; i < rects.size(); ++i)
{
Rect rect = rects.at(i);
Point p = rect.tl();
if(rect.tl().y > lowPoint)
{
lowPoint = rect.tl().y;
finalRect = rect;
}
}
rectangle(dst, finalRect, Scalar(255, 255, 0));
dst_img = src_img(finalRect);
}
imshow("dst_Img", dst_img);
waitKey();
return 0;
}
CMakeList.txt
cmake_minimum_required (VERSION 3.8)
project(opencv_idcard)
set(CMAKE_CXX_STANDARD 11)
add_executable (opencv_idcard "opencv_idcard.cpp" )
set(OpenCV_DIR "D:/develop/opencv-4.5.4/opencv-4.5.4-build")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(opencv_idcard ${OpenCV_LIBS})
來看看通過一系列的處理效果吧:

接下來要干的事情就主要有兩件,首先是繼承tess-two到Android,這樣離線識別便搞定了,另外一件事情就是影像預處理的代碼移植到Android上,這兩件事情完成便搞定了身份證號碼離線識別的功能了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398535.html
標籤:其他
