` 本帖最后由 wwwming0329 于 2016-1-11 09:27 编辑
最近一直出差在外地,这个周末总算抽出时间对当初申请的创龙DSP6748项目做个提交了。
一、目的
通过6748识别车辆的车牌号,读入车辆照片,车牌字符识别与输出。代码完全用C语言实现。
二、步骤与原理
1.车牌照片读入
这一部分移植FATFS来读取SD卡上的图片。由于帖子中的代码区域不支持折叠,未避免代码太长影响阅读,这里只贴部分代码。
实验中所有图片均为bmp格式的,因此这里只需要了解BMP格式文件的结构即可,具体可以看我之前的帖子。
- res = f_open(&fil1,bmpName,FA_WRITE|FA_READ);
- if(res != FR_OK)
- {
- while(1);
- }
- else
- {
- f_read(&fil1 ,&m.file.bfType, 2, &outnum); // 类型
- f_read(&fil1,&m.file.bfSize, 4,&outnum); // 文件长度
- f_read(&fil1,&m.file.bfReserverd1, 2,&outnum); // 保留字 1
- f_read(&fil1,&m.file.bfReserverd2, sizeof(short int)* 1, &outnum); // 保留字 2
- f_read(&fil1,&m.file.bfbfOffBits, sizeof(int)* 1, &outnum); // 偏移量
- f_read(&fil1,&m.info.biSize, sizeof(int)*1, &outnum); // 此结构大小
- f_read(&fil1,&m.info.biWidth, sizeof(int)* 1, &outnum); // 位图的宽度
- f_read(&fil1,&m.info.biHeight, sizeof(int)* 1, &outnum); // 位图的高度
- f_read(&fil1,&m.info.biPlanes, sizeof(short)* 1, &outnum); // 目标设备位图数
- f_read(&fil1,&m.info.biBitcount, sizeof(short) *1, &outnum); // 颜色深度
- f_read(&fil1,&m.info.biCompression, sizeof(int)* 1, &outnum); // 位图压缩类型
- f_read(&fil1,&m.info.biSizeImage, sizeof(int)* 1, &outnum); // 位图大小
- f_read(&fil1,&m.info.biXPelsPermeter, sizeof(int)* 1, &outnum); // 位图水平分辨率
- f_read(&fil1,&m.info.biYPelsPermeter, sizeof(int)* 1, &outnum); // 位图垂直分辨率
- f_read(&fil1,&m.info.biClrUsed, sizeof(int)* 1, &outnum); // 位图实际使用颜色数
- f_read(&fil1,&m.info.biClrImportant, sizeof(int)* 1, &outnum); // 位图显示中比较重要颜色数
- }
复制代码
2.提取图像信息
上面的函数中即已将图像信息读取过来了。
原始图像如下:(我去,不支持上传BMP格式图片,还得用QQ截个图)
3.确定车牌区域
确定车牌位置的方法有很多,这里使用的是最简单的算法来做车牌定位。
上一步,已经获取了图片数据。但是这里的是RGB数据,将RGB转换成HSV空间图像数据。设置蓝色车牌底色阈值范围,进行颜色过滤。过滤后去噪,然后做边缘检测去除周围干扰。然后通过水平投影与垂直投影确定车牌位置。
截取的图片如下:
4.倾斜矫正
有些图片的车牌位置会是倾斜的,为了提高之后的识别正确率,需要将图片矫正。
倾斜矫正一般在上一部Hough变换时,一起完成。在Hough变换后,检测截取的车牌区域角度,利用旋转算法,选择之前粗略提取的车牌位置。
5.提取完整车牌图像
可以看出上一步提取的车牌图片其实还包含边框的,这里需要在进行一次定位截取操作,方法与第3步一样只是选取的阀值不一样。
再次提取后图片:
6.去除噪点干扰
在去除噪点前,需要将车牌图片灰度化,二值化处理。灰度化与二值化在我之前的帖子中有详细介绍了。这里直接给出灰度化与二值化的车牌图片:
灰度化后:
二值化后:
可以看出二值化后的图片上方与下方有干扰点。
- void delpoint(byte *dst, int width, int height, int yuzhi)
- {
- int i, j, num = 0, num1;
- byte *src = (byte *)malloc(sizeof(byte) * width * height);
- memset(src, 0x00, sizeof(byte)*width * height);
- for (i = 1; i < height - 1; i++) //消除孤立噪点
- {
- for (j = 1; j < width - 1; j++)
- {
- if (dst[i * width + j] > 200)
- {
- num1 = dst[i * width + j - 1] + dst[i * width + j + 1] + dst[(i - 1) * width + j - 1] + dst[(i - 1) * width + j] + dst[(i - 1) * width + j + 1] + dst[(i + 1) * width + j - 1] + dst[(i + 1) * width + j] + dst[(i + 1) * width + j + 1] + 255;
- num = num1 / 255;
- if (num >= yuzhi)
- //if((dst[i*width+j-1]==0)&&(dst[i*width+j-1]==0)&&(dst[(i-1)*width+j+1]==0)&&(dst[(i-1)*width+j]==0)&&(dst[(i-1)*width+j+1]==0)&&(dst[(i+1)*width+j-1]==0)&&(dst[(i+1)*width+j]==0)&&(dst[(i+1)*width+j+1]==0))
- src[i * width + j] = 255;
- else src[i * width + j] = 0;
- }
- }
- }
- memcpy(dst, src, sizeof(byte)*width * height);
- }
复制代码
删除噪点后的图片:
7.车牌字符分割
上一步得到的图片已经很清晰了,可以用来对其进行分割了。通过水平投影与垂直投影可以确定字符的上下,左右边界。这个算法大致工作原理是先由下向上对图像进行逐行扫描直至遇到第一个黑色像素点,记录下来。然后再由上向下对图像进行逐行扫描直至找到第一个黑色像素,这样就找到了图像的大致高度范围;在这个高度范围内从左往右扫获取字符宽度即可。
8.读入模版
分割字符后需要对字符进行特征扫描来确认到底是啥字符呢。这里我们需要一些模版。
所谓模版就是一些特定的字符图片,我们需要将我们上一步分割的字符,与模版逐一进行特征比较来确认实际字符。
9.完成匹配,识别字符
部分识别算法:
- for (i = 36; i < 67; i++)
- {
- k = cmpstr(img->strr[0], img->strc[i]);
- if (k == 0)
- {
- n = i;
- break;
- }
- else
- {
- if (k < m)
- {
- m = k;
- n = i;
- }
- }
- }
- img->string[0] = n;
- for (i = 1; i < 7; i++)
- {
- n = 0;
- k = 0;
- m = 800;
- for (j = 0; j < 36; j++)
- {
- if ((img->p2[i] - img->p1[i]) < 4)
- {
- n = 1;
- break;
- }
- k = cmpstr(img->strr[i], img->strc[j]);
- if (k == 0)
- {
- n = j;
- break;
- }
- else
- {
- if (k < m)
- {
- m = k;
- n = j;
- }
- }
- }
- if (n == 27) //p&r
- {
- k = cmpstr(img->strc[27], img->strr[i]);
- j = cmpstr(img->strr[i], img->strc[25]);
- if (k < j)
- n = 27;
- else n = 25;
- }
- if (n == 0) //0
- {
- j = cmpstr(img->strc[0], img->strr[i]);
- k = cmpstr(img->strr[i], img->strc[12]);
- if (j > k)
- n = 12;
- else n = 0;
- }
- if (n == 26)
- {
- k = cmpstr(img->strc[26], img->strr[i]);
- j = cmpstr(img->strr[i], img->strc[0]);
- if (k < j)
- n = 26;
- else n = 0;
- }
- if (n == 19)
- {
- k = cmpstr(img->strc[0], img->strr[i]);
- j = cmpstr(img->strr[19], img->strc[i]);
- if (k < j)
- n = 0;
- else n = 19;
- }
- if (n == 13)
- {
- k = cmpstr(img->strc[13], img->strr[i]);
- j = cmpstr(img->strr[i], img->strc[0]);
- if (k < j)
- n = 13;
- else n = 0;
- }
复制代码
10.输出车牌字符
|