算法采用纵向及横向扫描方式,对清晰图片进行了实验,效果较好。
算法未检测小数点,感兴趣的同学可以Y方向腐蚀检测小数点,按照X坐标排序即可。
当然,数字及字符识别,SVM、K近邻、神经网络等等这些才是正道,平常用得比较多的算法。
源图如下:
代码如下:
//DATE : 20160729
//CODE FOR LIDAN NUMBER RECG
#include "highgui.h"
#include "cv.h"
using namespace std;
using namespace cv;
void detect(IplImage * img, char * strnum, CvRect rc);
int list[10][2] = {0};//1列坐标,2列字符数值
int l = 0;//字符个数
int main()
{
IplImage * src_img = cvLoadImage("plate1.jpg");
if(!src_img)
{
printf("src_img could not be load\n");
return -1;
}
//cvShowImage("src", src_img);//src img
IplImage * gray_img = cvCreateImage(cvGetSize(src_img), src_img->depth, 1);
cvCvtColor(src_img, gray_img, CV_BGR2GRAY);
cvShowImage("gray", gray_img);//gray img
IplImage * adp_img = cvCreateImage(cvGetSize(src_img), src_img->depth, 1);
int threshold_type = CV_THRESH_BINARY_INV;
int adaptive_method = CV_ADAPTIVE_THRESH_GAUSSIAN_C; //CV_ADAPTIVE_THRESH_MEAN_C
int block_size = 7;
double offset = 5;
//cvAdaptiveThreshold(gray_img, adp_img, 255, adaptive_method, threshold_type, block_size, offset);
cvThreshold(gray_img, adp_img, 80, 255.0, CV_THRESH_BINARY);
cvShowImage("adp", adp_img);
IplImage * xtx_img = cvCreateImage(cvGetSize(adp_img), adp_img->depth, adp_img->nChannels);
cvCopy(adp_img, xtx_img);
IplConvKernel * kernal;
//自定义 1x3 的核进行 x 方向的膨胀腐蚀
kernal = cvCreateStructuringElementEx(1, 3, 0, 1, CV_SHAPE_RECT);
cvDilate(xtx_img, xtx_img, kernal, 3);
cvErode(xtx_img, xtx_img, kernal, 3); //y 腐蚀去除碎片
//自定义 3x1 的核进行 y 方向的膨胀腐蚀
kernal = cvCreateStructuringElementEx(3, 1, 1, 0, CV_SHAPE_RECT);
cvDilate(xtx_img, xtx_img, kernal, 3); //x 膨胀回复形态
cvErode(xtx_img, xtx_img, kernal, 3); //x 腐蚀去除碎片
cvShowImage("xtx", xtx_img);
IplImage * temp_img = cvCreateImage(cvGetSize(xtx_img), xtx_img->depth, adp_img->nChannels);
cvCopy(xtx_img, temp_img);
cvShowImage("temp", temp_img);
CvSeq * contours = NULL;
CvMemStorage * storage = cvCreateMemStorage(0);
int count = cvFindContours(xtx_img, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL);
printf("The count is :%d\n", count);
int idx = 0;
char szName[56] = {0};
int tempcount = 0;
CvSeq * c=contours;
for( ; c!=NULL; c=c->h_next)
{
CvRect rc = cvBoundingRect(c, 0);
double tmparea = fabs(cvContourArea(c));
if(tmparea > ((src_img->height*src_img->width)/4))
{
cvSeqRemove(c,0); //删除面积大于设定值的轮廓,1/4
continue;
}
if(tmparea < ((src_img->height*src_img->width)/100))
{
cvSeqRemove(c,0); //删除面积小于设定值的轮廓,1/20
continue;
}
//cvDrawRect(src_img, cvPoint(rc.x, rc.y), cvPoint(rc.x+rc.width, rc.y+rc.height), CV_RGB(255, 0, 0));
IplImage * sub_img = cvCreateImage(cvSize(rc.width, rc.height), xtx_img->depth, adp_img->nChannels);
cvSetImageROI(temp_img, rc);
cvCopyImage(temp_img, sub_img);
cvResetImageROI(temp_img);
sprintf(szName, "win_%d", idx++);
detect(sub_img, szName, rc);//DETECT NUMBER
cvShowImage(szName, sub_img);
cvReleaseImage(&sub_img);
}
printf("\n字符识别结果为:");
for(int i=0; i
{
for(int j=0; i+j
{
if(list[j][0] > list[j + 1][0])
{
//坐标排序
int temp = list[j][0];
list[j][0] = list[j + 1][0];
list[j + 1][0] = temp;
//数值随之变化
temp = list[j][1];
list[j][1] = list[j+1][1];
list[j+1][1] = temp;
}
}
}
for(int i=0; i
{
printf("%d ", list[i][1]);
}
cvShowImage("src", src_img);//src img
cvWaitKey(0);
return 1;
}
void detect(IplImage * img, char * strnum, CvRect rc)
{
printf("\n模块 %s\n", strnum);
//printf("高度 %d\n", img->height);
//printf("宽度 %d\n", img->width);
list[l][0] = rc.x;//第l个字符横坐标
//if the height is more two longer than width,number is 1
if((img->height/img->width)>2)
{
list[l][1] = 1;
printf("字符是 1.\n");
}
else
{
int ld[3] = {0};
CvScalar pix;
int i = img->width/2;
int j = img->height/3;
int k = img->height*2/3;
//竖向扫描
for(int m=0; m<3; m++)
{
for(int n=m*j; n
{
pix = cvGet2D(img, n, i);
if(pix.val[0]==255)
{
ld[0]++;
break;
}
}
}
printf("竖向 %d\n", ld[0]);
//横向扫描
for(int m=0; m<2; m++)
{
for(int n=m*i; n
{
//横向1/3扫描
pix = cvGet2D(img, j, n);
if(pix.val[0]==255)
{
ld[1]++;
break;
}
}
}
printf("横向1 %d\n", ld[1]);
//横向扫描
for(int m=0; m<2; m++)
{
for(int n=m*i; n
{
//横向2/3扫描
pix = cvGet2D(img, k, n);
if(pix.val[0]==255)
{
ld[2]++;
break;
}
}
}
printf("横向2 %d\n", ld[2]);
if((ld[0]==2)&&(ld[1]==2)&&(ld[2]==2))
{
printf("字符是 0\n");
list[l][1] = 0;
}
else if((ld[0]==1)&&(ld[1]==2)&&(ld[2]==1))
{
printf("字符是 4\n");
list[l][1] = 4;
}
else if((ld[0]==3)&&(ld[1]==1)&&(ld[2]==2))
{
printf("字符是 6\n");
list[l][1] = 6;
}
else if((ld[0]==1)&&(ld[1]==1)&&(ld[2]==1))
{
printf("字符是 7\n");
list[l][1] = 7;
}
else if((ld[0]==3)&&(ld[1]==2)&&(ld[2]==2))
{
printf("字符是 8\n");
list[l][1] = 8;
}
else if((ld[0]==3)&&(ld[1]==2)&&(ld[2]==1))
{
printf("字符是 9\n");
list[l][1] = 9;
}
else if((ld[0]==3)&&(ld[1]==1)&&(ld[2]==1))
{
int l1=0, l2=0;
//横向扫描
int k = img->height/3;
for(int i=0; iwidth/2; i++)
{
//横向2/3扫描
pix = cvGet2D(img, k, i);
if(pix.val[0]==255)
{
l1++;
break;
}
}
//横向扫描
k = img->height*2/3;
for(int i=0; iwidth/2; i++)
{
//横向2/3扫描
pix = cvGet2D(img, k, i);
if(pix.val[0]==255)
{
l2++;
break;
}
}
if((l1==0)&&(l2==0))
{
printf("字符是 3\n");
list[l][1] = 3;
}
else if((l1==0)&&(l2==1))
{
printf("字符是 2\n");
list[l][1] = 2;
}
else if((l1==1)&&(l2==0))
{
printf("字符是 5\n");
list[l][1] = 5;
}
}
else printf("识别失败\n");
}
l++;
}
效果如下:
1、二值化后形态学处理
2、字符识别并排序
代码中有位置排序,可以自己看看。
代码基于OpenCV1,C语言编写,实验版本。目前OpenCV2已成为主流,毕竟功能完善了很多,也省去了管理空间的麻烦,得好好学学C++。