Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all articles
Browse latest Browse all 15843

车牌识别系统开发记录(三) 字符识别

$
0
0

这篇博文来谈谈车牌的字符识别。

目前,车牌字符识别算法主要是基于模板匹配、特征匹配或神经网络的方法。

在本文中我们主要来说说基于神经网络的字符识别方法,采用的是OpenCV中的CvANN_MLP。关于神经网络的具体细节,可以参考我以前的博文:

BP神经网络解析及Matlab实现

更加细节的东西可以查看如下参考文献:

Neural Networks【OpenCV Documentation】

BackPropWikipedia【Wikipedia】


现在我们确定了字符识别的总体框架,那么先来说说字符的特征提取问题。这里我们主要考虑的是:

垂直方向、水平方向的数据统计特征提取法:

这种特征提取法就是自左向右对图像进行逐列的扫描,统计每列黑色像素的个数,然后,自上往下逐行扫描,统计每行的黑色像素的个数,将统计结果作为字符的特征向量,如果字符的宽度为w,高度为h,则特征向量维数是w+h。


同时,为了能够提取更多的特征用于识别,还将输入字符每个点的值引入特征向量。

Code:

Mat OCR::features(Mat in, int sizeData){
    // 直方图特征,
    Mat vhist=ProjectedHistogram(in,VERTICAL);
    Mat hhist=ProjectedHistogram(in,HORIZONTAL);
    
    // 将输入字符resize为15*15大小
    Mat lowData;
    resize(in, lowData, Size(sizeData, sizeData) );
        
    // 特征向量维数
    int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;
    
    Mat out=Mat::zeros(1,numCols,CV_32F);
    
    int j=0;
    for(int i=0; i<vhist.cols; i++)
    {
        out.at<float>(j)=vhist.at<float>(i);
        j++;
    }
    for(int i=0; i<hhist.cols; i++)
    {
        out.at<float>(j)=hhist.at<float>(i);
        j++;
    }
    for(int x=0; x<lowData.cols; x++)
    {
        for(int y=0; y<lowData.rows; y++){
            out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);
            j++;
        }
    }
    if(DEBUG)
        cout << out << "\n===========================================\n";
    return out;
}
Mat OCR::ProjectedHistogram(Mat img, int t)
{
    int sz=(t)?img.rows:img.cols;
    Mat mhist=Mat::zeros(1,sz,CV_32F);

    for(int j=0; j<sz; j++){
        Mat data=(t)?img.row(j):img.col(j);
        mhist.at<float>(j)=countNonZero(data);
    }

    //Normalize histogram
    double min, max;
    minMaxLoc(mhist, &min, &max);
    if(max>0)
        mhist.convertTo(mhist,-1 , 1.0f/max, 0);

    return mhist;
}

不过,在上面的特征提取之前,我们其实还需要做几步预处理:

首先,就是字符分割。在字符分割里面,我们首先将输入车牌二值化,然后利用findContours寻找出每个字符的轮廓,再利用boundingRect定位出每个轮廓的矩形区域,然后割取出每个字符的区域。

Code:

vector<CharSegment> OCR::segment(Plate plate){
	Mat input=plate.plateImg;
    vector<CharSegment> output;

    Mat img_threshold;
    threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);
    
	if(DEBUG)
        imshow("Threshold plate", img_threshold);
	Mat img_contours;
    img_threshold.copyTo(img_contours);

    // 在车牌区域中寻找可能字符的的轮廓
    vector< vector< Point> > contours;
    findContours(img_contours,
            contours, 
            CV_RETR_EXTERNAL, 
            CV_CHAIN_APPROX_NONE); 
    cv::Mat result;
    img_threshold.copyTo(result);
    cvtColor(result, result, CV_GRAY2RGB);
    cv::drawContours(result,contours,
            -1, 
            cv::Scalar(255,0,0), 
            1); 

    vector<vector<Point> >::iterator itc= contours.begin();
    while (itc!=contours.end()) {
        Rect mr= boundingRect(Mat(*itc));
        rectangle(result, mr, Scalar(0,255,0));

        Mat auxRoi(img_threshold, mr);
        if(verifySizes(auxRoi)){
            auxRoi=preprocessChar(auxRoi);
            output.push_back(CharSegment(auxRoi, mr));
            rectangle(result, mr, Scalar(0,125,255));
        }
        ++itc;
    }
    if(DEBUG)
        cout << "Num chars: " << output.size() << "\n";

    if(DEBUG)
        imshow("SEgmented Chars", result);

    return output;
}

效果:



然后,就是对字符进行预处理(统一大小)。




Code:

// 这个函数主要是对输入图片归一化到统一的大小20×20
Mat OCR::preprocessChar(Mat in){
    int h=in.rows;
    int w=in.cols;

    Mat transformMat=Mat::eye(2,3,CV_32F);
    int m=max(w,h);
    transformMat.at<float>(0,2)=m/2 - w/2;
    transformMat.at<float>(1,2)=m/2 - h/2;

    Mat warpImage(m,m, in.type());
    warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );

    Mat out;
    resize(warpImage, out, Size(charSize, charSize) ); 

    return out;
}





作者:Linoi 发表于2014-4-15 10:33:28 原文链接
阅读:57 评论:0 查看评论

Viewing all articles
Browse latest Browse all 15843

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>