上个帖子里面简单介绍了SVM机制,给出了一个简单例子,不过那种界面上点击鼠标取像素值进行SVM训练,然后进行识别的操作给人的感觉不是那么明显。下面我们来将一个简化版的车牌识别过程和解决思路。
首先明确几个概念和知识:
1)SVM机制,就是把一部分数据作为已知项,通过某种规则来判断未知项是什么的过程;
2)车牌识别的实质是数字和字母的识别,手动将26个字符和10个数字制作那种白底黑字的图片作为SVM训练数据,同时使用同样大小的数字和字母制作图片模拟车牌,我认为这样的过程可以让我们一定程度上了解车牌识别的原理。
3)图像中全白的像素值为255,全黑为0.
一)操作步骤
1.制作36个字符图片
记事本写入字符,设置字体大小为“72”,为了程序便于处理,我们分为三行,并分别保存到3个文件里面。
2.将36个字符分割为36个图片保存
int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割
cutleft函数从第一列像素数值之和=0的开始,到第N列结束,且满足N+1列像素数值合!=0,此时获得图像类似下图(实际是黑色背景)
void cutTop(Mat& src, Mat& dstImg)//上下切割
cutTop函数与cutleft函数类似,只是按照行操作,获取最终图像如下:
注意上图中字符与图片文件名字的关系。
3.制作两个模拟车牌图片
将上图中字符任意组合排列,得到两个图片
4.通过程序识别两个模拟车牌图片,并显示结果
cutleft和cuttop函数获取车牌的每个字符,cal_char函数计算每个字符图片的像素之和。将车牌每个字符与前面保存好的36个字符像素之和比较,获取保存字符的文件名,从而获取车牌字符是什么,组合起来就是我们所要检测的车牌。
二)代码
- #include "opencv2/opencv.hpp"
- using namespace std;
- using namespace cv;
- typedef struct _CHAR_NODE{
- char c;
- int sum;
- }CHAR_NODE;
- CHAR_NODE g_CHAR_NODE[36];
- double sumMat(Mat& inputImg)
- {
- double sum = 0.0;
- //printf("\n =2=sumMat inputImg.rows=%d, inputImg.cols=%d sum=%f\n", inputImg.rows, inputImg.cols, sum);
- threshold(inputImg, inputImg, 100, 255, CV_THRESH_BINARY);
- for (int i = 0; i < inputImg.rows;i++)
- {
- for (int j = 0; j < inputImg.cols; j++)
- {
- sum += inputImg.at <uchar>(i, j);
- }
- }
- return sum;
- }
- double getRowSum(Mat& src, int i)
- {
- double sum = 0;
- double sum1 = 0;
- int rowNumber = src.rows;
- int colNumber = src.cols * src.channels();
- #if 0
- printf("rowNumber=%d, colNumber=%d\n", rowNumber, colNumber);
- for (int i = 0; i < rowNumber;i++)
- {
- uchar* data = src.ptr<uchar>(i);
- for (int j = 0; j < colNumber; j++)
- {
- if(j > 100)
- break;
- sum = data[j] + sum;
- }
- }
- printf("\nrowNumber=%d, colNumber=%d sum=%f\n", rowNumber, colNumber, sum);
- #endif
- Mat ColClone=src.row(i).clone();
- sum = sumMat(ColClone);
- // printf("getColSum sum=%f i=%d\n", sum,i);
- return sum;
- }
- double getColSum(Mat& src, int i)
- {
- double sum = 0;
- double sum1 = 0;
- int rowNumber = src.rows;
- int colNumber = src.cols * src.channels();
- #if 0
- printf("rowNumber=%d, colNumber=%d\n", rowNumber, colNumber);
- for (int i = 0; i < rowNumber;i++)
- {
- uchar* data = src.ptr<uchar>(i);
- for (int j = 0; j < colNumber; j++)
- {
- if(j > 100)
- break;
- sum = data[j] + sum;
- printf("data[j]=%d sum=%f\t", data[j], sum);
- }
- }
- printf("\nrowNumber=%d, colNumber=%d sum=%f\n", rowNumber, colNumber, sum);
- #endif
- Mat RowClone=src.col(i).clone();
- sum = sumMat(RowClone);
- // printf("getColSum sum=%f i=%d\n", sum,i);
- return sum;
- }
- void cutTop(Mat& src, Mat& dstImg)//上下切割
- {
- int top, bottom;
- top = 0;
- bottom = src.rows;
- double colValue = 0;
- int i;
- for (i = 0; i < src.rows; i++)
- {
- colValue = getRowSum(src, i);
- if (colValue>0)
- {
- top = i;
- break;
- }
- }
- for (; i < src.rows; i++)
- {
- colValue = getRowSum(src, i);
- if (colValue == 0)
- {
- bottom = i;
- break;
- }
- }
- int height = bottom - top;
- Rect rect(0, top, src.cols, height);
- dstImg = src(rect).clone();
- }
- int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割
- {
- int left, right;
- left = 0;
- right = src.cols;
- int i = 0;
- double colValue = 0;
- for (i = 0; i < src.cols; i++)
- {
- colValue = getColSum(src, i);
- if (colValue>0)
- {
- left = i;
- break;
- }
- }
- if (left == 0)
- {
- return 1;
- }
- for (; i < src.cols; i++)
- {
- colValue = getColSum(src, i);
- if (colValue == 0)
- {
- right = i;
- break;
- }
- }
- int width = right - left;
- Rect rect(left, 0, width, src.rows);
- leftImg = src(rect).clone();
- // imshow("1=",leftImg);
- Rect rectRight(right, 0, src.cols - right, src.rows);
- rightImg = src(rectRight).clone();
- cutTop(leftImg, leftImg);
- return 0;
- }
- int make_svm(char* filename, int char_num)
- {
- Mat imgSrc1 = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
- Mat imgLeft;
- Mat imgRight;
- imshow("origin", imgSrc1);
- threshold(imgSrc1, imgSrc1, 100 , 255, CV_THRESH_BINARY_INV);
- int nRet = cutLeft(imgSrc1, imgLeft, imgRight);
- int i = 0;
- while (nRet == 0)
- {
- char nameLeft[256];
- sprintf(nameLeft, "./char_number/left_%c.png", char_num + i);
- i++;
- printf("***nameLeft=%s\n", nameLeft);
- //imshow(nameLeft,imgLeft);
- imwrite(nameLeft, imgLeft);
- //imwrite(nameRight, imgRight);
- Mat srcTmp = imgRight;
- nRet = cutLeft(srcTmp, imgLeft, imgRight);
- }
- return 0;
- }
- void callmake_svm()
- {
- int a[3] = {'A', 'N', '0'};
- char str[64] = "chars_numbers3";
- for(int i = 0; i < 3; i ++)
- {
- char str_name[128] = "chars_numbers3";
- sprintf(str_name, "%s%d.png", str, i+1);
- make_svm(str_name, a[i]);
- }
- }
- char findout_char(Mat &charImg)
- {
- char cc = ' ';
- int i = 0;
- int j = 0;
- double fcharsum = 0;
- threshold(charImg, charImg, 100 , 255, CV_THRESH_BINARY_INV);
- fcharsum = sumMat(charImg);
- // printf("=1=get_findout_char charImg.rows=%d, charImg.cols=%d charsum=%d\n", charImg.rows, charImg.cols, charsum);
- for(i=0; i < 26; i++)
- {
- if(fcharsum == g_CHAR_NODE[i + 0].sum)
- return g_CHAR_NODE[i + 0].c;
- }
- for(j=0; j < 10; j++)
- {
- if(fcharsum == g_CHAR_NODE[i + j].sum)
- return g_CHAR_NODE[i + j].c;
- }
- printf("findout_char error\n");
- return cc;
- }
- int get_findout_char(char* filename, int char_num)
- {
- char str_result[12];
- Mat imgSrc1 = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
- Mat imgLeft;
- Mat imgRight;
- imshow("origin", imgSrc1);
- threshold(imgSrc1, imgSrc1, 100 , 255, CV_THRESH_BINARY_INV);
- imshow("threshold", imgSrc1);
- int nRet = cutLeft(imgSrc1, imgLeft, imgRight);
- int i = 0;
- printf("get_findout_char nRet=%d i=%d\n", nRet,i);
- while (nRet == 0)
- {
- char nameLeft[256];
- sprintf(nameLeft, "./char_number/left_%d.png", i);
- //printf("nameLeft=%s\n", nameLeft);
- //imshow("get_findout_char",imgLeft);
- //imwrite(nameLeft, imgLeft);
- //imwrite(nameRight, imgRight);
- str_result[i] = findout_char(imgLeft);
- //printf("nameLeft=%d <0>=%d\n", str_result[i], '0');
- i++;
- Mat srcTmp = imgRight;
- nRet = cutLeft(srcTmp, imgLeft, imgRight);
- }
- printf("get_findout_char str_result=%s\n", str_result);
- return 0;
- }
- double cal_char(char* filename)
- {
- double fsum = 0;
- Mat imgSrc1 = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
- Mat imgLeft;
- Mat imgRight;
- int i = 0;
- imshow("origin", imgSrc1);
- threshold(imgSrc1, imgSrc1, 100 , 255, CV_THRESH_BINARY_INV);
- // printf("=0=cal_cha src.cols=%d src.rows=%d fsum=%f\n", imgSrc1.cols, imgSrc1.rows, fsum); //
- fsum = sumMat(imgSrc1);
- return fsum;
- }
- int main(int argc, char** argv)
- {
- if(argc < 2)
- {
- printf("argc < 2\n");
- return -1;
- }
- printf("argv[1]=%s\n", argv[1]);
- if(strcmp(argv[1], "-svm") == 0)
- {
- callmake_svm();
- printf("-svm\n");
- }
- if(strcmp(argv[1], "-findout") == 0)
- {
- int i = 0;
- int j = 0;
- char filename1[128] = "./char_number/left_";//0.png
- char filename2[128] = "./char_number/left_";//0.png
- for(i=0; i < 26; i++)
- {
- sprintf(filename2, "%s%c.png", filename1, 'A' + i);
- g_CHAR_NODE[i].c = 'A' + i;
- g_CHAR_NODE[i].sum = cal_char(filename2);
- }
- for(j=0; j < 10; j++)
- {
- sprintf(filename2, "%s%d.png", filename1, j);
- g_CHAR_NODE[i + j].c = '0' + j;
- g_CHAR_NODE[i + j].sum = cal_char(filename2);
- }
- #if 0
- for(i=0; i < 26; i++)
- {
- printf("=2=g_CHAR_NODE[i + 0].c=%c, g_CHAR_NODE[i + 0].sum=%d\n", g_CHAR_NODE[i + 0].c, g_CHAR_NODE[i + 0].sum);
- }
- for(j=0; j < 10; j++)
- {
- printf("=2=g_CHAR_NODE[i + j].c=%d, g_CHAR_NODE[i + j].sum=%d\n", g_CHAR_NODE[i + j].c, g_CHAR_NODE[i + j].sum);
- }
- #endif
- get_findout_char(argv[2], 0);
- printf("-svm\n");
- }
- waitKey(0);
- return 0;
- }
复制代码
三)运行结果
四)总结
1)实际的车牌识别的过程比我们这个复杂很多,比如拍摄角度,图片的清晰度等都需要进行处理,而且处理的结果直接影响最终的识别正确性。
2)为了减少代码的逻辑,本程序开始识别以前直接将所要的图片信息保存到一个全局结构体中,实际中可以使用Opencv中操作xml文件的方法,将所有的保存文件信息保存到xml文件中,这也是后面实际的车牌识别使用的方式。
3)注意像素操作前应该将图像进行threshold操作。
4)下面会根据时间来完成一个外接设备的驱动,或者调用第三方库来完成Android系统上的车牌识别的项目结项。
Working