感謝 http://www.lxweimin.com/p/ac4c4536ca3e 很早就想做的一個demo
寫在前面
用到的技術:
圖像處理技術, 包括包括灰度化處理,二值化,腐蝕,輪廊檢測,
文字識別技術: 通過ocr識別技術可以將圖片中包含的數字信息以字符串的方式輸出。
3.開源庫
開源框架OpenCV和TesseractOCRiOS
OpenCV(完成圖像處理技術)
??OpenCV是一個開源的跨平臺計算機視覺和機器學習庫,通俗點的說,就是他給計算機提供了一雙眼睛,一雙可以從圖片中獲取信息的眼鏡,從而完成人臉識別、身份證識別、去紅眼、追蹤移動物體等等的圖像相關的功能。opencv官網
TesseractOCRiOS(完成文字識別技術)
??Tesseract是目前可用的最準確的開源OCR引擎,可以讀取各種格式的圖片并將他們轉換成各種語言文本。而TesseractOCRiOS則是針對iOS平臺封裝的Tesseract引擎庫。必須保證背景純凈
灰度化處理:圖片灰度化處理就是將指定圖片每個像素點的RGB三個分量通過一定的算法計算出該像素點的灰度值,使圖像只含亮度而不含色彩信息。
二值化:二值化處理就是將經過灰度化處理的圖片轉換為只包含黑色和白色兩種顏色的圖像,他們之間沒有其他灰度的變化。在二值圖中用255便是白色,0表示黑色。
腐蝕:圖片的腐蝕就是將得到的二值圖中的黑色塊進行放大。即連接圖片中相鄰黑色像素點的元素。通過腐蝕可以把身份證上的身份證號碼連接在一起形成一個矩形區域。
輪廊檢測:圖片經過腐蝕操作后相鄰點會連接在一起形成一個大的區域,這個時候通過輪廊檢測就可以把每個大的區域找出來,這樣就可以定位到身份證上面號碼的區域。
說完就開始吧
首先, 用CocoPods導入上面兩個庫, 這個demo做完差不多快200M
由于導入的庫不支持Bitcode機制,需要關掉,在工程->TARGETS->Build Setting-> Enable Bitcode設置為NO
TesseractOCRiOS庫中沒有自帶的語言包,需要我們自己手動導入,我們這里直接到tesseract-ocr網站,tessdata即是我們需要用到的語言包。下載下來的語言包有400多兆。這里我們只需要用到英語語言包,所以就只導入eng.traineddata就ok,其他的都刪掉。
導入語言包種需要注意幾點:
- 語言包需要放在tessdata目錄下。TesseractOCRiOS中查找語言包是在tessdata目錄下進行查找的,所以我們不能單獨把eng.traineddata導入項目中,而需要放在tessdata目錄下導入項目中。
- 將tessdata導入xcode項目,需要勾選Create folder refrences。上面已經提到了語言包需要放在tessdata目錄下,所以導入文件到xcode的時候需要創建文件夾的形式,而不是創建組的形式。
然后, 創建一個RecogizeCardManager用來管理身份證識別相關的代碼。
由于OpenCV和TesseractOCRiOS庫都是基于c++編寫的,所以需要把RecogizeCardManager.m后綴的.m改成.mm
VC里面子控件布局以及取怎么使用拍照和去相冊取照片以前寫過, 不再寫, 只寫manager里面的東西
創建一個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然后進行處理, 回調一個NSString作為身份證碼
- (void)reconizeWithImage:(UIImage *)image complete:(void (^)(NSString *))complete{
//掃描身份證圖片, 并進行預處理, 定位號碼區域圖片并返回
UIImage *numberImage = [self opencvScanCard:image];
if (numberImage == nil) {
complete(nil);
}
//利用TesseractOCR識別文字
[self tesseractReconizeImage:numberImage completion:^(NSString *numberText) {
complete(numberText);
}];
}
#pragma mark - 預處理圖片, 定位號碼區域并返回image
- (UIImage *)opencvScanCard:(UIImage *)image{
//將UIImage轉換成Mat
cv::Mat resultImage; //#import <opencv2/opencv.hpp>
UIImageToMat(image, resultImage);//#import <opencv2/imgcodecs/ios.h>
//轉為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾yu值二值化
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));
//取出身份證號碼區域
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;
}
//定位成功, 去原圖截取身份證號碼區域, 并轉換為灰度圖, 進行二值化處理
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轉換成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];
//回調結果
completion(tesseract.recognizedText);
});
}