车牌识别(二)切出车牌区域
上篇讲到,把车牌图片,根据蓝底这一特点,二值化原图,从而可以小区域锁定车牌所在位置,然后剪切出这块小区域。在剪切出车牌之前,如防大车牌在旋转角的时候,出现误差,所以先做个空心化处理。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef unsigned char byte;
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
//BMP图像结构
struct BMP_img
{
//{BMP头
BYTEbfType;//类型,判断是否为‘B’,‘M’
DWORD size;//文件尺寸
DWORD reser;//保留,为0
DWORD header_length;//头部长度,也就是数据起始位置
//}BMP头长度,14字节
//{信息头40字节
DWORD infoheader_length;//信息头长度,40
DWORD width;//图像宽度
DWORD height;//图像高度
WORDbiplanes;//颜色平面数,为1
WORDbmp_type;/* 8bit 24bit; */
DWORD compres;//0表示不压缩
DWORD datasize;//数据长度,size-54
DWORD bixpm;//水平分辩率
DWORD biypm;//垂直分辩率
DWORD clrused;//为0所有颜色,其它的为索引数
DWORD relclrused;//0表示都重要
//}信息头结束
//其它信息
BYTE *image;//指向一块内存,保存BMP的内容
DWORD lineBytes;//一行占多少字节
};
//从源BMP图中,剪切车牌所在区域的新结构
struct Bmp1{
DWORD width;
DWORD height;
BYTE *image;
int left;//保存车牌中7个字的左右列
int right;
int top;//保存车牌上下位置
int bottom;
int up;
int down;
byte strr;
byte string;//反回已找到的车牌下标
float ang;//倾斜角度
};
//蓝色车牌
struct HSV{
float H;//H值范围:190 ~ 245
float S;//S值范围: 0.35 ~ 1,我理解为黑白灰度
int V;//V值范围: 0.3 ~ 1
};
//文件图文件到内存中
int read_img(char const *fn, struct BMP_img *img)
{
FILE *infile;
if((infile=fopen(fn,"rb"))==NULL)return 0;
fread(&img->bfType,2,1,infile);//BM
if(!(img->bfType=='B' && img->bfType=='M'))return 0;
fread(&img->size,sizeof(DWORD),1,infile);
printf("\nBMP size :%d",(int)img->size);
fread(&img->reser,sizeof(DWORD),1,infile);
printf("\n保留位:");
fread(&img->header_length,sizeof(DWORD),1,infile);
printf("\nheader length :%d",(int)img->header_length);
fread(&img->infoheader_length,sizeof(DWORD),1,infile);
fread(&img->width, sizeof(DWORD), 1, infile);
fread(&img->height, sizeof(DWORD), 1, infile);
printf( "\nwidth :%d\nheight:%d ", (int)img->width, (int)img->height);
fread(&img->biplanes, sizeof(WORD), 1, infile);
fread(&img->bmp_type, sizeof(WORD), 1, infile);
printf("\nBMP Tpye :%d ", img->bmp_type);
fread(&img->compres, sizeof(DWORD), 1, infile);
if(img->compres==0) {printf("\nbmp图片为非压缩!");}printf(" ");
fread(&img->datasize, sizeof(DWORD), 1, infile);
printf("\nBMP Data Size :%d ",(int)img->datasize);
fread(&img->bixpm, sizeof(DWORD), 1, infile);
fread(&img->biypm, sizeof(DWORD), 1, infile);
fread(&img->clrused, sizeof(DWORD), 1, infile);
printf("\n实际使用颜色数=%d ",(int)img->clrused);printf(" ");
fread(&img->relclrused, sizeof(DWORD), 1, infile);
if(img->bmp_type==24)//24位色,这里只考虑24位色图,其它的不考虑
{
img->lineBytes=((img->width*3+3)>>2)<<2;//计算一行需要多少字节,对齐到4字节
//byte *temp=(byte *)malloc(sizeof(byte) * img->height * img->lineBytes);//分配一块内存,用于读文件
img->image=(byte *)malloc(img->lineBytes*img->height);//分配一块内存,用于保存图像数据
if(img->image==NULL) fprintf(stderr, "\n Allocation error for temp in read_bmp() \n");
fseek(infile, img->header_length, SEEK_SET);//跳过头部,也就是跳到图像位置
fread(img->image, sizeof(byte), (img->lineBytes)*img->height, infile);//全部读到内存中
}
fclose(infile);
return 1;
}
void WriteBmp24(char const *fn,byte *bmp,int width,int height)
{
FILE *infile;
int w4;
struct BMP_img img;
if((infile=fopen(fn,"wb"))==NULL)
{
return;
}
//一行有多少个字节
img.lineBytes=((width*3+3)>>2)<<2;//对齐到4字节边界
w4=img.lineBytes*height;//图像尺寸
img.bfType='B';img.bfType='M';
img.size=w4+54;
img.reser=0;
img.header_length=54;
img.infoheader_length=40;
img.width=width;
img.height=height;
img.biplanes=1;
img.bmp_type=24;
img.compres=0;
img.datasize=w4;
img.bixpm=0;
img.biypm=0;
img.clrused=0;
img.relclrused=0;
fwrite(&img.bfType,2,1,infile);//printf("\n打开的图为 %d",img->bfType);//B M
fwrite(&img.size,sizeof(DWORD),1,infile); // printf("\nBMP size :%l",img->size);
fwrite(&img.reser,sizeof(DWORD),1,infile);//printf("\n保留位:");
fwrite(&img.header_length,sizeof(DWORD),1,infile); //printf("\nheader length :%l",img->header_length);
fwrite(&img.infoheader_length,sizeof(DWORD),1,infile);
fwrite(&img.width, sizeof(DWORD), 1, infile);
fwrite(&img.height, sizeof(DWORD), 1, infile); //printf( "\nwidth :%l\nheight:%l ", img->width, img->height);
fwrite(&img.biplanes, sizeof(WORD), 1, infile);
fwrite(&img.bmp_type, sizeof(WORD), 1, infile);// printf("\nBMP Tpye :%l ", img->bmp_type);
fwrite(&img.compres, sizeof(DWORD), 1, infile); //if(img->compres==0) {printf("\nbmp图片为非压缩!");}printf(" ");
fwrite(&img.datasize, sizeof(DWORD), 1, infile);//printf("\nBMP Data Size :%l ",img->datasize);
fwrite(&img.bixpm, sizeof(DWORD), 1, infile);
fwrite(&img.biypm, sizeof(DWORD), 1, infile);
fwrite(&img.clrused, sizeof(DWORD), 1, infile); //printf("\n实际使用颜色数=%d ",img->clrused);printf(" ");
fwrite(&img.relclrused, sizeof(DWORD), 1, infile);
if(img.height*img.width*3==img.datasize)//已经对齐到了4字节
{
fwrite(bmp,img.datasize,1,infile);
}
else
{
for(int i=0;i<img.height;i++)//总共有多少行,需要一行一行保存,因为后面要补零
{
fwrite(&bmp,img.lineBytes,1,infile);
}
}
fclose(infile);
}
//把RGB数据转换成HSV空间图像数据
//输入,图像,HSV倍象素,宽,高
void hsvzation(byte *image,struct HSV *hsv,int width,int height)
{
int i,j,k;
DWORD l,lk;
//float min,max,delta,tmp;//h,s,v,m,n;
int min,max,delta,tmp;
byte r,g,b;
for(i=0;i<height;i++)
{
l=i*width;
for(k=0,j=0;j<width*3;k++,j+=3)
{
lk=l+k;
g=image;
b=image;
r=image;
tmp=min(r,g);//取3者最小值
min=min(tmp,b);
tmp=max(r,g);//取3者最大值
max=max(tmp,b);
hsv.V=max;//V保存三者最大值
delta=max-min;//保存最大值与最小值的差
if(delta==0)//如果3值相等
{
hsv.H=0;
hsv.S=0;//这句是我加的
continue;
}
//如果三值依次是100,150,200,则.S=100/200=0.5
//如果三值依次是0,50,100,则为1
//如果三值依次是99,99,100,则为0.01
//意思就是三值越接近,此值越小,越不接近,此值越大
//应该是指颜色灰度
hsv.S=(float)delta/max;
if(r==max)//如果红色为最大值,说明当前点的颜色偏红
{
//如果三值依次为200,150,100,则.H=50/100=0.5
//如果三值依次为100,50,0,则.H还是0.5
//如果三值依次为:100,99,99,则为0
//如果为100,0,100,则为-1
//可以看出,蓝色与绿色越接近,趋向0,否则为趋向+-1
hsv.H=(g-b)/(float)delta;//tmp;
}
else if(g==max)//如果绿色为最大值
hsv.H=2+(b-r);//没看懂,取值范围-253~255
else//蓝为最大值
{
//和红色最大一样的,[-1,0],,加上4后,变成【3,5]
hsv.H=4+(r-g)/(float)delta;//
}
//-1~1 *60=-60~60
//-253~257,*60=-15180~15420之间
hsv.H*=60;//(3,5)*60=
//可以看到,当其值为【-60,0)时,加上360=【300,360)区间
//至于那个-15180多的那种结果,加上360也不管用
if(hsv.H<0)
hsv.H+=360;//
//value=(int)(h+0.5);
}
}
}
//输入:image为经过HSV计算后的黑白图,再次去中心花,只出黑白框框图,用于计算车牌所在的区域
void edgesob8(byte *image,int width,int height)//8邻域
{
//int sum1,sum2,sum;double gray;
int i,j,logNum;
//int p;
BYTE *temp;
temp=(byte*)malloc(sizeof(byte)*width*height);//要配像素个内存区域
memset(temp,0x00,sizeof(byte)*width*height);//清零
//少循环了5行
for(i=3;i<height-2;i++)
{
for(j=3;j<width-2;j++)//少循环了5列
{
if(image)//如果当前点为白色
{
//当前像素*16,也就是255*16=4080
//这句话的意思是,如果周围全是白点,则变为黑,如果周围白点数不够,说明是边缘,则留着白点
logNum=16*image - image[(i-2)*width+j] - image[(i-1)*width+j-1] - 2*image[(i-1)*width+j] \
- image[(i-1)*width+j+1] - image - 2*image - 2*image \
- image - image[(i+1)*width+j-1] - 2*image[(i+1)*width+j] - image[(i+1)*width+j+1] \
- image[(i+2)*width+j];
if(logNum > 0)temp=255;
}
}
}
memcpy(image,temp,width*height);
free(temp);
temp=NULL;
}
//根据车牌蓝色范围,找出车牌所在的范围,两个坐标存入(*VL,*HL)-(*VH,*HH)
void location(byte *image,int width,int height,int yuzhi,int *HL,int *HH,int *VL,int *VH)
{
int i,j,n,maxnum,flag=0;
struct HSV *hsv;//float,float,int
int temp={0};
byte *temp1;
hsv=(struct HSV *)malloc(sizeof(struct HSV)*width*height);//分配HSV倍图像像素大小
hsvzation(image,hsv,width,height);//根据RGB,计算出HSV的值
//再分配一块内存,像素个
temp1=(byte *)malloc(sizeof(byte)*height*width);
for(i=0;i<height;i++)//循环高度
{
for(j=0,n=0;j<width;n+=3,j++)//循环宽度
{
if((hsv.H<240.0)&&(hsv.H>180.0)&&(hsv.V<250)&&(hsv.S>0.6))
{
temp1=255;//为白色
//putpixel(j,i,RGB(255,255,255));
}
else//为黑色
{
temp1=0;
//putpixel(j,i,RGB(0,0,0));
}
}
}//可以看到,本循环的意思就是把原始图像,黑白化,满足条件为白,否则通通为黑
//去掉白块中心区域,也就是说某白点,紧挨着它的12个点全为白点,则变为黑点
edgesob8(temp1,width,height);
for(i=0;i<height;i++)
{
temp=0;
for(j=0;j<width;j++)
{
if(temp1==255)
{
temp++;
}
}
}//本循环,计算每一行的白点数
maxnum=temp;
for(i=1;i<height;i++)
{
if(temp>maxnum)
maxnum=temp;
}//找到白点最多的行的白点数
maxnum=maxnum/3;//白点数除以3
for(i=0;i<height;i++)
{
if(flag==0)
{
if(temp>maxnum)//本行的白点数大于1/3最多白点数
{
*HL=i-yuzhi;//已找到第一行满足条件的行号-yuzhi,多留出一点
if(*HL<0)*HL=0;
flag=1;
}
}
if(flag==1)
{
if(temp==0)
{
*HH=i+yuzhi;
if(*HH>height)*HH=height;
break;
}
}
}
//计算各列的白点数
memset(temp,0x00,sizeof(int)*width);
for(i=0;i<width;i++)
{
for(j=0;j<height;j++)
{
if(temp1==255)
{
temp++;
}
}
}
flag=0;
maxnum=temp;
//找到最多白点列的白点数
for(i=1;i<width;i++)
{
if(temp>maxnum)
maxnum=temp;
}
maxnum=maxnum/3;
for(i=0;i<width;i++)//找到满足条件的第一列
{
if(temp>maxnum)
{
*VL=i-yuzhi;
if(*VL<0)*VL=0;
break;
}
}
for(i=width;i>*VL;i--)//找到满足条件的最后一列
{
if(temp>maxnum)
{
*VH=i+yuzhi;
if(*VH>width)*VH=width;
break;
}
}
free(temp1);
temp1=NULL;
free(hsv);
}
//从img中剪切出车牌所在区域,存入img1中
void CutBmp(struct BMP_img *img,struct Bmp1 *img1,int HL,int HH,int VL,int VH)
{
int i,j,n;
int x=0,y=0;
img1->width=VH-VL;//新宽度
img1->height=HH-HL;//新高度
img1->image=(byte*)malloc(img1->height*img1->width*3);
for(x=0,i=HL;i<HH;x++,i++)
{
for(y=0,n=0,j=VL*3;j<VH*3;n++,j+=3,y+=3)
{
img1->image=img->image;
img1->image=img->image;
img1->image=img->image;
}
}
}
int main(int argc, char **argv)
{
struct BMP_img img;//定义结构
struct Bmp1 img1;
int HL=0,HH=0,VH=0,VL=0;
//把当前文件夹下的1.bmp文件内容,读到img结构中,并上下镜像
if(read_img("1.bmp", &img)==0)
{
printf("error");
return 0;
}
location(img.image,img.width,img.height,15,&HL,&HH,&VL,&VH);//根据蓝底找到车牌所在位置
CutBmp(&img,&img1,HL,HH,VL,VH);//剪切出车牌区域,放入img1中
//写入镜像后的数据到2.bmp中
WriteBmp24("2.bmp",img1.image,img1.width,img1.height);
free(img.image);//释放动态分配的内存
printf("请打开2.bmp进行查看\n");
system("pause");
return 0;
}
页:
[1]