乘简 发表于 2018-8-4 11:14:21

车牌识别(六)投影锁定文字位置

本帖最后由 乘简 于 2018-8-4 11:22 编辑

投影法分垂直投影法也水平投影法,水平投影法就是根据每一行白点数,取最大值,去掉小于20%此白点数的上边区域与下边区域。

垂直投影法就是找到连续的白点为1个区域,去掉高度太小的区域,就是每个字的区域,从而可以切出每1个字所在的位置,与模板进行比较
#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);

    //计算一行需要多少字节,对齐到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, 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='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;
   
    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\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);
   
    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){
                wbmp=wbmp=wbmp=bmp;
            }
            else wbmp=wbmp=wbmp=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==255)||(dst==255))//如果第一行 或 第二行有白点
      for(j=0;j<img->height;j++)//消除此白点,直到碰到黑点退出
      {
            if(dst==255)
                dst=0;
            else break;
      }
    }
    for(i=0;i<img->width;i++)
    {
      //倒数1,2行中有白点,则消除,否则退出
      if((dst==255)||(dst==255))
            for(j=img->height-1;j>0;j--)
            {
                if(dst==255)
                  dst=0;
                else break;
            }
    }

    //记录每一行的白点数
    for(i=0;i<img->height;i++)
    {
      p=0;
      for(j=0;j<img->width;j++)
      {
            if(dst==255)
                p++;
      }
    }

    //找到白点数最多的行
    temp=0;
    for(i=0;i<img->height;i++)
    {
      if(p>temp)
      {
          temp=p;
      }
    }

    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<n)
      {
            img->up=i+1;//只有第一次,才赋值
            break;
      }
    }
    for(i=m+1;i<img->height;i++)//从下面往中间遍历
    {
      if(p<n)
      {
            img->down=i-1;
            break;
      }
    }
    free(p);

    //删除起始行之前的白点
    for(i=0;i<img->up;i++)
    {
      for(j=0;j<img->width;j++)
      {
            dst=0;
      }
    }
   
    //删除结束行之后的白点
    for(i=img->down+1;i<img->height;i++)
    {
      for(j=0;j<img->width;j++)
      {
            dst=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==255)//记录每列白点数
            {
                bd++;
                if(up>j)up=j;//最高白点
                if(down<j)down=j;//记住最后一个白点的位置
            }
      }

      if(bd)//当前列有白点
      {
            if(flag==0)//还没有记录起点
            {
                flag=1;
                img->left=i;//记录下起点
            }
      }
      else//当前列没白点
      {
            if(flag)//如果已记录了起点
            {
                if((down-up)+1>img->height/2)//找到正确的字符
                {
                  img->right=i-1;//记录结束区域
                  img->top=up;
                  img->bottom=down;
                  num++;//查找下一个字符
                }
                flag=0;//记录下一个记点
                up=img->height;
                down=0;
            }
      }
    }
    //如果最后一个没有结束
    if(flag){
      img->right=img->width-1;
      img->top=up;
      img->bottom=down;
      num++;
    }
      
    if(num<7)//位数不够
    {
      printf("car no min error\n");
    }
    else if(num>7)//如果找到多于7个字符,说明里面有一个“川”字
    {
      printf("car no error\n");
    }
}


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=img.image;
      }
    }
   
    //水平投影,去掉上下非文字区域
    shuipingtouying(&img1,temp);
   
    //垂直投影,去掉车牌中的点,并计算7个字各在什么位置
    cuizhitouying(&img1,temp);//垂直投影,框住每一个字符
   
    //写入镜像后的数据到2.bmp中
    WriteBmp1("2.bmp",temp,img.width,img.height);
   
    free(img.image);//释放动态分配的内存
    free(temp);
   
    printf("请打开2.bmp进行查看\n");
    system("pause");
    return 0;
}
页: [1]
查看完整版本: 车牌识别(六)投影锁定文字位置