zxing和opencv實現身份識別
**轉自: http://blog.csdn.net/sinat_28891771/article/details/71191777?locationNum=2&fps=1**
*實現原理分析 :通過zxing庫捕捉相機獲得影象,或者從相簿裡獲取圖片,再對影象進行處理. 對影象處理 : 對源影象進行畫素放大縮小處理>預處理(影象灰度化,低通濾波處理,邊緣檢測,二值化,中值平滑處理,閉運算)>刷選身份證號的矩形,得到有效行>對有效行進行灰度化,二值化>然後就進行識別.
實現過程:
1. 環境的配置
a. opencv3.2的依賴: 去官網下載opencv for android的sdk,解壓得到
在android studio中選擇improt module載入進來 將依賴的opencv的build.gradle裡的版本要求和主工程的build.gradle保持一致
最後將sdk目錄中的native的libs裡的檔案複製到主工程的main裡的jniLibs目錄下,jniLibs目錄需自己建立.這樣opencv庫就裝載成功了!
b. tesseract庫的使用,本文章不對tesseract如何編譯做詳細介紹,可以使用tess-two,有編譯好的,解壓的後,把Jar檔案新增到專案,把libs目錄的檔案複製到jniLibs目錄下這樣tess-two就整合完了.
c.
d. zxing庫的引用(本文不做介紹)
2 程式碼實現
如上圖主介面為三個入口,根據的scan_type的型別來呼叫zxing庫的掃描型別
在Zxing庫的CaptureActivity類做以下新增:
//OpenCV庫載入並初始化成功後的回撥函式 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { // TODO Auto-generated method stub switch (status) { case BaseLoaderCallback.SUCCESS: Log.i(TAG, "成功載入"); break; default: super.onManagerConnected(status); Log.i(TAG, "載入失敗"); break; } } };
在onResume的方法裡新增
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
這個opencv庫初始化載入回撥和onResume方法裡新增的判斷是一定要新增的.
其它細節不做詳細介紹,後面會將原始碼釋出!
在zxing庫的DecodeHandler類裡同二維碼識別一樣,將相機捕捉的影象進行解析
if (scan_type.equals(CaptureActivity.SCAN_TYPE_QRCODESCAN)) {
rawResult = multiFormatReader.decodeWithState(bitmap);//解析二維碼圖片
} else if (scan_type.equals(CaptureActivity.SCAN_TYPE_BANK_CARD)) {
result = BankCardIdentify.bankCardIdentify(activity, toBitmap(source, source.renderCroppedGreyscaleBitmap()));//解析銀行卡
} else {
result = IdCardIdentify.idCardIdentify(activity, toBitmap(source, source.renderCroppedGreyscaleBitmap())); //解析身份證
}
接下來就是IdCardIdentify類和CoreUtil類的介紹
IdCardIdentify類中的idCardIdentify方法,引數有activity和相機捕捉的bitmap.
public static String idCardIdentify(Activity activity, Bitmap bitmap) {
CoreUtil.copyToSD(activity);
bitmap = CoreUtil.scaleImage(bitmap, 900, 450); //根據畫素放大縮小圖片
bitmap = ICPretreatment.doICPretreatmentOne(bitmap);//影象預處理
CoreUtil.saveBitmap(bitmap);
return getResult(ICPretreatment.doICPretreatmentTwo(bitmap)); //返回有效行識別結果
}
先載入語言包檔案,將assets目錄下的語言包檔案儲存到sd目錄下,再對源影象進行比例方法縮小,然後將影象預處理,即找到號碼的位置,最後將號碼的位置進行識別
影象的預處理:
public static Bitmap doICPretreatmentOne(Bitmap bitmap) {
Mat rgbMat = new Mat(); //原圖
Mat grayMat = new Mat(); //灰度圖
Mat binaryMat = new Mat(); //二值化圖
Mat canny = new Mat();
Utils.bitmapToMat(bitmap, rgbMat);
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//灰度化
Imgproc.blur(grayMat, canny, new Size(3, 3));//低通濾波處理
Imgproc.Canny(grayMat, canny, 125, 225);//邊緣檢測處理類
Imgproc.threshold(canny, binaryMat, 165, 255, Imgproc.THRESH_BINARY);//二值化
Imgproc.medianBlur(binaryMat, binaryMat, 3);//中值平滑處理
Mat element_9 = new Mat(20, 20, CV_8U, new Scalar(1));
Imgproc.morphologyEx(binaryMat, element_9, MORPH_CROSS, element_9);//閉運算
/**
* 輪廓提取()
*/
ArrayList<MatOfPoint> contoursList = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(element_9, contoursList, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
Mat resultImage = Mat.zeros(element_9.size(), CV_8U);
Imgproc.drawContours(resultImage, contoursList, -1, new Scalar(255, 0, 255));
Mat effective = new Mat(); //身份證位置
//外包矩形區域
for (int i = 0; i < contoursList.size(); i++) {
Rect rect = Imgproc.boundingRect(contoursList.get(i));
if (rect.width != rect.height && rect.width / rect.height > 8) { //初步判斷找到有效位置
Imgproc.rectangle(resultImage, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 255), 1);
effective = new Mat(rgbMat, rect);
}
}
if (effective != null && effective.cols() > 0 && effective.rows() > 0) {
bitmap = Bitmap.createBitmap(effective.cols(), effective.rows(), Bitmap.Config.RGB_565);
Utils.matToBitmap(effective, bitmap);
} else {
bitmap = CoreUtil.cropBitmap(bitmap, 280, 360, 600, 70, true);
}
return bitmap;
}
將影象進行形態學相關的處理,最後刷選連通域的矩形來確定號碼的位置,如果沒有找到的話,就根據身份證的位置特徵進行切割
影象預處理第二步
public static Bitmap doICPretreatmentTwo(Bitmap bitmap) {
Mat rgbMat = new Mat(); //原圖
Mat grayMat = new Mat(); //灰度圖
Mat binaryMat = new Mat(); //二值化圖
Utils.bitmapToMat(bitmap, rgbMat);
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//灰度化
Imgproc.threshold(grayMat, binaryMat, 150, 255, Imgproc.THRESH_BINARY);//二值化
bitmap = Bitmap.createBitmap(binaryMat.cols(), binaryMat.rows(), Bitmap.Config.RGB_565);
Utils.matToBitmap(binaryMat, bitmap);
return bitmap;
}
最後一步識別
/**
* 對要識別的影象進行識別
*
* @param bitmap 要識別的bitmap
* @return
*/
public static String getResult(Bitmap bitmap) {
String result;
TessBaseAPI baseApi = new TessBaseAPI();
baseApi.setDebug(true);
baseApi.init(DATAPATH, “identify”);
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
baseApi.setImage(bitmap);
baseApi.setVariable(“tessedit_char_whitelist”, “0123456789X”);
result = baseApi.getUTF8Text();
result = result.replaceAll(“\s*”, “”);
if (result.equals(“”) || result.length() <= 16 || result.length() >= 20) { //允許4個字元的誤差
result = null;
}
baseApi.end();
bitmap.recycle();
return result;
}
效果展示:
所使用身份證素材均來自百度搜索
**轉自: http://blog.csdn.net/sinat_28891771/article/details/71191777?locationNum=2&fps=1**
*實現原理分析 :通過zxing庫捕捉相機獲得影象,或者從相簿裡獲取圖片,再對影象進行處理. 對影象處理 : 對源影象進行畫素放大縮小處理>預處理(影象灰度化,低通濾波處理,邊緣檢測,二值化,中值平滑處理,閉運算)>刷選身份證號的矩形,得到有效行>對有效行進行灰度化,二值化>然後就進行識別.
實現過程:
1. 環境的配置
a. opencv3.2的依賴: 去官網下載opencv for android的sdk,解壓得到
在android studio中選擇improt module載入進來 將依賴的opencv的build.gradle裡的版本要求和主工程的build.gradle保持一致
最後將sdk目錄中的native的libs裡的檔案複製到主工程的main裡的jniLibs目錄下,jniLibs目錄需自己建立.這樣opencv庫就裝載成功了!
b. tesseract庫的使用,本文章不對tesseract如何編譯做詳細介紹,可以使用tess-two,有編譯好的,解壓的後,把Jar檔案新增到專案,把libs目錄的檔案複製到jniLibs目錄下這樣tess-two就整合完了.
c. 語言包的放置,可以從tesseract-ocr的官網下載中文的或者英文的,但是針對只是身份證號的識別,打算自己訓練,官方下載的語言包檔案都過大,本篇文章不對如何訓練做詳細介紹.
d. zxing庫的引用(本文不做介紹)
2 程式碼實現
如上圖主介面為三個入口,根據的scan_type的型別來呼叫zxing庫的掃描型別
在Zxing庫的CaptureActivity類做以下新增:
//OpenCV庫載入並初始化成功後的回撥函式
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
// TODO Auto-generated method stub
switch (status) {
case BaseLoaderCallback.SUCCESS:
Log.i(TAG, "成功載入");
break;
default:
super.onManagerConnected(status);
Log.i(TAG, "載入失敗");
break;
}
}
};
在onResume的方法裡新增
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
這個opencv庫初始化載入回撥和onResume方法裡新增的判斷是一定要新增的.
其它細節不做詳細介紹,後面會將原始碼釋出!
在zxing庫的DecodeHandler類裡同二維碼識別一樣,將相機捕捉的影象進行解析
if (scan_type.equals(CaptureActivity.SCAN_TYPE_QRCODESCAN)) {
rawResult = multiFormatReader.decodeWithState(bitmap);//解析二維碼圖片
} else if (scan_type.equals(CaptureActivity.SCAN_TYPE_BANK_CARD)) {
result = BankCardIdentify.bankCardIdentify(activity, toBitmap(source, source.renderCroppedGreyscaleBitmap()));//解析銀行卡
} else {
result = IdCardIdentify.idCardIdentify(activity, toBitmap(source, source.renderCroppedGreyscaleBitmap())); //解析身份證
}
接下來就是IdCardIdentify類和CoreUtil類的介紹
IdCardIdentify類中的idCardIdentify方法,引數有activity和相機捕捉的bitmap.
public static String idCardIdentify(Activity activity, Bitmap bitmap) {
CoreUtil.copyToSD(activity);
bitmap = CoreUtil.scaleImage(bitmap, 900, 450); //根據畫素放大縮小圖片
bitmap = ICPretreatment.doICPretreatmentOne(bitmap);//影象預處理
CoreUtil.saveBitmap(bitmap);
return getResult(ICPretreatment.doICPretreatmentTwo(bitmap)); //返回有效行識別結果
}
先載入語言包檔案,將assets目錄下的語言包檔案儲存到sd目錄下,再對源影象進行比例方法縮小,然後將影象預處理,即找到號碼的位置,最後將號碼的位置進行識別
影象的預處理:
public static Bitmap doICPretreatmentOne(Bitmap bitmap) {
Mat rgbMat = new Mat(); //原圖
Mat grayMat = new Mat(); //灰度圖
Mat binaryMat = new Mat(); //二值化圖
Mat canny = new Mat();
Utils.bitmapToMat(bitmap, rgbMat);
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//灰度化
Imgproc.blur(grayMat, canny, new Size(3, 3));//低通濾波處理
Imgproc.Canny(grayMat, canny, 125, 225);//邊緣檢測處理類
Imgproc.threshold(canny, binaryMat, 165, 255, Imgproc.THRESH_BINARY);//二值化
Imgproc.medianBlur(binaryMat, binaryMat, 3);//中值平滑處理
Mat element_9 = new Mat(20, 20, CV_8U, new Scalar(1));
Imgproc.morphologyEx(binaryMat, element_9, MORPH_CROSS, element_9);//閉運算
/**
* 輪廓提取()
*/
ArrayList<MatOfPoint> contoursList = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(element_9, contoursList, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
Mat resultImage = Mat.zeros(element_9.size(), CV_8U);
Imgproc.drawContours(resultImage, contoursList, -1, new Scalar(255, 0, 255));
Mat effective = new Mat(); //身份證位置
//外包矩形區域
for (int i = 0; i < contoursList.size(); i++) {
Rect rect = Imgproc.boundingRect(contoursList.get(i));
if (rect.width != rect.height && rect.width / rect.height > 8) { //初步判斷找到有效位置
Imgproc.rectangle(resultImage, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 255), 1);
effective = new Mat(rgbMat, rect);
}
}
if (effective != null && effective.cols() > 0 && effective.rows() > 0) {
bitmap = Bitmap.createBitmap(effective.cols(), effective.rows(), Bitmap.Config.RGB_565);
Utils.matToBitmap(effective, bitmap);
} else {
bitmap = CoreUtil.cropBitmap(bitmap, 280, 360, 600, 70, true);
}
return bitmap;
}
將影象進行形態學相關的處理,最後刷選連通域的矩形來確定號碼的位置,如果沒有找到的話,就根據身份證的位置特徵進行切割
影象預處理第二步
public static Bitmap doICPretreatmentTwo(Bitmap bitmap) {
Mat rgbMat = new Mat(); //原圖
Mat grayMat = new Mat(); //灰度圖
Mat binaryMat = new Mat(); //二值化圖
Utils.bitmapToMat(bitmap, rgbMat);
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//灰度化
Imgproc.threshold(grayMat, binaryMat, 150, 255, Imgproc.THRESH_BINARY);//二值化
bitmap = Bitmap.createBitmap(binaryMat.cols(), binaryMat.rows(), Bitmap.Config.RGB_565);
Utils.matToBitmap(binaryMat, bitmap);
return bitmap;
}
最後一步識別
/**
* 對要識別的影象進行識別
*
* @param bitmap 要識別的bitmap
* @return
*/
public static String getResult(Bitmap bitmap) {
String result;
TessBaseAPI baseApi = new TessBaseAPI();
baseApi.setDebug(true);
baseApi.init(DATAPATH, “identify”);
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
baseApi.setImage(bitmap);
baseApi.setVariable(“tessedit_char_whitelist”, “0123456789X”);
result = baseApi.getUTF8Text();
result = result.replaceAll(“\s*”, “”);
if (result.equals(“”) || result.length() <= 16 || result.length() >= 20) { //允許4個字元的誤差
result = null;
}
baseApi.end();
bitmap.recycle();
return result;
}
效果展示:
所使用身份證素材均來自百度搜索