zoukankan      html  css  js  c++  java
  • 图像边缘检测

    一 一阶微分

       函数f(x, y)的一阶微分构成梯度grad(f):,梯度幅度mag: ,梯度方向为:,梯度方向垂直于边缘方向。

      在离散情况下,需要将一阶微分转换为一阶差分,具体如下:

       考虑一维函数g(x),其泰勒展开式为:

       求解一阶导数为:,其误差为:

       使用联合求解得:

       , 其误差为:

       基于以上分析,函数f(x, y)的梯度可离散化为:

       在实际图像边缘检测中,会对离散梯度函数进行适当扩展,这样就形成了Prewitt算子(前两组)及Sobel算子(后两组),具体如下:

      

      Sobel算子在Prewitt算子基础上改变了中心像素的权重,在编码中,一般会优先使用Sobel算子,这里有必要给出更大尺寸的Sobel算子,如5*5, 7*7等;

      如果对Prewitt算子进行更大尺寸扩展,直接扩展尺寸即可,但对于Sobel算子的扩展,由于各点权重不一致,还需要确定扩展后的权重值;仅考虑一个维度上的数据(1, 2, 1), 当扩展为更大尺寸时应该如何选择权重,如(x1, x2, x3, x4, x5)?

      这里应该采用高斯函数来确定系数;假设图像噪声为正态分布,高斯平滑被认为是最优的平滑方式;由于差分运算为线性运算,对各点应用差分后,其噪声也应该满足正态分布,使用高斯函数确定系数可以突出信号并且最大程度抑制噪声。

     高斯函数:,其能量主要集中在距离原点区间内,针对模板尺寸为5时,有,带人到高斯函数中,可求解系数

    则5*5Sobel算子为:

    使用一阶微分算子,在图像边缘上会产生较大的响应,可以通过设定合适的阈值来检测图像边缘。

    使用一阶微分检测边缘前需要使用高斯函数对图像进行滤波处理。高斯函数剔除图像高频成分保留低频部分,从而有效避免图像随机噪声干扰,如下图:

       

    二 二阶微分

       函数f(x)的一阶微分:

       函数f(x)的二阶微分:  

       函数f(x, y)的二阶偏微分构成laplace算子:

       函数f(x,y)的二阶微分构成laplace算子:,其矩阵形式如下:

       二阶微分强调灰度突变,并不 强调灰度缓慢变化区域,利用这种特性,可以将灰度突变叠加到原始图像中以实现图像边缘锐化,其方法为:

       二阶微分会产生过零点,可以通过检测二阶微分过零点来检测图像边缘。

    三 Marr-Hildreth边缘检测

       假设图像中噪声为信号无关噪声,满足平均值为0,符合正态分布,可使用如下步骤检测边缘:

        1)使用高斯滤波可以减小加性噪声的影响;

        2)在平滑后图像上应用laplace算子;

        3)检测图像零交叉点,作为图像边缘。

        将以上操作用数学表达式描述为:,其中f(x, y)为图像函数,G(x, y)为高斯函数;

        微分与卷积均为线性运算,上式可改写为:,这样就可以提前计算出(laplace of Gaussian);

       

       

       欲形成5*5拉普拉斯模板,根据公式有,带人(x, y)坐标可得出离散高斯拉普拉斯模板:

       使用高斯拉普拉斯模板与图像卷积运算,完成1)2)步操作,接下来需要检测过零点以确定图像边缘,可能的方法有:

       1)在3*3区域内检测图像左/右, 上/下或对角线符号变化,若符号发生改变,则确定为过零点;

      2)将3*3区域划分为4个象限,每个象限4个像素(同一像素可能被划分到不同的象限),求每个象限的平均值,若存在两个平均值符号不同的象限,则确定为过零点。

    四 Canny边缘检测

       canny边缘检测思路如下:

       1)使用高斯滤波剔除白噪声,使用一阶微分算子(如Sobel)与图像卷积(以上操作可先对高斯函数做一次卷积,再与图像卷积);

       2)计算卷积后图像梯度幅值与梯度方向;

       3)在梯度方向上使用非极大值抑制,细化边缘;

       4)使用滞后阈值方案(高低阈值)连接边缘,高阈值确定边缘,低阈值连接边缘。

    void cannyTrace(int y,int x,int width,int widthstep,int thresh,uchar* pImageData,int* pMag)
        {
            int y_trace[8] = {-1,-1,-1, 0,0, 1,1,1};
            int x_trace[8] = {-1, 0, 1,-1,1,-1,0,1};
            for(int k = 0;k < 8;k++)
            {
                int yy = y + y_trace[k];
                int xx = x + x_trace[k];
                if(*(pImageData + yy * widthstep + xx) == 128 && pMag[yy * width + xx] > thresh)
                {
                    *(pImageData + yy * widthstep + xx) = 255;
                    cannyTrace(yy,xx,width,widthstep,thresh,pImageData,pMag);
                }
            }
        }
    
        void cannyGradHV(unsigned char* img, int* dx, int* dy, int* mag, float* theta, int width, int height)
        {
            int widthstep = (width + 3) / 4 * 4;
    
            for(short y = 1; y < height - 1; ++y)
            {
                for(short x = 1; x < width - 1; ++x)
                {
                    *(dx + y * width + x) =  *(img + (y - 1) * widthstep + x - 1)
                                           + *(img + (y    ) * widthstep + x - 1) * 2
                                           + *(img + (y + 1) * widthstep + x - 1)
                                           - *(img + (y - 1) * widthstep + x + 1)
                                           - *(img + (y    ) * widthstep + x + 1) * 2
                                           - *(img + (y + 1) * widthstep + x + 1);
    
                    *(dy + y * width + x) = *(img + (y - 1) * widthstep + x - 1)
                                          + *(img + (y - 1) * widthstep + x    ) * 2
                                          + *(img + (y - 1) * widthstep + x + 1)
                                          - *(img + (y + 1) * widthstep + x - 1)
                                          - *(img + (y + 1) * widthstep + x    ) * 2
                                          - *(img + (y + 1) * widthstep + x + 1);
    
                    *(mag   + y * width + x) = abs(*(dx + y * width + x)) + abs(*(dy + y * width + x));
                    *(theta + y * width + x) = atan2((float)(*(dy + y * width + x)),(float)(*(dx + y * width + x)));
                }
            }
        }
    void CannyCustomized(IplImage* pImage, IplImage* pCanny, float upper_threshold, float lower_threshold, bool hysteresis,  CANNY_TPYE type)
        {
    
            IplImage* pFilterImage     = cvCreateImage(cvGetSize(pImage),IPL_DEPTH_8U,1);
            uchar*    pFilterImageData = (uchar*)pFilterImage->imageData;
            
            //cvSmooth(pImage,pFilterImage,CV_GAUSSIAN,5,1);
            cvCopy(pImage, pFilterImage);
    
            int width  = pImage->width;
            int height = pImage->height;
            int widthstep = pImage->widthStep;
            
            // 梯度值与梯度方向
            int*  dx = new int[pImage->width * pImage->height];
            int*  dy = new int[pImage->width * pImage->height];
            int* mag = new int[pImage->width * pImage->height];
            float* theta = new float[pImage->width * pImage->height];
           
            switch(type)
            {
                case CANNY_HORIZONTAL_VERTICAL: cannyGradHV(pFilterImageData, dx, dy, mag, theta, pImage->width, pImage->height); break;
                case CANNY_HORIZONTAL:          cannyGradH(pFilterImageData, dx, dy, mag, theta, pImage->width, pImage->height);  break; 
                case CANNY_VERTICAL:            cannyGradV(pFilterImageData, dx, dy, mag, theta, pImage->width, pImage->height);  break; 
                case CANNY_CUSTOM:              cannyGradC(pFilterImageData, dx, dy, mag, theta, pImage->width, pImage->height);  break; 
                default:                        cannyGradHV(pFilterImageData, dx, dy, mag, theta, pImage->width, pImage->height);
            }
            
            // 非极大值抑制
            IplImage* pRestrainImage     = cvCreateImage(cvGetSize(pImage),IPL_DEPTH_8U,1);
            uchar*    pRestrainImageData = (uchar*)pRestrainImage->imageData;
            cvZero(pRestrainImage);
    
            for(short y = 1; y < pFilterImage->height - 1; y++)
            {
                for(short x = 1; x < pFilterImage->width - 1; x++)
                {
                    int index = y * width + x;
    
                    if(mag[index] > 0)
                    {
                        int temp1 = 0;
                        int temp2 = 0;
    
                        /*        以下为图像梯度示意
                         *          g1  g2
                         *              c
                         *              g3  g4
                         *        c为当前素 梯度角度从g1 g2 之间 到 g3 g4 之间
                         */
                        if((theta[index] >=  CV_PI / 2 && theta[index]  <  CV_PI * 3 / 4) || 
                           (theta[index] >= -CV_PI / 2 && theta[index]  < -CV_PI / 4    )){
                               
                               int g1 = mag[index + width - 1];
                               int g2 = mag[index + width    ];
                               int g3 = mag[index - width    ];
                               int g4 = mag[index - width + 1];
    
                               double weight = fabs((double)dx[index] / (double)dy[index]);
    
                               temp1 = g1 * weight + g2 * (1.0 - weight);
                               temp2 = g4 * weight + g3 * (1.0 - weight);
    
                        /*        以下为图像梯度示意
                         *          g1  
                         *          g2  c   g3
                         *                  g4
                         *        c为当前素 梯度角度从g1 g2 之间 到 g3 g4 之间
                         */
                        }else if((theta[index] >=  CV_PI * 3 / 4 && theta[index] < CV_PI) ||
                            (theta[index] >= -CV_PI / 4     && theta[index] < 0 )){
                               int g1 = mag[index + width - 1];
                               int g2 = mag[index - 1        ];
                               int g3 = mag[index + 1        ];
                               int g4 = mag[index - width + 1];
    
                               double weight = fabs((double)dy[index] / (double)dx[index]);
    
                               temp1 = g1 * weight + g2 * (1.0 - weight);
                               temp2 = g4 * weight + g3 * (1.0 - weight);
    
                        /*        以下为图像梯度示意
                         *                  g1
                         *          g3  c   g2
                         *          g4      
                         *        c为当前素 梯度角度从g1 g2 之间 到 g3 g4 之间
                         */
                        }else if((theta[index] >= 0   && theta[index] < CV_PI / 4) ||
                            (theta[index] >= -CV_PI && theta[index] < -CV_PI * 3 / 4)){
                               int g1 = mag[index + width + 1];
                               int g2 = mag[index + 1        ];
                               int g3 = mag[index - 1        ];
                               int g4 = mag[index - width - 1];
    
                               double weight = fabs((double)dy[index] / (double)dx[index]);
    
                               temp1 = g1 * weight + g2 * (1.0 - weight);
                               temp2 = g4 * weight + g3 * (1.0 - weight);
    
                         /*        以下为图像梯度示意
                         *              g2  g1
                         *              c   
                         *          g4  g3    
                         *        c为当前素 梯度角度从g1 g2 之间 到 g3 g4 之间
                         */
                        }else if((theta[index] >= CV_PI / 4   && theta[index] < CV_PI / 2) ||
                            (theta[index] >= -CV_PI * 3 / 4 && theta[index] < -CV_PI / 2)){
                               int g1 = mag[index + width + 1];
                               int g2 = mag[index + width    ];
                               int g3 = mag[index - width    ];
                               int g4 = mag[index - width - 1];
    
                               double weight = fabs((double)dx[index] / (double)dy[index]);
    
                               temp1 = g1 * weight + g2 * (1.0 - weight);
                               temp2 = g4 * weight + g3 * (1.0 - weight);
                        }
                        
                        // 标记128为候选边缘
                        if(mag[index] >= temp1 && mag[index] >= temp2)
                            *(pRestrainImageData + y * widthstep + x) = 128;
                    }
                }
            }
            
            // 确定高低阈值
            short upper = upper_threshold;
            short lower = lower_threshold;
            if(upper_threshold < 1.0 || lower_threshold < 1.0)
            {
                int hist[1024];
                memset(hist, 0, sizeof(int) * 1024);
    
                for(short y = 1; y < pFilterImage->height - 1; y++)
                {
                    for(short x = 1; x < pFilterImage->width - 1; x++)
                    {
                        ++hist[mag[y * width + x]];
                    }
                }
                
                int threshold = (pFilterImage->width - 2) * (pFilterImage->height - 2) * 0.85;
                if(upper_threshold > 0.01) threshold = (pFilterImage->width - 2) * (pFilterImage->height - 2) * upper_threshold;
    
                int sum = 0;
                for(short i = 0; i < 1024; ++i)
                {
                    sum += hist[i];
                    if(sum > threshold)
                    {
                        upper = i;
                        lower = upper * 0.5;
                        if(lower_threshold > 0.01)
                            lower = upper * lower_threshold;
    
                        break;
                    }
                }
            }
            
            // 根据高低阈值标记边缘
            if(hysteresis)
            {
                for(short y = 0; y < pFilterImage->height; ++y)
                {
                    for(short x = 0; x < pFilterImage->width; ++x)
                    {
                        if(*(pRestrainImageData + y * widthstep + x) == 128 && mag[y * width + x] > upper)
                        {
                            *(pRestrainImageData + y * widthstep + x) = 255;
                            cannyTrace(y,x,width,widthstep,lower,pRestrainImageData,mag);
                        }
                    }
                }
            }
            else
            {
                for(short y = 0; y < pFilterImage->height; ++y)
                {
                    for(short x = 0; x < pFilterImage->width; ++x)
                    {
                        if(*(pRestrainImageData + y * widthstep + x) == 128 && mag[y * width + x] > upper)
                            *(pRestrainImageData + y * widthstep + x) = 255;
                    }
                }
            }
            
            // 剔除无效边缘
            for(short y = 0; y < pFilterImage->height; ++y)
            {
                for(short x = 0; x < pFilterImage->width; ++x)
                {
                    if(*(pRestrainImageData + y * widthstep + x) != 255)
                        *(pRestrainImageData + y * widthstep + x) = 0;
                }
            }
    
            cvCopy(pRestrainImage, pCanny);
    
            delete []dx;
            delete []dy;
            delete []mag;
            delete []theta;
            cvReleaseImage(&pFilterImage);
            cvReleaseImage(&pRestrainImage);
    
        }
  • 相关阅读:
    part11-1 Python图形界面编程(Python GUI库介绍、Tkinter 组件介绍、布局管理器、事件处理)
    part10-3 Python常见模块(正则表达式)
    Cyclic Nacklace HDU
    模拟题 Right turn SCU
    状态DP Doing Homework HDU
    Dp Milking Time POJ
    区间DP Treats for the Cows POJ
    DP Help Jimmy POJ
    Dales and Hills Gym
    Kids and Prizes Gym
  • 原文地址:https://www.cnblogs.com/luofeiju/p/9304282.html
Copyright © 2011-2022 走看看