乘简 发表于 2018-8-2 11:55:49

车牌识别(三)旋转校正

本帖最后由 乘简 于 2018-8-2 13:08 编辑

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define pi 3.14159265
#define RADIAN(angle) ((angle)*pi/180.0)

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);
}

//把二值图保存为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);
}

//把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经过蓝低换算过的黑白图,为了消除锯齿,有利于计算旋转角度
void Dilation(byte *image,int width,int height,int type,int num)
{
    int dwWidth=width;
    int dwHeight=height;

    int i=0;
    int j=0;

    BYTE *temp;
    int l;
    int k=0;

    temp=(BYTE*)malloc(dwHeight*dwWidth*sizeof(BYTE));
    memcpy(temp,image,dwHeight*dwWidth*sizeof(byte));
    memset(temp,0,dwWidth*dwHeight*sizeof(BYTE));

    if(type==0)
    {
      //水平方向
      for(i=0;i<dwHeight;i++)
      {
            for(j=(num-1)/2;j<dwWidth-(num-1)/2;j++)
            {
                l=dwWidth*i+j;//当前点的位置
                for(k=-(num-1)/2;k<=(num-1)/2;k++)
                {
                  if(image==255)//当前点的前后5点内(num为5时)有一个白点,则当前点为白色
                  {
                        temp=255;
                        break;
                  }
                }
            }
      }
    }
    else
    {
      //垂直方向(i=2 ~ height-2)
      for(i=(num-1)/2;i<dwHeight-(num-1)/2;i++)
      {
            for(j=0;j<dwWidth;j++)
            {
                for(k=-(num-1)/2;k<=(num-1)/2;k++)//-2~2,正好num次(当num为5时)
                {
                  if(image==255)//上下5点内有白色,则本点为白色
                  {
                        temp=255;
                        break;
                  }
                }
            }
      }
    }
    memcpy(image,temp,sizeof(byte)*width*height);
    free(temp);
    temp=NULL;
}

//type为0时,当前与上一比较,相等则为0,不相等,则为白色
//从第一行有白色开始,保留其白色,第二行清掉,。。。
//最后一行,也清掉,最后一行的下一行,保留期白色
void sob(byte *srcBmp,int width,int height,int type)//sob算子边缘检测;取出边界;
{
    int i,j,l,l1;
    int p1;
    byte *tempy;
    byte *tempx;
    tempx=(byte *)malloc(sizeof(byte)*width*height);
    if(tempx==NULL)
      exit(-1);
    tempy=(byte *)malloc(sizeof(byte)*width*height);
    if(tempy==NULL)
      exit(-1);

    for (i = 0; i < width; i++)//第一行与最后一行清零
    {
      tempx = 0;
      tempy = 0;
      tempx = 0;
      tempy = 0;
    }
    for (i = 0; i < height; i++)//第一列与最后一列清零
    {
      tempx = 0;
      tempy = 0;
      tempx = 0;
      tempy = 0;
    }

    for(i =1; i<height-1; i++)//少循环两行,最上面的行与最下面的行
    {
      l=i*width;
      l1=(i+1)*width;
      for(j=1;j<width-1;j++)//由于使用3×3的模板,为防止越界,所以不处理最下边和最右边的两列像素
      {
            p1 = srcBmp;//当前点
            p1 = srcBmp;//后一点
            p1 = srcBmp;//下一行当前点

            if(p1==p1)tempx=0;else tempx=255;//与下一点比较,如果不全为白色,则x=255

            if(p1==p1)tempy=0;else tempy=255;//与后一点比较,不全为白色,相等为0
      }
    }

    if(type==0)//保留水平线
       memcpy(srcBmp,tempx,sizeof(byte)*width*height);
    if(type==1)//只保留垂直线
       memcpy(srcBmp,tempy,sizeof(byte)*width*height);
    if(type==2)//合并二者的线,就是轮廓
    {
      for(i=0;i<height;i++)
      {
            for(j=0;j<width;j++)
            {
                tempx|=tempy;
            }
      }
      memcpy(srcBmp,tempx,sizeof(byte)*width*height);
    }
    free(tempx);
    free(tempy);
    tempx=NULL;
    tempy=NULL;
}

