【基於tesseract或ANN的神經網路的身份證號OCR識別】
之前寫了一篇,結果瀏覽器崩了,文字全無。這次直接上程式碼吧。
身份證號的識別過程:
#include<iostream> #include<opencv2\opencv.hpp> using namespace std; using namespace cv; int main() { Mat img = imread("234.png"); namedWindow("原圖"); imshow("原圖", img); Mat temp, temp2, temp3; //灰度化 cvtColor(img, temp, COLOR_BGR2GRAY); cvtColor(temp, img, COLOR_GRAY2BGRA); namedWindow("灰度化"); imshow("灰度化", temp); //二值化 threshold(temp, temp2, 60, 255, CV_THRESH_BINARY); namedWindow("二值化"); imshow("二值化", temp2); //腐蝕 Mat erodeElement = getStructuringElement(MORPH_RECT, Size(13, 13)); erode(temp2, temp3, erodeElement); namedWindow("腐蝕"); imshow("腐蝕", temp3); //輪廓檢測 vector<vector<Point> > contours; //定義一個容器來儲存所有檢測到的輪廊 vector<Vec4i> hierarchy; //輪廓檢測函式 Mat conv(temp3.size(), CV_8UC1); temp3.convertTo(conv, CV_8UC1); findContours(conv, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, cvPoint(0, 0)); int index = 0; long max_Index = static_cast<long>(contours.size()); if (index < max_Index - 1) { ++index; } //取出身份證號碼區域 vector<Rect> rects; Rect numberRect = Rect(0, 0, 0, 0); vector<vector<Point> >::const_iterator itContours = contours.begin(); for (int i = 0; i <max_Index; ++i) { Rect rect = boundingRect(itContours[i]); numberRect = rect; Mat dst(numberRect.height, numberRect.width, CV_8UC4, numberRect.area()); dst = img(numberRect); for (int i = 0; i <max_Index; ++i) { Rect rect = boundingRect(itContours[i]); numberRect = rect; Mat dst(numberRect.height, numberRect.width, CV_8UC4, numberRect.area()); dst = img(numberRect); if (rect.y > img.rows / 2 && rect.width / rect.height > 6) { imshow("身份證號:", dst); } } } waitKey(0); cout << CV_MAJOR_VERSION << endl; return 0; }
執行結果:
第二種方法是根據識別出來的身份證號矩形圖,根據垂直投影進行字元分割,然後送入到訓練好的資料集中進行識別。
首先,定義一個數組用來儲存每一列畫素中白色畫素的個數。
int perPixelValue;//每個畫素的值
int* projectValArry = new int[width];//建立一個用於儲存每列白色畫素個數的陣列
memset(projectValArry, 0, width*4);//必須初始化陣列
然後,遍歷二值化後的圖片,將每一列中白色的(也就是數字區域)畫素記錄在陣列中。
//遍歷每一列的影象灰度值,查詢每一行255的值
for (int col = 0; col < width; ++col)
{
for (int row = 0; row < height; ++row)
{
perPixelValue = binImg.at<uchar>(row, col);
if (perPixelValue == 255)//如果是黑底白字
{
projectValArry[col]++;
}
}
}
最後,根據數組裡的灰度值畫出投影圖
/*新建一個Mat用於儲存投影直方圖並將背景置為白色*/
Mat verticalProjectionMat(height, width, CV_8UC1);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
perPixelValue = 255; //背景設定為白色。
verticalProjectionMat.at<uchar>(i, j) = perPixelValue;
}
}
/*將直方圖的曲線設為黑色*/
for (int i = 0; i < width; i++)
{
for (int j = 0; j < projectValArry[i]; j++)
{
perPixelValue = 0; //直方圖設定為黑色
verticalProjectionMat.at<uchar>(height - 1 - j, i) = perPixelValue;
}
}
imshow("【投影】",verticalProjectionMat);
delete[] projectValArry;//不要忘了刪除陣列空間
有了投影圖做切割就很容易了,其實最主要的就是那個儲存灰度值的陣列,下面就需要根據這個陣列的內容來找到相鄰字元間的分割點。
vector<Mat> roiList;//用於儲存分割出來的每個字元
int startIndex = 0;//記錄進入字元區的索引
int endIndex = 0;//記錄進入空白區域的索引
bool inBlock = false;//是否遍歷到了字元區內
for (int i = 0; i < srcImg.cols; ++i)
{
if (!inBlock && projectValArry[i] != 0)//進入字元區了
{
inBlock = true;
startIndex = i;
cout << "startIndex is " << startIndex << endl;
}
else if (projectValArry[i] == 0 && inBlock)//進入空白區了
{
endIndex = i;
inBlock = false;
Mat roiImg = srcImg(Range(0,srcImg.rows),Range(startIndex,endIndex+1));
roiList.push_back(roiImg);
}
}
另外對於tesseract的識別,可以參考如下:
#include <baseapi.h>
#include <renderer.h>
#include <opencv2\highgui.hpp>
#include <opencv2\imgproc.hpp>
#include <iostream>
void main() {
cv::Mat image = cv::imread("身份證號.jpg");
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::imshow("原灰度圖", image);
// eng.traineddata所在路徑
//const char* datapath = "D:\\tesseract\\tessdata";
// 新建tess基類
tesseract::TessBaseAPI tess;
// 初始化
tess.Init(NULL, "eng", tesseract::OEM_DEFAULT);
// 設定白名單
tess.SetVariable("tessedit_char_whitelist", "0123456789");
// 設定識別模式
tess.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
// 設定識別影象
tess.SetImage((uchar*)image.data, image.cols,
image.rows, image.step[1], image.step[0]);
// 進行識別
char* out = tess.GetUTF8Text();
std::cout << out << std::endl;
cv::waitKey();
}
#include <tesseract/baseapi.h>
tesseract::TessBaseAPI tessearct_api;
const char *languagePath = "/usr/local/Cellar/tesseract/3.04.01_2/share/tessdata"; const char *languageType = "chi_sim";
int nRet = tessearct_api.Init(languagePath, languageType,tesseract::OEM_DEFAULT);
if (nRet != 0) {
printf("初始化字型檔失敗!");
return -1;
}
tessearct_api.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
tessearct_api.SetImage(seg_image.data, seg_image.cols, seg_image.rows, 1,seg_image.cols);
string out = string(tessearct_api.GetUTF8Text());
cout<<"the out result :"<<out<<endl;