找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1987|回复: 0

车牌识别(二)切出车牌区域

[复制链接]
发表于 2018-8-1 10:49:38 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
上篇讲到,把车牌图片,根据蓝底这一特点,二值化原图,从而可以小区域锁定车牌所在位置,然后剪切出这块小区域。

在剪切出车牌之前,如防大车牌在旋转角的时候,出现误差,所以先做个空心化处理。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. typedef unsigned long       DWORD;
  5. typedef int                 BOOL;
  6. typedef unsigned char       BYTE;
  7. typedef unsigned short      WORD;
  8. typedef float               FLOAT;
  9. typedef unsigned char       byte;

  10. #define max(a,b)            (((a) > (b)) ? (a) : (b))
  11. #define min(a,b)            (((a) < (b)) ? (a) : (b))

  12. //BMP图像结构
  13. struct BMP_img
  14. {
  15.     //{BMP头
  16.     BYTE  bfType[2];//类型,判断是否为‘B’,‘M’
  17.     DWORD size;//文件尺寸
  18.     DWORD reser;//保留,为0
  19.     DWORD header_length;//头部长度,也就是数据起始位置
  20.     //}BMP头长度,14字节

  21.     //{信息头40字节
  22.     DWORD infoheader_length;//信息头长度,40
  23.     DWORD width;//图像宽度
  24.     DWORD height;//图像高度
  25.     WORD  biplanes;//颜色平面数,为1
  26.     WORD  bmp_type;/* 8bit 24bit; */
  27.     DWORD compres;//0表示不压缩
  28.     DWORD datasize;//数据长度,size-54
  29.     DWORD bixpm;//水平分辩率
  30.     DWORD biypm;//垂直分辩率
  31.     DWORD clrused;//为0所有颜色,其它的为索引数
  32.     DWORD relclrused;//0表示都重要
  33.     //}信息头结束

  34.     //其它信息
  35.     BYTE *image;//指向一块内存,保存BMP的内容
  36.     DWORD lineBytes;//一行占多少字节
  37. };

  38. //从源BMP图中,剪切车牌所在区域的新结构
  39. struct Bmp1{
  40.     DWORD width;
  41.     DWORD height;
  42.     BYTE *image;
  43.     int left[10];//保存车牌中7个字的左右列
  44.     int right[10];
  45.     int top[10];//保存车牌上下位置
  46.     int bottom[10];
  47.     int up;
  48.     int down;
  49.     byte strr[7][1024];
  50.     byte string[7];//反回已找到的车牌下标
  51.     float ang;//倾斜角度
  52. };

  53. //蓝色车牌
  54. struct HSV{
  55.   float H;//H值范围:190 ~ 245
  56.   float S;//S值范围: 0.35 ~ 1,我理解为黑白灰度
  57.   int V;//V值范围: 0.3 ~ 1
  58. };

  59. //文件图文件到内存中
  60. int read_img(char const *fn, struct BMP_img *img)
  61. {
  62.     FILE *infile;
  63.     if((infile=fopen(fn,"rb"))==NULL)return 0;

  64.     fread(&img->bfType,2,1,infile);//BM
  65.     if(!(img->bfType[0]=='B' && img->bfType[1]=='M'))return 0;
  66.     fread(&img->size,sizeof(DWORD),1,infile);
  67.     printf("\nBMP size             :%d",(int)img->size);
  68.     fread(&img->reser,sizeof(DWORD),1,infile);
  69.     printf("\n保留位:");
  70.     fread(&img->header_length,sizeof(DWORD),1,infile);
  71.     printf("\nheader length    :%d",(int)img->header_length);
  72.     fread(&img->infoheader_length,sizeof(DWORD),1,infile);
  73.     fread(&img->width, sizeof(DWORD), 1, infile);
  74.     fread(&img->height, sizeof(DWORD), 1, infile);
  75.     printf( "\nwidth   :%d\n  height  :%d ", (int)img->width, (int)img->height);
  76.     fread(&img->biplanes, sizeof(WORD), 1, infile);
  77.     fread(&img->bmp_type, sizeof(WORD), 1, infile);
  78.     printf("\nBMP Tpye             :%d ", img->bmp_type);
  79.     fread(&img->compres, sizeof(DWORD), 1, infile);
  80.     if(img->compres==0) {printf("\nbmp图片为非压缩!");}printf(" ");
  81.     fread(&img->datasize, sizeof(DWORD), 1, infile);
  82.     printf("\nBMP Data Size        :%d ",(int)img->datasize);
  83.     fread(&img->bixpm, sizeof(DWORD), 1, infile);
  84.     fread(&img->biypm, sizeof(DWORD), 1, infile);
  85.     fread(&img->clrused, sizeof(DWORD), 1, infile);
  86.     printf("\n实际使用颜色数=%d ",(int)img->clrused);printf(" ");
  87.     fread(&img->relclrused, sizeof(DWORD), 1, infile);

  88.     if(img->bmp_type==24)//24位色,这里只考虑24位色图,其它的不考虑
  89.     {
  90.         img->lineBytes=((img->width*3+3)>>2)<<2;//计算一行需要多少字节,对齐到4字节

  91.         //byte *temp=(byte *)malloc(sizeof(byte) * img->height * img->lineBytes);//分配一块内存,用于读文件
  92.         img->image=(byte *)malloc(img->lineBytes*img->height);//分配一块内存,用于保存图像数据
  93.         if(img->image==NULL) fprintf(stderr, "\n Allocation error for temp in read_bmp() \n");
  94.         fseek(infile, img->header_length, SEEK_SET);//跳过头部,也就是跳到图像位置
  95.         fread(img->image, sizeof(byte), (img->lineBytes)*img->height, infile);//全部读到内存中
  96.     }
  97.     fclose(infile);
  98.     return 1;
  99. }

  100. void WriteBmp24(char const *fn,byte *bmp,int width,int height)
  101. {
  102.     FILE *infile;
  103.     int w4;
  104.     struct BMP_img img;
  105.     if((infile=fopen(fn,"wb"))==NULL)
  106.     {
  107.         return;
  108.     }
  109.     //一行有多少个字节
  110.     img.lineBytes=((width*3+3)>>2)<<2;//对齐到4字节边界
  111.     w4=img.lineBytes*height;//图像尺寸
  112.     img.bfType[0]='B';img.bfType[1]='M';
  113.     img.size=w4+54;
  114.     img.reser=0;
  115.     img.header_length=54;
  116.     img.infoheader_length=40;
  117.     img.width=width;
  118.     img.height=height;
  119.     img.biplanes=1;
  120.     img.bmp_type=24;
  121.     img.compres=0;
  122.     img.datasize=w4;
  123.     img.bixpm=0;
  124.     img.biypm=0;
  125.     img.clrused=0;
  126.     img.relclrused=0;

  127.     fwrite(&img.bfType,2,1,infile);//printf("\n打开的图为 %d",img->bfType);//B M
  128.     fwrite(&img.size,sizeof(DWORD),1,infile);     //        printf("\nBMP size             :%l",img->size);
  129.     fwrite(&img.reser,sizeof(DWORD),1,infile);//printf("\n保留位:");
  130.     fwrite(&img.header_length,sizeof(DWORD),1,infile); //printf("\nheader length    :%l",img->header_length);
  131.     fwrite(&img.infoheader_length,sizeof(DWORD),1,infile);
  132.     fwrite(&img.width, sizeof(DWORD), 1, infile);
  133.     fwrite(&img.height, sizeof(DWORD), 1, infile);     //printf( "\nwidth   :%l\n  height  :%l ", img->width, img->height);
  134.     fwrite(&img.biplanes, sizeof(WORD), 1, infile);
  135.     fwrite(&img.bmp_type, sizeof(WORD), 1, infile);  // printf("\nBMP Tpye             :%l ", img->bmp_type);
  136.     fwrite(&img.compres, sizeof(DWORD), 1, infile);    //if(img->compres==0) {printf("\nbmp图片为非压缩!");}printf(" ");
  137.     fwrite(&img.datasize, sizeof(DWORD), 1, infile);//printf("\nBMP Data Size        :%l ",img->datasize);
  138.     fwrite(&img.bixpm, sizeof(DWORD), 1, infile);
  139.     fwrite(&img.biypm, sizeof(DWORD), 1, infile);
  140.     fwrite(&img.clrused, sizeof(DWORD), 1, infile);    //printf("\n实际使用颜色数=%d ",img->clrused);printf(" ");
  141.     fwrite(&img.relclrused, sizeof(DWORD), 1, infile);
  142.     if(img.height*img.width*3==img.datasize)//已经对齐到了4字节
  143.     {
  144.         fwrite(bmp,img.datasize,1,infile);
  145.     }
  146.     else
  147.     {
  148.         for(int i=0;i<img.height;i++)//总共有多少行,需要一行一行保存,因为后面要补零
  149.         {
  150.             fwrite(&bmp[i*img.width*3],img.lineBytes,1,infile);
  151.         }
  152.     }
  153.     fclose(infile);
  154. }

  155. //把RGB数据转换成HSV空间图像数据
  156. //输入,图像,HSV倍象素,宽,高
  157. void hsvzation(byte *image,struct HSV *hsv,int width,int height)
  158. {
  159.     int i,j,k;
  160.     DWORD l,lk;
  161.     //float min,max,delta,tmp;//h,s,v,m,n;
  162.     int min,max,delta,tmp;
  163.     byte r,g,b;
  164.     for(i=0;i<height;i++)
  165.     {
  166.         l=i*width;
  167.         for(k=0,j=0;j<width*3;k++,j+=3)
  168.         {
  169.             lk=l+k;
  170.             g=image[l*3+j+1];
  171.             b=image[l*3+j];
  172.             r=image[l*3+j+2];

  173.             tmp=min(r,g);//取3者最小值
  174.             min=min(tmp,b);

  175.             tmp=max(r,g);//取3者最大值
  176.             max=max(tmp,b);
  177.             hsv[lk].V=max;//V保存三者最大值
  178.             delta=max-min;//保存最大值与最小值的差
  179.             if(delta==0)//如果3值相等
  180.             {
  181.                 hsv[lk].H=0;
  182.                 hsv[lk].S=0;//这句是我加的
  183.                 continue;
  184.             }

  185.             //如果三值依次是100,150,200,则.S=100/200=0.5
  186.             //如果三值依次是0,50,100,则为1
  187.             //如果三值依次是99,99,100,则为0.01
  188.             //意思就是三值越接近,此值越小,越不接近,此值越大
  189.             //应该是指颜色灰度
  190.             hsv[lk].S=(float)delta/max;

  191.             if(r==max)//如果红色为最大值,说明当前点的颜色偏红
  192.             {
  193.                 //如果三值依次为200,150,100,则.H=50/100=0.5
  194.                 //如果三值依次为100,50,0,则.H还是0.5
  195.                 //如果三值依次为:100,99,99,则为0
  196.                 //如果为100,0,100,则为-1
  197.                 //可以看出,蓝色与绿色越接近,趋向0,否则为趋向+-1
  198.                 hsv[lk].H=(g-b)/(float)delta;//tmp;
  199.             }
  200.             else if(g==max)//如果绿色为最大值
  201.                 hsv[lk].H=2+(b-r);//没看懂,取值范围-253~255
  202.             else//蓝为最大值
  203.             {
  204.                 //和红色最大一样的,[-1,0],[0-1],加上4后,变成【3,5]
  205.                 hsv[lk].H=4+(r-g)/(float)delta;//
  206.             }
  207.             //-1~1 *60=-60~60
  208.             //-253~257,*60=-15180~15420之间
  209.             hsv[lk].H*=60;//(3,5)*60=[180,300]
  210.             
  211.             //可以看到,当其值为【-60,0)时,加上360=【300,360)区间
  212.             //至于那个-15180多的那种结果,加上360也不管用
  213.             if(hsv[lk].H<0)
  214.                 hsv[lk].H+=360;//
  215.             //value=(int)(h+0.5);
  216.         }
  217.     }
  218. }

  219. //输入:image为经过HSV计算后的黑白图,再次去中心花,只出黑白框框图,用于计算车牌所在的区域
  220. void edgesob8(byte *image,int width,int height)//8邻域
  221. {

  222.     //int sum1,sum2,sum;double gray;
  223.     int i,j,logNum;
  224.     //int p[8];
  225.     BYTE *temp;
  226.     temp=(byte*)malloc(sizeof(byte)*width*height);//要配像素个内存区域
  227.     memset(temp,0x00,sizeof(byte)*width*height);//清零
  228.     //少循环了5行
  229.     for(i=3;i<height-2;i++)
  230.     {
  231.         for(j=3;j<width-2;j++)//少循环了5列
  232.         {
  233.             if(image[i*width+j])//如果当前点为白色
  234.             {
  235.                 //当前像素*16,也就是255*16=4080
  236.                 //这句话的意思是,如果周围全是白点,则变为黑,如果周围白点数不够,说明是边缘,则留着白点
  237.                 logNum=16*image[i*width+j] - image[(i-2)*width+j] - image[(i-1)*width+j-1] - 2*image[(i-1)*width+j] \
  238.                         - image[(i-1)*width+j+1] - image[i*width+j-2] - 2*image[i*width+j-1] - 2*image[i*width+j+1] \
  239.                         - image[i*width+j+2] - image[(i+1)*width+j-1] - 2*image[(i+1)*width+j] - image[(i+1)*width+j+1] \
  240.                         - image[(i+2)*width+j];
  241.                 if(logNum > 0)temp[i*width+j]=255;
  242.             }
  243.         }
  244.     }
  245.    memcpy(image,temp,width*height);
  246.    free(temp);
  247.    temp=NULL;
  248. }

  249. //根据车牌蓝色范围,找出车牌所在的范围,两个坐标存入(*VL,*HL)-(*VH,*HH)
  250. void location(byte *image,int width,int height,int yuzhi,int *HL,int *HH,int *VL,int *VH)
  251. {
  252.     int i,j,n,maxnum,flag=0;
  253.     struct HSV *hsv;//float,float,int
  254.     int temp[2000]={0};
  255.     byte *temp1;
  256.     hsv=(struct HSV *)malloc(sizeof(struct HSV)*width*height);//分配HSV倍图像像素大小
  257.     hsvzation(image,hsv,width,height);//根据RGB,计算出HSV的值

  258.     //再分配一块内存,像素个
  259.     temp1=(byte *)malloc(sizeof(byte)*height*width);

  260.     for(i=0;i<height;i++)//循环高度
  261.     {
  262.        for(j=0,n=0;j<width;n+=3,j++)//循环宽度
  263.         {
  264.            if((hsv[i*width+j].H<240.0)&&(hsv[i*width+j].H>180.0)&&(hsv[i*width+j].V<250)&&(hsv[i*width+j].S>0.6))
  265.            {
  266.                temp1[i*width+j]=255;//为白色
  267.                //putpixel(j,i,RGB(255,255,255));
  268.            }
  269.            else//为黑色
  270.            {
  271.                temp1[i*width+j]=0;
  272.                //putpixel(j,i,RGB(0,0,0));
  273.            }
  274.        }
  275.     }//可以看到,本循环的意思就是把原始图像,黑白化,满足条件为白,否则通通为黑
  276.    
  277.     //去掉白块中心区域,也就是说某白点,紧挨着它的12个点全为白点,则变为黑点
  278.     edgesob8(temp1,width,height);
  279.    
  280.     for(i=0;i<height;i++)
  281.     {
  282.         temp[i]=0;
  283.         for(j=0;j<width;j++)
  284.         {
  285.             if(temp1[i*width+j]==255)
  286.             {
  287.                 temp[i]++;
  288.             }
  289.         }
  290.     }//本循环,计算每一行的白点数
  291.    
  292.     maxnum=temp[0];
  293.     for(i=1;i<height;i++)
  294.     {
  295.        if(temp[i]>maxnum)
  296.            maxnum=temp[i];
  297.     }//找到白点最多的行的白点数
  298.    
  299.     maxnum=maxnum/3;//白点数除以3
  300.     for(i=0;i<height;i++)
  301.     {
  302.         if(flag==0)
  303.         {
  304.             if(temp[i]>maxnum)//本行的白点数大于1/3最多白点数
  305.             {
  306.                 *HL=i-yuzhi;//已找到第一行满足条件的行号-yuzhi,多留出一点
  307.                 if(*HL<0)*HL=0;
  308.                 flag=1;
  309.             }
  310.         }
  311.        if(flag==1)
  312.        {
  313.            if(temp[i]==0)
  314.            {
  315.                *HH=i+yuzhi;
  316.                if(*HH>height)*HH=height;
  317.                break;
  318.            }
  319.        }
  320.     }

  321.     //计算各列的白点数
  322.         memset(temp,0x00,sizeof(int)*width);
  323.         for(i=0;i<width;i++)
  324.         {
  325.        for(j=0;j<height;j++)
  326.        {
  327.            if(temp1[j*width+i]==255)
  328.            {
  329.                temp[i]++;
  330.            }
  331.        }
  332.    }
  333.    flag=0;
  334.    maxnum=temp[0];

  335.     //找到最多白点列的白点数
  336.     for(i=1;i<width;i++)
  337.     {
  338.        if(temp[i]>maxnum)
  339.            maxnum=temp[i];
  340.     }
  341.     maxnum=maxnum/3;
  342.     for(i=0;i<width;i++)//找到满足条件的第一列
  343.     {
  344.         if(temp[i]>maxnum)
  345.         {
  346.            *VL=i-yuzhi;
  347.             if(*VL<0)*VL=0;
  348.             break;
  349.         }
  350.     }

  351.     for(i=width;i>*VL;i--)//找到满足条件的最后一列
  352.     {
  353.         if(temp[i]>maxnum)
  354.            {
  355.                *VH=i+yuzhi;
  356.                if(*VH>width)*VH=width;
  357.                break;
  358.            }
  359.     }
  360.     free(temp1);
  361.     temp1=NULL;
  362.     free(hsv);
  363. }

  364. //从img中剪切出车牌所在区域,存入img1中
  365. void CutBmp(struct BMP_img *img,struct Bmp1 *img1,int HL,int HH,int VL,int VH)
  366. {
  367.    int i,j,n;

  368.    int x=0,y=0;
  369.    img1->width=VH-VL;//新宽度
  370.    img1->height=HH-HL;//新高度
  371.    img1->image=(byte*)malloc(img1->height*img1->width*3);

  372.    for(x=0,i=HL;i<HH;x++,i++)
  373.    {
  374.        for(y=0,n=0,j=VL*3;j<VH*3;n++,j+=3,y+=3)
  375.        {
  376.            img1->image[x*img1->width*3+y]=img->image[i*img->width*3+j];
  377.            img1->image[x*img1->width*3+y+1]=img->image[i*img->width*3+j+1];
  378.            img1->image[x*img1->width*3+y+2]=img->image[i*img->width*3+j+2];
  379.        }
  380.     }
  381. }

  382. int main(int argc, char **argv)
  383. {
  384.     struct BMP_img img;//定义结构
  385.     struct Bmp1 img1;
  386.     int HL=0,HH=0,VH=0,VL=0;

  387.     //把当前文件夹下的1.bmp文件内容,读到img结构中,并上下镜像
  388.     if(read_img("1.bmp", &img)==0)
  389.     {
  390.         printf("error");
  391.         return 0;
  392.     }
  393.    
  394.     location(img.image,img.width,img.height,15,&HL,&HH,&VL,&VH);//根据蓝底找到车牌所在位置
  395.     CutBmp(&img,&img1,HL,HH,VL,VH);//剪切出车牌区域,放入img1中

  396.     //写入镜像后的数据到2.bmp中
  397.     WriteBmp24("2.bmp",img1.image,img1.width,img1.height);
  398.    
  399.     free(img.image);//释放动态分配的内存
  400.    
  401.     printf("请打开2.bmp进行查看\n");
  402.     system("pause");
  403.     return 0;
  404. }
复制代码


2.bmp
1.bmp
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-22 01:15 , Processed in 0.031482 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表