這段時間項目的需求,需要在注冊的時候進行身份證識別。就簡單的搞了一下。
身份證識別
項目的需求是通過攝像頭的采集獲取到身份證上面的一些數據,比如身份證號碼、姓名等信息。
而不是簡單的知曉身份證號碼后來一波正則判斷。
實現方案
- 首先通過手機攝像頭獲取到手機的身份證的圖片,務必要背景簡單,色調單一,主要是在后面處理圖像的時候對識別率的提升有幫助。
- 通過openCV對原始圖片處理,把圖片變成灰度圖->二值化->腐蝕、膨脹->輪廓檢測->剪裁等處理,使得圖片成為我們想要的樣式。
- 然后裁剪出合適的區域,如只要身份證的號碼的區域就只需要裁剪出身份證號碼即可。
- 通過OCR對圖片進行識別,讓圖片中顯示的文字轉換成文字。
- 識別好號碼后,進行一波身份證號碼識別的預處理,網上有很多的正則表達式,可以搜索下。我的代碼中也會有體現的。
- 通過身份證號碼就可以得知出生年月以及性別(18位身份證:第17位代表的是性別,奇數為男性,偶數為女性)
openCV相關
- opencv的相關代碼是用C++寫的,所以在引用這個opencv的頭文件的那個類,需要把.m文件修改為.mm文件。
- 相關的類引用了opencv,需要把引用的頭文件放在最前面,建議使用pch來引用,可以避免造成不必要的麻煩。
下面弄個簡單的代碼演示下:
-
原始圖片:女神 新垣結衣
我女神,新垣結衣 - 處理后的圖片
還是我女神
opencv demo下載里面沒有opencv的framework,另行[下載](鏈接: https://pan.baidu.com/s/1o8LjSgi) 密碼: 7ubn。
OCR相關
利用谷歌的tesseractOCR和tessdata的庫(其中有英文的庫和中文的庫)來識別處理完成后的圖片。
- 注意 : 其中tessdata拖入到工程中需要拖入一個真實的文件夾(藍色文件),而不是一個在工程中虛擬的文件夾。
引入的依賴庫
依賴庫
處理圖片
#pragma mark - 處理圖片得到身份證號碼圖片
//掃描身份證圖片,并進行預處理,定位號碼區域圖片并返回
- (UIImage *)opencvScanCardWithNumber:(UIImage *)image {
//將UIImage轉換成Mat
cv::Mat resultImage;
UIImageToMat(image, resultImage);
//轉為灰度圖
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//利用閾值二值化
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
//腐蝕,填充(腐蝕是讓黑色點變大)
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(27,27));
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);
//取出身份證號碼區域
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識別文字
- (void)tesseractRecognizeImage:(UIImage *)image compleate:(CompleateBlock)compleate {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"chi_sim"];
// tesseract.image = [image g8_blackAndWhite];
if (image == nil) {
NSLog(@"圖片沒有處理成功");
return;
}
tesseract.image = image;
// Start the recognition
BOOL done = [tesseract recognize];
//執行回調
compleate(tesseract.recognizedText);
});
}
獲取攝像頭的權限后,開啟攝像頭,在其代理中實現
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;
得到號碼后,可以對性別進行判斷
#pragma mark - 判斷性別
- (NSString *)judgeGenderWithIdNumber:(NSString *)idNumber {
NSInteger genderNumber = [[idNumber substringWithRange:NSMakeRange(16, 1)] integerValue];
if (genderNumber % 2 == 0) {
return [NSString stringWithFormat:@"性別:%@", @"女"];
} else {
return [NSString stringWithFormat:@"性別:%@", @"男"];
}
}
相關的[依賴文件]鏈接: https://pan.baidu.com/s/1gfkSXWn 密碼: kan7。直接拖入到工程中就可以了(opencv、tesseractOCR以及tessdata)。