簡介
身份證識別,又稱OCR技術(shù)。OCR技術(shù)是光學(xué)字符識別的縮寫,是通過掃描等光學(xué)輸入方式將各種票據(jù)、報刊、書籍、文稿及其它印刷品的文字轉(zhuǎn)化為圖像信息,再利用文字識別技術(shù)將圖像信息轉(zhuǎn)化為可以使用的計算機(jī)輸入技術(shù)。
實現(xiàn)原理及步驟
- 灰度化處理
- 二值化
- 腐蝕:
- 輪廊檢測
- 圖像文字識別,
圖片灰度化處理,就是將指定圖片每個像素點的RGB三個分量通過一定的算法計算出該像素點的灰度值,使圖像只含亮度而不含色彩信息。
二值化處理就是將經(jīng)過灰度化處理的圖片轉(zhuǎn)換為只包含黑色和白色兩種顏色的圖像,他們之間沒有其他灰度的變化。在二值圖中用255便是白色,0表示黑色
圖片的腐蝕就是將得到的二值圖中的黑色塊進(jìn)行放大。即連接圖片中相鄰黑色像素點的元素。通過腐蝕可以把身份證上的身份證號碼連接在一起形成一個矩形區(qū)域。
圖片經(jīng)過腐蝕操作后相鄰點會連接在一起形成一個大的區(qū)域,這個時候通過輪廊檢測就可以把每個大的區(qū)域找出來,這樣就可以定位到身份證上面號碼的區(qū)域。
將圖像信息轉(zhuǎn)化為可以使用的計算機(jī)輸入技術(shù)。比如下面這張包含一串?dāng)?shù)字的圖片,通過ocr識別技術(shù)可以將圖片中包含的數(shù)字信息以字符串的方式輸出。
使用到開源庫
OpenCV 和 TesseractOCRiOS
OpenCV是一個開源的跨平臺計算機(jī)視覺和機(jī)器學(xué)習(xí)庫,通俗點的說,就是他給計算機(jī)提供了一雙眼睛,一雙可以從圖片中獲取信息的眼鏡,從而完成人臉識別、身份證識別、去紅眼、追蹤移動物體等等的圖像相關(guān)的功能
Tesseract是目前可用的最準(zhǔn)確的開源OCR引擎,可以讀取各種格式的圖片并將他們轉(zhuǎn)換成各種語言文本。而TesseractOCRiOS則是針對iOS平臺封裝的Tesseract引擎庫。
cocopods 引入相應(yīng)框架


我再測試的時候,發(fā)現(xiàn)OpenVC 3.4.2版本報錯,沒有找到原因,就選用了下面的版本
# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'
target 'RecognitionDemo' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for RecognitionDemo
target 'RecognitionDemoTests' do
inherit! :search_paths
# Pods for testing
end
target 'RecognitionDemoUITests' do
inherit! :search_paths
# Pods for testing
end
pod 'OpenCV', '~>3.1.0.1'
pod 'TesseractOCRiOS', '~> 4.0.0'
end
導(dǎo)入完成之后運(yùn)行項目,會發(fā)現(xiàn)報如下錯誤

由于導(dǎo)入的庫不支持Bitcode機(jī)制,需要關(guān)掉,在工程->TARGETS->Build Setting-> Enable Bitcode設(shè)置為NO就ok。

導(dǎo)入TesseractOCRiOS需要的語言包
TesseractOCRiOS庫中沒有自帶的語言包,需要我們自己手動導(dǎo)入,我們這里直接到tesseract-ocr網(wǎng)站,tessdata即是我們需要用到的語言包。下載下來的語言包有400多兆。這里我們只需要用到英語語言包,所以就只導(dǎo)入eng.traineddata就ok,其他的都刪掉。
導(dǎo)入語言包種需要注意幾點:
語言包需要放在tessdata目錄下。TesseractOCRiOS中查找語言包是在tessdata目錄下進(jìn)行查找的,所以我們不能單獨把eng.traineddata導(dǎo)入項目中,而需要放在tessdata目錄下導(dǎo)入項目中。
將tessdata導(dǎo)入xcode項目,需要勾選Create folder refrences。上面已經(jīng)提到了語言包需要放在tessdata目錄下,所以導(dǎo)入文件到xcode的時候需要創(chuàng)建文件夾的形式,而不是創(chuàng)建組的形式

