1. 程式人生 > >【基於tesseract或ANN的神經網路的身份證號OCR識別】

【基於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;