iOS OCR(身份證號碼識別)

簡介

身份證識別,又稱OCR技術(shù)。OCR技術(shù)是光學(xué)字符識別的縮寫,是通過掃描等光學(xué)輸入方式將各種票據(jù)、報刊、書籍、文稿及其它印刷品的文字轉(zhuǎn)化為圖像信息,再利用文字識別技術(shù)將圖像信息轉(zhuǎn)化為可以使用的計算機(jī)輸入技術(shù)。

實現(xiàn)原理及步驟

  • 灰度化處理
  • 二值化
  • 腐蝕:
  • 輪廊檢測
  • 圖像文字識別,
  1. 圖片灰度化處理,就是將指定圖片每個像素點的RGB三個分量通過一定的算法計算出該像素點的灰度值,使圖像只含亮度而不含色彩信息。

  2. 二值化處理就是將經(jīng)過灰度化處理的圖片轉(zhuǎn)換為只包含黑色和白色兩種顏色的圖像,他們之間沒有其他灰度的變化。在二值圖中用255便是白色,0表示黑色

  3. 圖片的腐蝕就是將得到的二值圖中的黑色塊進(jìn)行放大。即連接圖片中相鄰黑色像素點的元素。通過腐蝕可以把身份證上的身份證號碼連接在一起形成一個矩形區(qū)域。

  4. 圖片經(jīng)過腐蝕操作后相鄰點會連接在一起形成一個大的區(qū)域,這個時候通過輪廊檢測就可以把每個大的區(qū)域找出來,這樣就可以定位到身份證上面號碼的區(qū)域。

  5. 將圖像信息轉(zhuǎn)化為可以使用的計算機(jī)輸入技術(shù)。比如下面這張包含一串?dāng)?shù)字的圖片,通過ocr識別技術(shù)可以將圖片中包含的數(shù)字信息以字符串的方式輸出。

使用到開源庫

OpenCV 和 TesseractOCRiOS

  1. OpenCV是一個開源的跨平臺計算機(jī)視覺和機(jī)器學(xué)習(xí)庫,通俗點的說,就是他給計算機(jī)提供了一雙眼睛,一雙可以從圖片中獲取信息的眼鏡,從而完成人臉識別、身份證識別、去紅眼、追蹤移動物體等等的圖像相關(guān)的功能

  2. Tesseract是目前可用的最準(zhǔn)確的開源OCR引擎,可以讀取各種格式的圖片并將他們轉(zhuǎn)換成各種語言文本。而TesseractOCRiOS則是針對iOS平臺封裝的Tesseract引擎庫。

cocopods 引入相應(yīng)框架


0E2C84FE-596A-4416-85EA-8405085C8DE6.png
FDF2E3DC-1DA7-4015-B04F-FD3A07C0D8DB.png

我再測試的時候,發(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)報如下錯誤

B9E33B0C-157C-4F00-AC7E-CBB1538D074E.png

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

9129BA5F-16F4-43C7-94AD-69C1F460A78C.png

導(dǎo)入TesseractOCRiOS需要的語言包

TesseractOCRiOS庫中沒有自帶的語言包,需要我們自己手動導(dǎo)入,我們這里直接到tesseract-ocr網(wǎng)站,tessdata即是我們需要用到的語言包。下載下來的語言包有400多兆。這里我們只需要用到英語語言包,所以就只導(dǎo)入eng.traineddata就ok,其他的都刪掉。

導(dǎo)入語言包種需要注意幾點:

  1. 語言包需要放在tessdata目錄下。TesseractOCRiOS中查找語言包是在tessdata目錄下進(jìn)行查找的,所以我們不能單獨把eng.traineddata導(dǎo)入項目中,而需要放在tessdata目錄下導(dǎo)入項目中。

  2. 將tessdata導(dǎo)入xcode項目,需要勾選Create folder refrences。上面已經(jīng)提到了語言包需要放在tessdata目錄下,所以導(dǎo)入文件到xcode的時候需要創(chuàng)建文件夾的形式,而不是創(chuàng)建組的形式

03A3D80E-B3E6-4E91-AF00-AFE2666EE404.png

代碼實現(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);
    });
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:http://www.itdecent.cn/p/ac4c4536ca3e# 一、前言??身份證識別,又稱O...
    ZhangCc_閱讀 1,623評論 1 11
  • 最近不少簡友說git上下載下來的代碼報各種問題,因為包含的庫都比較大,所以大家在pod的時候耐心等待,另外我已經(jīng)將...
    peaktan閱讀 38,470評論 158 327
  • 技術(shù)要點分析:此次項目中主要的技術(shù)劃分為身份證號碼區(qū)域提取和光學(xué)字符識別。身份證號碼區(qū)域的提取涉及有:圖像灰度化閥...
    HoFie閱讀 9,666評論 12 34
  • 身份證識別,又稱OCR技術(shù)。OCR技術(shù)是光學(xué)字符識別的縮寫,是通過掃描等光學(xué)輸入方式將各種票據(jù)、報刊、書籍、文稿及...
    SunshineAutumn閱讀 6,585評論 12 16
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,171評論 3 119

友情鏈接更多精彩內(nèi)容