车牌字符的切割(一)



      摘要:据了解,我国的标准车牌有统一的大小,即宽度为45cm,高度为15cm。车牌上单个字符宽为45mm,其中,第一个字符的空间包括左右空出的空间为45mm,第二个字符与第三个字符之间的间隙为34mm,其他字符间隙为12mm。字符总数为7个,且单个字符宽度约占车牌的10%。有了这些信息,就得到了车牌字符的切割算法公式。

 
 
 

#include <cv.h> #include <cxcore.h> #include <highgui.h> #include <iostream> using namespace std; #define T 27                            //判断一行是不是车牌有效信息的阈值 #define T1                             //判断一列是不是车牌有效信息的阈值 #define S(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)] //S(image,x,y)指该图像(x,y)像素点的像素值,[(x)]是数组,类似于a[i] IplImage *pImgSrc;                       //源图像 IplImage *pImgSrc1 = NULL;        //灰度图 IplImage *pImgSrc2 = NULL;        //高斯滤波后的图 IplImage *pImgSrc3 = NULL;        //边缘检测后的图  IplImage *pImgSrc4 = NULL;        //感兴趣的图片 IplImage *pImgSrc5 = NULL;        //归一化的灰度图 int i, j; int r_start, r_end;               //用来记录车牌开始,结束行 int c_start, c_end;              //用来记录车牌开始,结束列 int row[120];                     //row[]存放含有有效车牌信息的第j行,把所有有效行放一个数组里面 int col[340];                      //存放每个字符双边界(列)的位置 int k = 0;                           //含有有效车牌信息的行数 int CWidth = 45;               //每个字符的宽度(我国的标准车牌有统一的大小,即宽度为45cm,高度为15cm。车牌上单个字符宽45mm) int Space = 12;                  //字符之间的间隙(第2字符与第3字符之间的间隙为34mm,其他字符间隙为12mm。) int reWidth = 410;  //410是感兴趣图像的宽度 int reHeight = 85; //85是感兴趣的高度
 
 void UpAndDown(IplImage *src_son_row);//定义一个子函数,找到图片中含有车牌有效信息的上下边界 
 
 
 void LeftAndRight(IplImage *src_son_col);  //有效信息的左边界和右边界 
 

void find_ROI(IplImage *before_ROI, IplImage *after_ROI);//找到图片中只含有目标区域的部分

void cut_and_drawLine(IplImage *befour_cut_image);//对含有车牌有效信息的图片分割字符,并画出分割线;
 
 void showChar(IplImage *showChar);//显示所分割出来的字符