//检测到最长直线的角度,就是车牌的倾斜角度。
int hough(byte *srcBmp,int width,int height)
{
    int kmax=0;
    int yuzhi=0;
    int i,j,k,p;
    float c;
    int mp= (int) (sqrt(width*width + height*height)+0.5);//计算对角线的长
    int ma=180;//180
    int npp;
   
    //清零npp
    for(i=0;i<180;i++)
    for(j=0;j<1000;j++)
    npp=0;

    /*ap=90*(p+mp);
    int *npp=(int *)malloc(sizeof(int)*ap);//myMalloc(ap,0);
    memset(npp,0x00,sizeof(int)*ap);*/

    for(i=1;i<height;i++)//跳过第0行循环
    {
      for(j=1;j<width;j++)//跳过第0列循环
      {
            if(srcBmp==255)//如果当前点为白色
            {
                for(k=1;k<ma;k++)//1~179度
                {
                  c=pi*k/180.0;//k度的弧长(弧度,r=1),取值范围为(0,pi)之间
                  p=(int)(i*cos(c)+j*sin(c));//cos(c)=(-1~+1)之间,sin(c)=(0~1)之间
                  p=(int)(p/2+mp/2);
                  npp=npp+1;
                }
            }
      }
    }
   
    kmax=0;
    for(i=1;i<ma;i++)//1~179度之间遍历
    {
      for(j=1;j<mp;j++)
      {
            if(npp>yuzhi)//大于域值
            {
                yuzhi=npp;//找到最大域值
                kmax=i;//记下度数
            }
      }
    }
    return kmax;
}

void Hough(struct Bmp1 *img)
{
    DWORD i,j;
    float ang;
    struct HSV *hsv=(struct HSV *)malloc(sizeof(struct HSV)*img->width*img->height);
    hsvzation(img->image,hsv,img->width,img->height);//计算HSV
    byte *temp=(byte *)malloc(img->height*img->width);
   
    for(i=0;i<img->height;i++)
    {
       for(j=0;j<img->width;j++)
      {
         if((hsv.H<230.0)&&(hsv.H>180.0)&&(hsv.V<250)&&(hsv.S>0.6))
         {
            temp=255;
         }
         else
         {
               temp=0;
         }
       }
    }
   
    WriteBmp1("2.bmp",temp,img->width,img->height);//膨胀前
    //膨胀,垂直方向,尺寸5,消除水平线的锯齿
    Dilation(temp,img->width,img->height,1,5);
    WriteBmp1("3.bmp",temp,img->width,img->height);//膨胀后
   
    //留保最上面的连线与最下面的连线,所以最后一个参数为0即可,节省时间
    sob(temp,img->width,img->height,0);
    WriteBmp1("4.bmp",temp,img->width,img->height);//连缘检测后的图像
   
    //计算出角度
    ang=hough(temp,img->width,img->height);
   
    if(ang==1 || ang==179)ang=0;//如果只有1度的角度,可以不用旋转
    if(ang>90)//让其值保持在+-90之间
    {
      ang=ang-180;
    }
    img->ang=ang;
   
    free(hsv);
    free(temp);
}

