感謝 http://www.reibang.com/p/ac4c4536ca3e 很早就想做的一個demo
寫在前面
用到的技術(shù):
圖像處理技術(shù), 包括包括灰度化處理猾昆,二值化儒搭,腐蝕脐湾,輪廊檢測,
文字識別技術(shù): 通過ocr識別技術(shù)可以將圖片中包含的數(shù)字信息以字符串的方式輸出。
3.開源庫
開源框架OpenCV和TesseractOCRiOS
OpenCV(完成圖像處理技術(shù))
??OpenCV是一個開源的跨平臺計算機(jī)視覺和機(jī)器學(xué)習(xí)庫您单,通俗點(diǎn)的說却紧,就是他給計算機(jī)提供了一雙眼睛,一雙可以從圖片中獲取信息的眼鏡惨撇,從而完成人臉識別伊脓、身份證識別、去紅眼魁衙、追蹤移動物體等等的圖像相關(guān)的功能报腔。opencv官網(wǎng)
TesseractOCRiOS(完成文字識別技術(shù))
??Tesseract是目前可用的最準(zhǔn)確的開源OCR引擎,可以讀取各種格式的圖片并將他們轉(zhuǎn)換成各種語言文本剖淀。而TesseractOCRiOS則是針對iOS平臺封裝的Tesseract引擎庫纯蛾。必須保證背景純凈
灰度化處理:圖片灰度化處理就是將指定圖片每個像素點(diǎn)的RGB三個分量通過一定的算法計算出該像素點(diǎn)的灰度值,使圖像只含亮度而不含色彩信息纵隔。
二值化:二值化處理就是將經(jīng)過灰度化處理的圖片轉(zhuǎn)換為只包含黑色和白色兩種顏色的圖像翻诉,他們之間沒有其他灰度的變化。在二值圖中用255便是白色捌刮,0表示黑色碰煌。
腐蝕:圖片的腐蝕就是將得到的二值圖中的黑色塊進(jìn)行放大。即連接圖片中相鄰黑色像素點(diǎn)的元素绅作。通過腐蝕可以把身份證上的身份證號碼連接在一起形成一個矩形區(qū)域芦圾。
輪廊檢測:圖片經(jīng)過腐蝕操作后相鄰點(diǎn)會連接在一起形成一個大的區(qū)域,這個時候通過輪廊檢測就可以把每個大的區(qū)域找出來棚蓄,這樣就可以定位到身份證上面號碼的區(qū)域堕扶。
說完就開始吧
首先, 用CocoPods導(dǎo)入上面兩個庫, 這個demo做完差不多快200M
由于導(dǎo)入的庫不支持Bitcode機(jī)制,需要關(guān)掉梭依,在工程->TARGETS->Build Setting-> Enable Bitcode設(shè)置為NO
TesseractOCRiOS庫中沒有自帶的語言包稍算,需要我們自己手動導(dǎo)入,我們這里直接到tesseract-ocr網(wǎng)站役拴,tessdata即是我們需要用到的語言包糊探。下載下來的語言包有400多兆。這里我們只需要用到英語語言包河闰,所以就只導(dǎo)入eng.traineddata就ok科平,其他的都刪掉。
導(dǎo)入語言包種需要注意幾點(diǎn):
- 語言包需要放在tessdata目錄下姜性。TesseractOCRiOS中查找語言包是在tessdata目錄下進(jìn)行查找的瞪慧,所以我們不能單獨(dú)把eng.traineddata導(dǎo)入項(xiàng)目中,而需要放在tessdata目錄下導(dǎo)入項(xiàng)目中部念。
- 將tessdata導(dǎo)入xcode項(xiàng)目弃酌,需要勾選Create folder refrences氨菇。上面已經(jīng)提到了語言包需要放在tessdata目錄下,所以導(dǎo)入文件到xcode的時候需要創(chuàng)建文件夾的形式妓湘,而不是創(chuàng)建組的形式查蓉。
然后, 創(chuàng)建一個RecogizeCardManager用來管理身份證識別相關(guān)的代碼。
由于OpenCV和TesseractOCRiOS庫都是基于c++編寫的榜贴,所以需要把RecogizeCardManager.m后綴的.m改成.mm
VC里面子控件布局以及取怎么使用拍照和去相冊取照片以前寫過, 不再寫, 只寫manager里面的東西
創(chuàng)建一個NAReconizeIdCardManager繼承于NSObject
引入
#import <opencv2/opencv.hpp>
//#import <opencv2/imgproc/types_c.h>
#import <opencv2/imgcodecs/ios.h>
#import <TesseractOCR/TesseractOCR.h>
寫個單例聲明出去
+ (instancetype)sharedManager{
static NAReconizeIdCardManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[NAReconizeIdCardManager alloc] init];
});
return manager;
}
外部傳入UIImage然后進(jìn)行處理, 回調(diào)一個NSString作為身份證碼
- (void)reconizeWithImage:(UIImage *)image complete:(void (^)(NSString *))complete{
//掃描身份證圖片, 并進(jìn)行預(yù)處理, 定位號碼區(qū)域圖片并返回
UIImage *numberImage = [self opencvScanCard:image];
if (numberImage == nil) {
complete(nil);
}
//利用TesseractOCR識別文字
[self tesseractReconizeImage:numberImage completion:^(NSString *numberText) {
complete(numberText);
}];
}
#pragma mark - 預(yù)處理圖片, 定位號碼區(qū)域并返回image
- (UIImage *)opencvScanCard:(UIImage *)image{
//將UIImage轉(zhuǎn)換成Mat
cv::Mat resultImage; //#import <opencv2/opencv.hpp>
UIImageToMat(image, resultImage);//#import <opencv2/imgcodecs/ios.h>
//轉(zhuǎn)為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾yu值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕, 填充(腐蝕是讓黑色點(diǎn)變大)
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;
}
#pragma mark - 利用TesseractOCR識別文字
//#import <TesseractOCR/TesseractOCR.h>
- (void)tesseractReconizeImage:(UIImage *)image completion:(void (^)(NSString *))completion{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"eng"];
tesseract.image = [image g8_blackAndWhite];
tesseract.image = image;
//開始識別
[tesseract recognize];
//回調(diào)結(jié)果
completion(tesseract.recognizedText);
});
}