void main()
{
pImgSrc = cvLoadImage("004.bmp", -1);
pImgSrc2 = cvCreateImage(cvGetSize(pImgSrc), IPL_DEPTH_8U, 1);//创建一副8位无符型单通道的图片,大小和pImgSrc相同
pImgSrc1 = cvCreateImage(cvGetSize(pImgSrc), IPL_DEPTH_8U, 1);//同上
pImgSrc3 = cvCreateImage(cvGetSize(pImgSrc), IPL_DEPTH_8U, 1);//同上
cvCvtColor(pImgSrc, pImgSrc1, CV_RGB2GRAY);                             // 将彩图转化为灰度图,储存在pImgSrc1中;
cvSmooth(pImgSrc1, pImgSrc2, CV_GAUSSIAN, 3, 0, 0);                 //高斯滤波
cvCanny(pImgSrc2, pImgSrc3, 100, 200, 3);                                     //二值化
cvDilate(pImgSrc3, pImgSrc3, 0, 1);
cvErode(pImgSrc3, pImgSrc3, 0, 1);
cvNamedWindow("cvcanny", 1);
cvShowImage("cvcanny", pImgSrc3);

r_start = 0;
r_end = 0;
c_start = 0;
c_end = 0;
cout << "像素的行数为:" << pImgSrc3->height << endl;
cout << "像素的列数为:" << pImgSrc3->width << endl;

UpAndDown(pImgSrc3);                     //找到图片中含有车牌有效信息的上边界和下边界
LeftAndRight(pImgSrc3);                    //找到图片中含有车牌有效信息的最左边界和右边界

find_ROI(pImgSrc2, pImgSrc4);        //找到图片中只含有目标区域的部分

pImgSrc5 = cvCreateImage(cvSize(reWidth, reHeight), IPL_DEPTH_8U, 1);
cvResize(pImgSrc4, pImgSrc5, CV_INTER_LINEAR);               //线性插值
cvNamedWindow("感兴趣图像的宽度与高度", 1);
cvShowImage("感兴趣图像的宽度与高度", pImgSrc5);

cut_and_drawLine(pImgSrc5);                 //对含有车牌有效信息的图片分割出字符,并画出分割线
showChar(pImgSrc5);                            //显示出所有分割出来的字符
cvReleaseImage(&pImgSrc5);
}
void UpAndDown(IplImage *src_son_row) {

//判断每行是不是含有车牌信息的行是通过查看黑点白点变化的次数来确定的

for (j = 0; j < src_son_row->height; j++)
//遍历整幅图的行和列,寻找包含车牌信息的行数
{
int count = 0;                                       //  count/2  记录每行白点(水平的白线看做一个点)的个数
for (i = 0; i < src_son_row->width; i++)
{
if (S(src_son_row, i, j) != S(src_son_row, i + 1, j))  //统计行跳数
count++;
if (count > T)                              //把含有车牌有效信息的行j存放到row[k]
{
row[k] = j;
k++;                                  //记录含有有效车牌信息的行数
break;
}
}
}
cout << "有效行k值为:" << k << endl;
for (i = 0; i<k; i++)                                 //从上边开始,3行连续时认为是起始行
{
if ((row[i] == row[i + 1] - 1) && (row[i + 1] == row[i + 2] - 1)) {
r_start = row[i];
// cout<<"the start row123:"<<row_start<<endl;
break;
}
}
cout << "the start row:" << r_start << endl;
cvLine(pImgSrc1, cvPoint(0, r_start), cvPoint(pImgSrc->width, r_start), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("上画线", 1);

cvShowImage("上画线", pImgSrc1);


for (i = k - 1; i > r_start; i--)     //从下边开始,3行连续时认为是起始行
{
if ((row[i] == row[i - 1] + 1) && (row[i - 1] == row[i - 2] + 1)) {
r_end = row[i];
break;
}
}
cout << "the end row:" << r_end << endl;
cvLine(pImgSrc1, cvPoint(0, r_end), cvPoint(pImgSrc->width, r_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("上下画线", 1);
cvShowImage("上下画线", pImgSrc1);
}

void LeftAndRight(IplImage *src_son_col) {
//判断每列是不是含有车牌有效信息是查看每列中含有白点像素的个数
bool flag = false;
for (i = 5; i<src_son_col->width; i++)    
     //找到左列开始,i的大小可以根据运行效果来更改
{
int count = 0;
for (j = r_start; j<r_end; j++)
{
if (S(src_son_col, i, j) == 255)
count++;
if (count>T1)
{
c_start = i;
flag = true;
break;
}
}
if (flag) break;
}
cout << "the start col:" << c_start << endl;
cvLine(pImgSrc1, cvPoint(c_start, r_start), cvPoint(c_start, r_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("左画线", 1);
cvShowImage("左画线", pImgSrc1);

flag = false;
for (i = src_son_col->width - 10; i>c_start; i--)          
//找到右列开始
{
int count = 0;
for (j = r_start; j<r_end; j++)
{
if (S(src_son_col, i, j) == 255)
count++;
if (count>T1)
{
c_end = i;
flag = true;
break;
}
}
if (flag) break;
}
cout << "the end col:" << c_end << endl;
cvLine(pImgSrc1, cvPoint(c_end, r_start), cvPoint(c_end, r_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("左右画线", 1);
cvShowImage("左右画线", pImgSrc1);
}

void find_ROI(IplImage *before_ROI, IplImage *after_ROI) {
CvRect ROI_rect;                
//获得图片感兴趣区域
ROI_rect.x = c_start;
//用来记录车牌开始的列
ROI_rect.y = r_start;
//用来记录车牌开始的行
ROI_rect.width = c_end - c_start;
ROI_rect.height = r_end - r_start;
//设置ROI区域
cvSetImageROI(pImgSrc1, ROI_rect);
pImgSrc4 = cvCreateImage(cvSize(ROI_rect.width, ROI_rect.height), IPL_DEPTH_8U, 1);
cvCopy(pImgSrc1, pImgSrc4);
//ROI区域拷贝
cvResetImageROI(pImgSrc1);
//释放ROI区域
}

void cut_and_drawLine(IplImage *befour_cut_image) {
//得到每个字符的双边界
//只看水平方向

for (i = 0; i<7; i++)
{
switch (i) {
case 0:                                          
//第一个字符名占45个像素
case 1:                                          
col[i * 2] = i*CWidth + i*Space;
cout << col[i * 2] << endl;
col[i * 2 + 1] = (i + 1)*CWidth + i*Space;
cout << col[i * 2 + 1] << endl;
break;                                      
//第二个与第三个字符
之间有个点宽度是34个像素
 
 
		case 2:
			//2~6是剩余的字母和数字各自占45个像素
		case 3:
		case 4:
		case 5:
		case 6:
			col[i * 2] = i*CWidth + i*Space + 27;
			cout << col[i * 2] << endl;
			col[i * 2 + 1] = (i + 1)*CWidth + i*Space + 27;
			cout << col[i * 2 + 1] << endl;
			break;
		}
	}
	for (i = 0; i<15; i++)        //画出每个字符的区域
	{
		cvLine(befour_cut_image, cvPoint(col[i], 0), cvPoint(col[i], reHeight), cvScalar(255, 0, 0), 1, 8, 0);
	}
}
void showChar(IplImage *showChar) {
	cvNamedWindow("分割后的车牌", 1);//定义一个“分割后的车牌”窗口,用于显示图像
	cvShowImage("分割后的车牌", showChar);//在“分割后的车牌”窗口中显示图像
	cvWaitKey(0);
}

运行结果如下图:
 
 
      由运行结果可知,在水平方向上各字符点分别为(0,45)(57,102) (141,186)  (198,243)  (255,300)  (312,357) (369,414)
         
      但是当输入另一张车牌号图片时,运行结果如下图所示,由此可见,这个程序代码并不能达到将倾斜的字符完整切割的效果。
 
          因此,下一次的实验需要添加一个倾斜校正的程序代码,达到实现倾斜车牌号也可以正常分割的目的,详细步骤见下期,车牌字符的切割(二)。
 
 

      

 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
 
 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/HHCCWWlxy/article/details/78445788
今日推荐