天天看點

車牌識别(八)縮放每個文字到模闆大小

由于給定的車牌圖檔中的文字大小不一,還得進行縮放操作,儲存與模闆大小一緻,再與模闆進行比較。

給定的模闆為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;
}
           

切出的車牌字元,

車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小

縮放後的車牌字元,

車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小
車牌識别(八)縮放每個文字到模闆大小

繼續閱讀