代碼實現(xiàn):
導(dǎo)入下面框架
#import <opencv2/opencv.hpp>
#import <opencv2/imgproc/types_c.h>
#import <opencv2/imgcodecs/ios.h>
#import <TesseractOCR/TesseractOCR.h>
灰度處理
+ (void)grayscaleWithImg:(UIImage *)image callback:(void(^)(UIImage *image))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
if (callback) {
callback(MatToUIImage(resultImage));
}
});
}
二值化
+ (void)binarizationWithImg:(UIImage *)image callback:(void(^)(UIImage *image))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
if (callback) {
callback(MatToUIImage(resultImage));
}
});
}
腐蝕,填充
+ (void)corrodeWithImg:(UIImage *)image callback:(void(^)(UIImage *image))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
//將UIImage轉(zhuǎn)換成Mat
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕,填充(腐蝕是讓黑色點變大)
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
cv::erode(resultImage, resultImage, erodeElement);
if (callback) {
callback(MatToUIImage(resultImage));
}
});
}
輪廓檢測
+ (void)outlineWithImg:(UIImage *)image callback:(void(^)(UIImage *image))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
//將UIImage轉(zhuǎn)換成Mat
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕,填充(腐蝕是讓黑色點變大)
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
cv::erode(resultImage, resultImage, erodeElement);
//輪廊檢測
std::vector<std::vector<cv::Point>> contours;//定義一個容器來存儲所有檢測到的輪廊
cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
cv::drawContours(resultImage, contours, -1, cv::Scalar(255),4);
if (callback) {
callback(MatToUIImage(resultImage));
}
});
}
獲取所以矩形區(qū)域
+ (void)getImgsWithImg:(UIImage *)image callback:(void(^)(NSArray<UIImage *> *images))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
//將UIImage轉(zhuǎn)換成Mat
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕,填充(腐蝕是讓黑色點變大)
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
cv::erode(resultImage, resultImage, erodeElement);
//輪廊檢測
std::vector<std::vector<cv::Point>> contours;//定義一個容器來存儲所有檢測到的輪廊
cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
cv::drawContours(resultImage, contours, -1, cv::Scalar(255),4);
//取出身份證號碼區(qū)域
std::vector<cv::Rect> rects;
cv::Rect numberRect = cv::Rect(0,0,0,0);
std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
NSMutableArray *imageArr = [NSMutableArray array];
for ( ; itContours != contours.end(); ++itContours) {
cv::Rect rect = cv::boundingRect(*itContours);
rects.push_back(rect);
//算法原理
numberRect = rect;
//身份證號碼定位失敗
if (numberRect.width != 0 && numberRect.height != 0) {
//定位成功成功,去原圖截取身份證號碼區(qū)域,并轉(zhuǎn)換成灰度圖、進(jìn)行二值化處理
cv::Mat matImage;
UIImageToMat(image, matImage);
resultImage = matImage(numberRect);
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
cv::threshold(resultImage, resultImage, 80, 255, CV_THRESH_BINARY);
//將Mat轉(zhuǎn)換成UIImage
UIImage *numberImage = MatToUIImage(resultImage);
[imageArr addObject:numberImage];
}
}
if (callback) {
callback(imageArr);
}
});
}
獲取身份證號碼
//掃描身份證圖片,并進(jìn)行預(yù)處理,定位號碼區(qū)域圖片并返回
- (UIImage *)opencvScanCard:(UIImage *)image {
//將UIImage轉(zhuǎn)換成Mat
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕,填充(腐蝕是讓黑色點變大)
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
cv::erode(resultImage, resultImage, erodeElement);
//輪廊檢測
std::vector<std::vector<cv::Point>> contours;//定義一個容器來存儲所有檢測到的輪廊
cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
//取出身份證號碼區(qū)域
std::vector<cv::Rect> rects;
cv::Rect numberRect = cv::Rect(0,0,0,0);
std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
for ( ; itContours != contours.end(); ++itContours) {
cv::Rect rect = cv::boundingRect(*itContours);
rects.push_back(rect);
//算法原理
if (rect.width > numberRect.width && rect.width > rect.height * 5) {
numberRect = rect;
}
}
//身份證號碼定位失敗
if (numberRect.width == 0 || numberRect.height == 0) {
return nil;
}
//定位成功成功,去原圖截取身份證號碼區(qū)域,并轉(zhuǎn)換成灰度圖、進(jìn)行二值化處理
cv::Mat matImage;
UIImageToMat(image, matImage);
resultImage = matImage(numberRect);
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
cv::threshold(resultImage, resultImage, 80, 255, CV_THRESH_BINARY);
//將Mat轉(zhuǎn)換成UIImage
UIImage *numberImage = MatToUIImage(resultImage);
return numberImage;
}
圖像轉(zhuǎn)文字
+ (void)tesseractRecognizeImage:(UIImage *)image callback:(void(^)(NSString *text))callback
{
dispatch_async(dispatch_get_main_queue(), ^{
G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"chi_sim"];
tesseract.image = [image g8_blackAndWhite];
tesseract.image = image;
// Start the recognition
[tesseract recognize];
//執(zhí)行回調(diào)
callback(tesseract.recognizedText);
});
}