zoukankan      html  css  js  c++  java
  • 车牌识别--倾斜矫正

    在车牌识别系统中, 车牌字符能够正确分割的前提是车牌图像能够水平,以至于水平投影和垂直投影能够正常进行。如果车牌倾斜没有矫正,那么水平投影和垂直投影,甚至铆钉都无法正常处理。所以,当车辆信息中获取车牌的第一步,应该是检查倾斜角度,做倾斜矫正。

    倾斜矫正,这里使用的算法:

    1、倾斜角度检测: 霍夫变换

    关于hough变换,可以参考前面图像处理博文:

    http://blog.csdn.net/liujia2100/article/details/6989693   直线检测

    http://blog.csdn.net/liujia2100/article/details/6989688   文本图像倾斜矫正

    2、倾斜矫正: 图像旋转

    下面详细说明倾斜矫正过程:

    原车牌图像为(从车牌图像中,可以看到车牌有倾斜角度):

    1、 获取车牌在车辆中的粗略位置(可以用多种方法,这里暂不分析)

    2、提取车牌整体图片数据, 根据第一步结果,提取出,车牌在辆大体位置信息。

    关于车牌定位,我使用两部,第一步粗略定位,然后做一些预处理,比如倾斜矫正,然后第二部才是精确定位,只提取车牌的位置信息图像

    3、利用HSV颜色空间转换,获取车牌背景蓝色区域位置,获取车牌粗略信息图像后,由于车牌背景颜色与周围颜色有很明显的区别,这里采用HSV颜色过滤的方法,过滤绿色背景图像

    4、水平膨胀, 水平膨胀的目的,是为了边缘检测,只要求检测边缘,尽量除去字符信息,也可以降低hough变换的运算量

    5、水平差分运算,相当于 边缘检测,经过上面的处理后,才进行边缘检测

    6、这个时候就可以利用hough变换检测直线了。

    由于hough变换运算量十分大,所以,尽量减少图像中的白点,来降低计算量,因此前面才做了这么多步骤。

    请看下图的红线,就是检测出来的角度,为177度(Hough代码在下面)。

    7、利用旋转算法,旋转刚才粗略提取的车牌位置(旋转代码在下面),尽管旋转后的车牌有些锯齿,但是已经能够保证水平,就可以使用水平投影和垂直投影了

    这是旋转后的车牌,有些锯齿出现,由于图像分辨率较低,就没有用差值运算。

    8、精确提取车牌

    9、正常分割字符

    10、识别结果

    由于正弦余弦运算,计算量比较大,这里进行一部分优化,就是正弦余弦计算用数组代替。

    生成正弦,余弦数组的的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    int main(void)
    {
        char buf[20];
        int i;
        float p;
        float k;
        FILE *fcos;
        FILE *fsin;
        
        fcos = fopen(".\cos.txt", "wb");
        fsin = fopen(".\sin.txt", "wb");
        
        if(fcos == NULL || fsin == NULL)
        {
            printf("open error
    ");
            exit(-1);
        }
    
    
        i = 0;
    
        for(i = 0; i <= 180; i++)
        {
            k = 3.1415926 * i / 180.0;
            p = cos(k);
            
                    
            if((i%16 == 0))
                fwrite("
    ",strlen("
    "),1,fcos);
            
            sprintf(buf,"%f, ", p);
            fwrite(buf,strlen(buf),1,fcos);
    
        }
    
        for(i = 0; i <= 180; i++)
        {
            k = 3.1415926 * i / 180.0;
            p = sin(k);
            
                    
            if((i%16 == 0))
                fwrite("
    ",strlen("
    "),1,fsin);
            
            sprintf(buf,"%f, ", p);
            fwrite(buf,strlen(buf),1,fsin);
    
        }
        fclose(fcos);
        fclose(fsin);
        return 0;
    }

    生成数组为:

    float carCos[] = {
    1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, 0.970296, 0.965926, 
    0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, 0.866025, 0.857167, 
    0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, 
    0.669131, 0.656059, 0.642788, 0.629320, 0.615662, 0.601815, 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, 0.469472, 0.453991, 
    0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, 0.207912, 0.190809, 
    0.173648, 0.156434, 0.139173, 0.121869, 0.104528, 0.087156, 0.069757, 0.052336, 0.034900, 0.017452, 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, 
    -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, -0.309017, -0.325568, -0.342020, -0.358368, 
    -0.374607, -0.390731, -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, -0.587785, -0.601815, 
    -0.615661, -0.629320, -0.642788, -0.656059, -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798635, 
    -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, -0.913545, -0.920505, -0.927184, -0.933580, 
    -0.939693, -0.945519, -0.951056, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, -0.994522, -0.996195, 
    -0.997564, -0.998630, -0.999391, -0.999848, -1.000000
    };
    
    float carSin[] = {
    0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, 0.207912, 0.224951, 0.241922, 0.258819, 
    0.275637, 0.292372, 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, 0.500000, 0.515038, 
    0.529919, 0.544639, 0.559193, 0.573576, 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, 
    0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798635, 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, 0.866025, 0.874620, 0.882948, 0.891007, 
    0.898794, 0.906308, 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, 0.951056, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, 0.978148, 0.981627, 
    0.984808, 0.987688, 0.990268, 0.992546, 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, 
    0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, 
    0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, 
    0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.629320, 0.615662, 0.601815, 
    0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, 0.469472, 0.453991, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, 
    0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, 0.207912, 0.190809, 0.173648, 0.156435, 0.139173, 0.121869, 0.104529, 0.087156, 
    0.069757, 0.052336, 0.034900, 0.017452, 0.000000
    };

    旋转RGB图像的主要函数:

    image: 图像数据

    iRotateAngle: 要旋转的角度

    width, height:原始图像的宽度,高度

    lwidth,lheight:旋转后图像的宽度,高度

    unsigned char *RotateRGB(unsigned char *image, float iRotateAngle,int width,int height,int &lwidth,int &lheight)
    {
    
        int i,j,k,m,n; 
        long    lNewWidth;
        long    lNewHeight;
          float gray;
        long    i0;
        long    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;
    
    
        if(iRotateAngle >= 0)
        {
            fSina = (float)carSin[(int)iRotateAngle];
            fCosa = (float)carCos[(int)iRotateAngle];
        }
        else
        {
            fSina = 0 - (float)carSin[0 -(int)iRotateAngle];
            fCosa = (float)carCos[0 - (int)iRotateAngle];
        }
    
        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  = (long) ( max( fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2) ) + 0.5);
    
        lNewHeight = (long) ( max( fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2) )  + 0.5);
        unsigned char *temp=myMalloc(lNewHeight*lNewWidth*3,0,0);
    
        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<lNewWidth*3;m+=3,j++)
            {
                i0 = (long) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5);
                j0 = (long) ( ((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
                {
                     *(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;
        
    }

    Hough变化的主要函数

    返回值Kmax,就是检测到最长直线的角度,就是车牌的倾斜角度。

    int hough(unsigned char *srcBmp,int width,int height)
    {
        int kmax=0;
        int pmax=0;
        int yuzhi=0;
        int i,j,k,m,n,p;
        int mp = (int) (sqrt(width*width + height*height)+1);
        int ma = 180;//180
        int ap;
        int npp[180][1000];
        for(i=0;i<180;i++)
            for(j=0;j<1000;j++)
            npp[i][j]=0;
            
        for(i = 0;i < height;i++)
            for(j = 0; j < width;j++)
            {
                if(srcBmp[i * width + j]==255)
                { 
                    for(k = 0; k < ma; k++)
                    {
                        p=(int)(i * carCos[k] + j * carSin[k]);
                        p=(int)(p/2 + mp/2);
                        npp[k][p]=npp[k][p]++;
                    } 
                }
            }
            kmax=0;
            pmax=0;
            n=0;
            for(i = 0; i < ma; i++)
                for(j = 0; j < mp; j++)
                {
                    if(npp[i][j] > yuzhi)
                    {
                        yuzhi=npp[i][j];
                        kmax=i;
                        pmax=j;
                    }
                }
                for(i = 0; i < height;i++)
                    for(j = 0; j < width;j++)
                    {
                        if(srcBmp[i*width+j]==255)
                        { 
                            p=(int)(i*carCos[kmax] + j *carSin[kmax]);
                            p=(int)(p/2+mp/2);
    #if defined(DISPLAYDEBUG)
                            if(p==pmax)
                              putpixel(j,i,RGB(255,0,0));
    #endif
                        }
                    }
                    return kmax;
    }

    原文地址

  • 相关阅读:
    Android——继续深造——从安装Android Studio 2.0开始(详)
    PHP——安装wampserver丢失MSVCR110.dll
    Marza Gift for GDC 2016
    Retrieve OpenGL Context from Qt 5.5 on OSX
    Space Time Varying Color Palette
    Screen Space Depth Varying Glow based on Heat Diffusion
    Visualization of Detail Point Set by Local Algebraic Sphere Fitting
    Glass Dragon
    Jump Flood Algorithms for Centroidal Voronoi Tessellation
    京都之行
  • 原文地址:https://www.cnblogs.com/jsplyy/p/4263563.html
Copyright © 2011-2022 走看看