由于給定的車牌圖檔中的文字大小不一,還得進行縮放操作,儲存與模闆大小一緻,再與模闆進行比較。
給定的模闆為16 x 32像素,是以要把鎖定的圖檔進行縮放操作。
#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頭
BYTE bfType[2];//類型,判斷是否為‘B’,‘M’
DWORD size;//檔案尺寸
DWORD reser;//保留,為0
DWORD header_length;//頭部長度,也就是資料起始位置
//}BMP頭長度,14位元組
//{資訊頭40位元組
DWORD infoheader_length;//資訊頭長度,40
DWORD width;//圖像寬度
DWORD height;//圖像高度
WORD biplanes;//顔色平面數,為1
WORD bmp_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[10];//儲存車牌中7個字的左右列
int right[10];
int top[10];//儲存車牌上下位置
int bottom[10];
int up;
int down;
byte strr[7][2500];
byte string[7];//反回已找到的車牌下标
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[0]=='B' && img->bfType[1]=='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\n height :%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);
//計算一行需要多少位元組,對齊到4位元組
img->lineBytes=(img->width*img->bmp_type+31)/32*4;//printf("\nLineBytes :%l\n",img->lineBytes);
if(img->bmp_type==24)//24位色
{
img->image=(unsigned char *)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);//跳過頭部,也就是跳到圖像位置
if(img->datasize==img->width*3)
fread(img->image, sizeof(unsigned char), (img->lineBytes)*img->height, infile);//全部讀到記憶體中
else
{
for(int i=0;i<img->height;i++)
{
fread(&img->image[i*img->width*3], sizeof(unsigned char), img->lineBytes, infile);//全部讀到記憶體中
}
}
}
fclose(infile);
return 1;
}
//把二值圖儲存為24位黑白圖
void WriteBmp1(char const *fn,byte *bmp,int width,int height)
{
int w4;
struct BMP_img img;
//一行有多少個位元組
img.lineBytes=((width*3+3)>>2)<<2;//對齊到4位元組邊界
w4=img.lineBytes*height;//圖像尺寸
img.bfType[0]='B';img.bfType[1]='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;
FILE *infile;
if((infile=fopen(fn,"wb"))==NULL)
{
return;
}
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\n height :%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);
byte *wbmp=(byte*)malloc(w4);//後面多加兩個位元組,用于4位元組對齊
for(int i=0,s,w;i<height;i++)
{
s=i*width;
w=i*img.lineBytes;
for(int j=0;j<width;j++)
{
if(bmp[s+j]){
wbmp[w+j*3]=wbmp[w+j*3+1]=wbmp[w+j*3+2]=bmp[s+j];
}
else wbmp[w+j*3]=wbmp[w+j*3+1]=wbmp[w+j*3+2]=0;
}
}
fwrite(wbmp,img.datasize,1,infile);
free(wbmp);
fclose(infile);
}
//水準投影,從中間往兩邊計算,進而去掉最上面與最下面的多餘部分
void shuipingtouying(struct Bmp1 *img,byte *dst)//得到車牌的上下邊緣
{
byte temp;
int i,j,m,n;
int *p=(int*)malloc(img->height*sizeof(int));//申請以行為機關的整型記憶體數
for(i=0;i<img->width;i++)//寬度循環
{
if((dst[i]==255)||(dst[img->width+i]==255))//如果第一行 或 第二行有白點
for(j=0;j<img->height;j++)//消除此白點,直到碰到黑點退出
{
if(dst[j*img->width+i]==255)
dst[j*img->width+i]=0;
else break;
}
}
for(i=0;i<img->width;i++)
{
//倒數1,2行中有白點,則消除,否則退出
if((dst[img->width*(img->height-1)+i]==255)||(dst[img->width*(img->height-2)+i]==255))
for(j=img->height-1;j>0;j--)
{
if(dst[j*img->width+i]==255)
dst[j*img->width+i]=0;
else break;
}
}
//記錄每一行的白點數
for(i=0;i<img->height;i++)
{
p[i]=0;
for(j=0;j<img->width;j++)
{
if(dst[i*img->width+j]==255)
p[i]++;
}
}
//找到白點數最多的行
temp=0;
for(i=0;i<img->height;i++)
{
if(p[i]>temp)
{
temp=p[i];
}
}
n=temp/5;//以20%做為閥值
img->up=0;
img->down=img->height;
m=img->height/2;
for(i=m-1;i>0;i--)//從中間往上下周遊,如果有一行的白點數小于20%,則做為起點與終點
{
if(p[i]<n)
{
img->up=i+1;//隻有第一次,才指派
break;
}
}
for(i=m+1;i<img->height;i++)//從下面往中間周遊
{
if(p[i]<n)
{
img->down=i-1;
break;
}
}
free(p);
//删除起始行之前的白點
for(i=0;i<img->up;i++)
{
for(j=0;j<img->width;j++)
{
dst[i*img->width+j]=0;
}
}
//删除結束行之後的白點
for(i=img->down+1;i<img->height;i++)
{
for(j=0;j<img->width;j++)
{
dst[i*img->width+j]=0;
}
}
}
//垂直投影法
void cuizhitouying(struct Bmp1 *img,byte *temp)
{
DWORD i,j;
int num,flag;
int up;
int down;
int bd;//目前列白點數
//計算1個字元的寬度,也就是1塊車牌的寬度,把所有字挨緊,空白區域占30%左右
//然後一塊車牌有7個字,是以一個字的寬度大根是width*0.7/7
num=flag=0;
up=img->height;//最高一個白點
down=0;//最低一個白點
for(i=0;i<img->width;i++)//按寬周遊
{
bd=0;
for(j=0;j<img->height;j++)//按高周遊,也就是1列1列的周遊
{
if(temp[j*img->width+i]==255)//記錄每列白點數
{
bd++;
if(up>j)up=j;//最高白點
if(down<j)down=j;//記住最後一個白點的位置
}
}
if(bd)//目前列有白點
{
if(flag==0)//還沒有記錄起點
{
flag=1;
img->left[num]=i;//記錄下起點
}
}
else//目前列沒白點
{
if(flag)//如果已記錄了起點
{
if((down-up)+1>img->height/2)//找到正确的字元
{
img->right[num]=i-1;//記錄結束區域
img->top[num]=up;
img->bottom[num]=down;
num++;//查找下一個字元
}
flag=0;//記錄下一個記點
up=img->height;
down=0;
}
}
}
//如果最後一個沒有結束
if(flag){
img->right[num]=img->width-1;
img->top[num]=up;
img->bottom[num]=down;
num++;
}
if(num<7)//位數不夠
{
printf("car no min error\n");
}
else if(num>7)//如果找到多于7個字元,說明裡面有一個“川”字
{
printf("car no error\n");
}
}
//複制單個字元,存進數組中
void strBmp(struct Bmp1 *img,byte *temp)
{
int i,j,k,n;
//int w,h;
for(i=0;i<7;i++)
{
n=0;
for(j=img->top[i];j<=img->bottom[i];j++)//高度循環
{
for(k=img->left[i];k<=img->right[i];k++)//寬度循環
{
img->strr[i][n++]=temp[j*img->width+k];
}
}
if(i==0)WriteBmp1("z1.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==1)WriteBmp1("z2.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==2)WriteBmp1("z3.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==3)WriteBmp1("z4.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==4)WriteBmp1("z5.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==5)WriteBmp1("z6.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
if(i==6)WriteBmp1("z7.bmp",img->strr[i],img->right[i]-img->left[i]+1,img->bottom[i]-img->top[i]+1);
}
}
//縮放到指定的寬度與高度
void changeGray(byte *srcBmp,byte *dstBmp,int width,int height,int nWidth,int nHeight)
{
int i=0,j=0,i0,j0;
float xx;float yy;
xx=(float)nWidth/width;//寬度縮放比
yy=(float)nHeight/height;
memset(dstBmp,0x00,nHeight*nWidth*sizeof(byte));
if(width<nWidth/2)//說明為1
{
for(i=0;i<nHeight;i++)
{
for(j=0;j<width;j++)
{
if(i<=height)
{
dstBmp[i*nWidth+j]=srcBmp[i*width+j];
}
else{
dstBmp[i*nWidth+j]=srcBmp[(height-1)*width+j];
}
}
}
}
else
{
for(i = 0; i <nHeight; i++)
{
for(j = 0; j <nWidth; j++)
{
//i0 = (int) ((float)i/yy+0.5);
//j0 = (int) ((float)j/xx+0.5);
i0 = (int) ((float)i/yy);
j0 = (int) ((float)j/xx);
if((j0>=0)&&(j0<width)&&(i0>=0)&&(i0<height))
// {
dstBmp[i*nWidth+j]=srcBmp[i0*width+j0];
//}
//else
//{
// dstBmp[i*nWidth+j]=255;
//}
}
}
}
}
void guiyi(struct Bmp1 *img)
{
int xxx;
int i;
int yyy;
byte *temp=(byte *)malloc(sizeof(byte)*512);
for(i=0;i<7;i++)
{
xxx=img->right[i]-img->left[i]+1;
yyy=img->bottom[i]-img->top[i]+1;
changeGray(img->strr[i],temp,xxx,yyy,16,32);
memcpy(img->strr[i],temp,sizeof(byte)*512);
if(i==0)WriteBmp1("zz1.bmp",img->strr[i],16,32);
if(i==1)WriteBmp1("zz2.bmp",img->strr[i],16,32);
if(i==2)WriteBmp1("zz3.bmp",img->strr[i],16,32);
if(i==3)WriteBmp1("zz4.bmp",img->strr[i],16,32);
if(i==4)WriteBmp1("zz5.bmp",img->strr[i],16,32);
if(i==5)WriteBmp1("zz6.bmp",img->strr[i],16,32);
if(i==6)WriteBmp1("zz7.bmp",img->strr[i],16,32);
}
}
int main(int argc, char **argv)
{
struct BMP_img img;//定義結構
struct Bmp1 img1;
//把目前檔案夾下的1.bmp檔案内容,讀到img結構中,并上下鏡像
if(read_img("1.bmp", &img)==0)
{
printf("error");
return 0;
}
img1.width=img.width;
img1.height=img.height;
byte *temp=(byte*)malloc(img.width*img.height);
//24位灰階圖轉單色灰階圖
for(int i=0;i<img.height;i++)
{
for(int j=0;j<img.width;j++)
{
temp[i*img.width+j]=img.image[i*img.width*3+j*3];
}
}
//水準投影,去掉上下非文字區域
shuipingtouying(&img1,temp);
//垂直投影,去掉車牌中的點,并計算7個字各在什麼位置
cuizhitouying(&img1,temp);//垂直投影,框住每一個字元
//寫入鏡像後的資料到2.bmp中
WriteBmp1("2.bmp",temp,img.width,img.height);
//把每個文字切出來
strBmp(&img1,temp);//把車牌字元放到數組裡面
//縮放每個文字,與模闆大小相等
guiyi(&img1);
free(img.image);//釋放動态配置設定的記憶體
free(temp);
printf("請打開2.bmp進行檢視\n");
system("pause");
return 0;
}
切出的車牌字元,
縮放後的車牌字元,