//旋转图片
byte *RotateRGB(byte *image, float iRotateAngle,int width,int height,int *lwidth,int *lheight)
{
    byte *temp;
    // 循环变量
    int i,j,m,n;
    // 旋转后图像的宽度和高度
    int lNewWidth,lNewHeight;
    // 象素在源DIB中的坐标
    int i0,j0;
    // 旋转角度(弧度)
    float    fRotateAngle;
    // 旋转角度的正弦和余弦
    float    fSina, fCosa;
    // 源图四个角的坐标(以图像中心为坐标系原点)
    float    fSrcX1,fSrcY1,fSrcX2,fSrcY2,fSrcX3,fSrcY3,fSrcX4,fSrcY4;
    // 旋转后四个角的坐标(以图像中心为坐标系原点)
    float    fDstX1,fDstY1,fDstX2,fDstY2,fDstX3,fDstY3,fDstX4,fDstY4;
    // 两个中间常量
    float    f1,f2;

    // 将旋转角度从度转换到弧度
    fRotateAngle = (float) RADIAN(iRotateAngle);
    // 计算旋转角度的正弦
    fSina = (float) sin((double)fRotateAngle);
    // 计算旋转角度的余弦
    fCosa = (float) cos((double)fRotateAngle);
    // 计算原图的四个角的坐标(以图像中心为坐标系原点)
    fSrcX1 = (float) (- (width- 1) / 2);
    fSrcY1 = (float) ((height - 1) / 2);
    fSrcX2 = (float) ((width- 1) / 2);
    fSrcY2 = (float) ((height - 1) / 2);
    fSrcX3 = (float) (- (width- 1) / 2);
    fSrcY3 = (float) (- (height - 1) / 2);
    fSrcX4 = (float) ((width- 1) / 2);
    fSrcY4 = (float) (- (height - 1) / 2);

    // 计算新图四个角的坐标(以图像中心为坐标系原点)
    fDstX1 =fCosa * fSrcX1 + fSina * fSrcY1;
    fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1;
    fDstX2 =fCosa * fSrcX2 + fSina * fSrcY2;
    fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2;
    fDstX3 =fCosa * fSrcX3 + fSina * fSrcY3;
    fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3;
    fDstX4 =fCosa * fSrcX4 + fSina * fSrcY4;
    fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4;
    // 计算旋转后的图像实际宽度
    lNewWidth= (int) ( max( fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2) ) + 0.5);

    // 计算旋转后的图像高度
    lNewHeight = (int) ( max( fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2) )+ 0.5);

    //分配内存,用于保存新图片
    temp=(byte*)malloc(lNewHeight*lNewWidth*3);
    // 两个常数,这样不用以后每次都计算了
    f1 = (float) (-0.5 * (lNewWidth - 1) * fCosa - 0.5 * (lNewHeight - 1) * fSina + 0.5 * (width- 1));
    f2 = (float) ( 0.5 * (lNewWidth - 1) * fSina - 0.5 * (lNewHeight - 1) * fCosa + 0.5 * (height - 1));

    for(i = 0; i < lNewHeight; i++)
    {
      // 针对图像每列进行操作
      for(m=0,j = 0;j < lNewWidth;m+=3,j++)
      {

            // 计算该象素在源DIB中的坐标
            i0 = (int) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5);
            j0 = (int) ( ((float) j) * fCosa + ((float) i) * fSina + f1 + 0.5);

            // 判断是否在源图范围内
            if( (j0 >= 0) && (j0 < width) && (i0 >= 0) && (i0 < height))
            {
                n=i0*width*3+j0*3;
                *(temp + lNewWidth * i*3 + m+1)=*(image + n+1);
                *(temp + lNewWidth * i*3 + m+2)=*(image + n+2);
                *(temp + lNewWidth * i*3+ m)=*(image + n);
            }
            else
            {
                // 对于源图中没有的象素,直接赋值为0
               *(temp + lNewWidth * i*3+ m+1)=0;
               *(temp + lNewWidth * i*3+ m+2)=0;
               *(temp + lNewWidth * i*3+ m)=0;
            }
      }
    }
    *lwidth=lNewWidth;
    *lheight=lNewHeight;
    return temp;
}

//旋转
void xuanzhuan(struct Bmp1 *img)
{
    int lwidth=0,lheight=0;
   
    byte *p;
    p=RotateRGB(img->image,img->ang,img->width,img->height,&lwidth,&lheight);
    img->width=lwidth;
    img->height=lheight;
    free(img->image);//释放原来的内存
    img->image=p;
}

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中
    img1.image=(byte*)malloc(img.datasize);
    memcpy(img1.image,img.image,img.datasize);
    img1.width=img.width;
    img1.height=img.height;
   
    Hough(&img1);//计算出角度
    xuanzhuan(&img1);//旋转

    //写入镜像后的数据到2.bmp中
    WriteBmp24("5.bmp",img1.image,img1.width,img1.height);
   
    free(img.image);//释放动态分配的内存
    free(img1.image);
   
    printf("请打开2 3 4 5.bmp进行查看\n");
    system("pause");
    return 0;
}


页: [1]
查看完整版本: 车牌识别(三)旋转校正