图像边缘提取算法

Canny边缘检测算法

参考:http://blog.csdn.net/jia20003/article/details/41173767

经典的Canny边缘检测算法通常都是从高斯模糊开始,到基于双阈值实现边缘连接结束。但是在实际工程应用中,考虑到输入图像都是彩色图像,最终边缘连接之后的图像要二值化输出显示,所以完整的Canny边缘检测算法实现步骤如下:

1.      彩色图像转换为灰度图像

2.      对图像进行高斯模糊

3.      计算图像梯度,根据梯度计算图像边缘幅值与角度

4.      非最大信号压制处理(边缘细化)

5.      双阈值边缘连接处理

6.      二值化图像输出结果

1、对图像进行高斯模糊,实现图形的平滑,去除尖锐噪声

定义:高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。

高斯滤波的相关解释:http://blog.csdn.net/hhygcy/article/details/43290

void gaussianFilter2 (int width, int height)        //高斯滤波器  {
    int templates[25] = { 1, 4, 7, 4, 1,                  //高斯滤波窗口
        4, 16, 26, 16, 4,
        7, 26, 41, 26, 7,
        4, 16, 26, 16, 4,
        1, 4, 7, 4, 1 };
    memcpy(smooth, gray, width*height*sizeof(int) );         //给smooth赋值gray
    for (int j=2;j<height-2;j++)
    {
        for (int i=2;i<width-2;i++)                         //i,j用于循环gray中每一个像素(除去图像的边缘)
        {
            int sum = 0;
            int index = 0;
            for ( int m=j-2; m<j+3; m++)
            {
                for (int n=i-2; n<i+3; n++)
                {
                    sum += gray[ m*width + n] * templates[index++] ;
                }
            }
            sum /= 273;                      //sum等于使用高斯滤波平滑后的像素值                          
            if (sum > 255)
                sum = 255;
            smooth[ j*width+i ] = sum;
        }
    }
}

2、梯度求解函数

void gradient(int width, int height)
{
    for (int row = 0; row < height-1; row++)
    {
        for (int col = 0; col < width-1; col++)
        {
            int index = row * width + col;
            // 计算X方向梯度
            //float xg = 0.5*(smooth[(row+1)*width+col]- smooth[row*width+col]+ smooth[(row+1)*width+col+1]- smooth[row*width+col]); //此处算法有点错误修改如下
            float xg = 0.5*(smooth[(row+1)*width+col]- smooth[row*width+col]+ smooth[(row+1)*width+col+1]- smooth[row*width+col+1]);
            float yg =  0.5*(smooth[row*width+col]- smooth[row*width+col+1]+ smooth[(row+1)*width+col]- smooth[(row+1)*width+col+1]);
            // 计算振幅与角度
            data[index] =sqrt(xg*xg+yg*yg);
            if(xg == 0)
            {
                if(yg > 0)
                {
                    magnitudes[index]=90;
                }
                if(yg < 0)
                {
                    magnitudes[index]=-90;
                }
            }
            else if(yg == 0)
            {
                magnitudes[index]=0;
            }
            else
            {
                magnitudes[index] = (float)((atan(yg/xg) * 180)/3.1415926);
            }
            // make it 0 ~ 180
            magnitudes[index] += 90;
        }
    }
}

3、非最大信号压制(边缘细化)

信号压制本来是数字信号处理中经常用的,这里的非最大信号压制主要目的是实现边缘细化,通过该步处理边缘像素进一步减少。非最大信号压制主要思想是假设3x3的像素区域,中心像素P(x,y) 根据上一步中计算得到边缘角度值angle,可以将角度分为四个离散值0、45、90、135分类依据如下:

其中黄色区域取值范围为0~22.5 与157.5~180

绿色区域取值范围为22.5 ~ 67.5

蓝色区域取值范围为67.5~112.5

红色区域取值范围为112.5~157.5

分别表示上述四个离散角度的取值范围。得到角度之后,比较中心像素角度上相邻

两个像素,如果中心像素小于其中任意一个,则舍弃该边缘像素点,否则保留。一

个简单的例子如下:

void  byxh(int width, int height)                                     // 非最大信号压制
{
    for (int row = 1; row < height-1; row++) {
        for (int col = 1; col < width-1; col++) {
            int index = row * width + col;
            float angle = magnitudes[index];
            float m0 = data[index];
            magnitudes[index] = m0;
            if(angle >=0 && angle < 22.5)                        // angle 0
            {
                float m1 =data[row*width+col-1];
                float m2 = data[row*width+col+1];
                if(m0 < m1 || m0 < m2)
                {
                    magnitudes[index] = 0;
                }
            }
            else if(angle >= 22.5 && angle < 67.5)                        // angle +45
            {
                float m1 = data[(row-1)*width+col+1];
                float m2 = data[(row+1)*width+col-1];
                if(m0 < m1 || m0 < m2)
                {
                    magnitudes[index] = 0;
                }
            }
            else if(angle >= 67.5 && angle < 112.5)                // angle 90
            {
                float m1 = data[(row+1)*width+col];
                float m2 = data[(row-1)*width+col];
                if(m0 < m1 || m0 < m2)
                {
                    magnitudes[index] = 0;
                }
            }
            else if(angle >=112.5 && angle < 157.5)                // angle 135 / -45
            {
                float m1 = data[(row-1)*width+col-1];
                float m2 = data[(row+1)*width+col+11];
                if(m0 < m1 || m0 < m2)
                {
                    magnitudes[index] = 0;
                }
            }
            else if(angle >=157.5) // angle 0
            {
                float m1 = data[(row+1)*width+col];
                float m2 =data[(row-1)*width+col];
                if(m0 < m1 || m0 < m2)
                {
                    magnitudes[index] = 0;
                }
            }
        }
    }
}

4、 模糊阈值和边缘连接

非最大信号压制以后,输出的幅值如果直接显示结果可能会少量的非边缘像素被包含到结果中,所以要通过选取阈值进行取舍,传统的基于一个阈值的方法如果选择的阈值较小起不到过滤非边缘的作用,如果选择的阈值过大容易丢失真正的图像边

缘,Canny提出基于双阈值(Fuzzy threshold)方法很好的实现了边缘选取,在实际应用中双阈值还有边缘连接的作用。双阈值选择与边缘连接方法通过假设两个阈值其中一个为高阈值TH另外一个为低阈值TL则有:

a.      对于任意边缘像素低于TL的则丢弃

b.      对于任意边缘像素高于TH的则保留

c.      对于任意边缘像素值在TL与TH之间的,如果能通过边缘连接到一个像素大于

TH而且边缘所有像素大于最小阈值TL的则保留,否则丢弃。代码实现如下:

void FuzzyThreshold(int width, int height)
{
    float lowThreshold =1.5;
    float highThreshold =3.75;    //通过改变lowThreshold、highThreshold两个值来修正提取效果
    int offset = 0;
    for(int i=0;i<width*height;i++)
        data[i]=0;

    for (int row = 0; row < height; row++) {
        for (int col = 0; col < width; col++) {
            if(magnitudes[offset] >= highThreshold && data[offset] == 0)
            {
                edgeLink(width, height,col, row, offset, lowThreshold);
            }
            offset++;
        }
    }
}

void edgeLink(int width, int height,int x1, int y1, int index, float threshold) {
    int x0 = (x1 == 0) ? x1 : x1 - 1;
    int x2 = (x1 == width - 1) ? x1 : x1 + 1;
    int y0 = y1 == 0 ? y1 : y1 - 1;
    int y2 = y1 == height -1 ? y1 : y1 + 1;
    data[index] = magnitudes[index];
    for (int x = x0; x <= x2; x++) {
        for (int y = y0; y <= y2; y++) {
            int i2 = x + y * width;
            if ((y != y1 || x != x1)
                && data[i2] == 0
                && magnitudes[i2] >= threshold)
            {
                edgeLink(width, height,x, y, i2,threshold);
                return;
            }
        }
    }
}

5、图像二值化

void arrayToImg(int width, int height,int bitcount)//二值化
{
    DWORD dwLineBytes=GetLineBytes(width,bitcount);
    int k=0;
    BYTE s;
    for(int i=0;i<height;i++)
    {
        for(int j=0;j<width*3;j++)
        {
            s=data[k];
            *(imgData+dwLineBytes*(height-1-i)+j)=data[k];
            j++;
            *(imgData+dwLineBytes*(height-1-i)+j)=data[k];;
            j++;
            *(imgData+dwLineBytes*(height-1-i)+j)=data[k];;
            k++;
        }
    }
}
时间: 2024-10-05 06:35:18

图像边缘提取算法的相关文章

Matlab实现:图像边缘提取

1. 边缘提取算法 方法一:一阶微分算子 Sobel算子 Sobel算子检测方法对灰度渐变和噪声较多的图像处理效果较好,Sobel算子对边缘定位不是很准确,图像的边缘不止一个像素. Roberts算子 Roberts算子检测方法对具有陡峭的低噪声的图像处理效果较好,但是利用roberts算子提取边缘的结果是边缘比较粗,因此边缘的定位不是很准确. Prewitt算子 Prewitt算子检测方法对灰度渐变和噪声较多的图像处理效果较好.但边缘较宽,而且间断点多. Canny算子 Canny算子是目前边

基于matlab的经典图像边缘检测算法

图像边缘检测算法 (1)Robert算子边缘检测 (2)Sobel算子边缘检测 (3)Prewitt算子边缘检测 (4)LOG算子边缘检测 (5)Canny边缘检测 Matlab的实现. 其实还只是掉包侠,一点算法没有写 争取有空用openCV写一遍 I=imread('1.jpg'); I0=rgb2gray(I); subplot(231); imshow(I); BW1=edge(I0,'Roberts',0.16); subplot(232); imshow(BW1); title('R

图像缩放算法

图像缩放算法较多,下面仅以最邻近插值算法和双线性插值算法作介绍. 如下图1所示,表示原始图像和缩放以后的图像. 图1 图像缩放(原始图像à缩放图像) 图像缩放就是将原始图像中的点经过某一算法映射到目标图像的点的行为,即要找到目标图像中的点p1对应在原始图像中点p0,简单而言就是找点p0. 假设: 原始图像src的分辨率为(srcW * srcH): 目标图像dst的分辨率为(dstW * dstH). 那么: 原始图像宽与目标图像宽的比例 原始图像高与目标图像高的比例 由 所以,原始图像中的点p

图像放大算法

http://www.cnblogs.com/celerychen/archive/2010/11/25/3588222.html 一. 图像放大算法 图像放大有许多算法,其关键在于对未知像素使用何种插值方式.以下我们将具体分析几种常见的算法,然后从放大后的图像是否存在色彩失真,图像的细节是否得到较好的保存,放大过程所需时间是否分配合理等多方面来比较它们的优劣. 当把一个小图像放大的时候,比如放大400%,我们可以首先依据原来的相邻4个像素点的色彩值,按照放大倍数找到新的ABCD像素点的位置并进

图像融合算法(归纳篇)

综合关注几篇的papers的图像融合算法,对整个过程作归纳,与大家分享(^_^).基于sift特征的全景拼接方法的整个过程的大致流程: 对需拼接的图像进行预处理,主要是几何校正和消噪.对于几何校正,因为我们考虑的是视频的实时处理,那么我们只需考虑摄像机的所有运动形式,其中包含8个自由度,可用投影变换来表示.H=[m0 m1 m2;m3 m4 m5;m6 m7 1],考虑到它的算法复杂度已经有n的3次方,我们可以考虑通过控制摄像机的运动方式来减少复杂度,比如令摄像机只有平移旋转和缩放,即仿射变换,

最简单的分形图像生成算法

本文将提供一段完整地生成一幅分形图像文件的C语言代码,并且极为简单.我相信这应该是最简单的分形图像生成算法.大部分的分形图像代码也都很短,但一有递归迭代就难以理解了.而这段代码则很好懂,并且其生成的图像会使人意想不到. #include <iostream> #include <cmath> #include <cstdlib> #define DIM 1000 void pixel_write(int,int); FILE *fp; int main() { fp =

图像融合算法(感应篇)

复按照几个papers图像融合算法,诱导整个过程,与您分享(^_^). 基于sift的全景拼接方法的整个过程的大致流程: 对需拼接的图像进行预处理.主要是几何校正和消噪.对于几何校正.因为我们考虑的是视频的实时处理,那么我们仅仅需考虑摄像机的全部运动形式,当中包括8个自由度.可用投影变换来表示.H=[m0 m1 m2;m3 m4 m5;m6 m7 1],考虑到它的算法复杂度已经有n的3次方.我们能够考虑通过控制摄像机的运动方式来减少复杂度,比方令摄像机仅仅有平移旋转和缩放,即仿射变换,减少了一次

图像缩放算法【转】

转自:http://blog.csdn.net/qq_21792169/article/details/51020005 版权声明:本文为Linux_Google原创文章,转载请加上原创链接. 转载别人的,但是这篇文章写得确实太好了,所以想分享出来,可是原创文章地址找不到了 ,很可惜. 图像缩放算法 摘要:首先给出一个基本的图像缩放算法,然后一步一步的优化其速度和缩放质量: 高质量的快速的图像缩放 全文 分为:      上篇 近邻取样插值和其速度优化      中篇 二次线性插值和三次卷积插值

C#写的图像细化算法

自己用C#写的图像细化算法,输入图像为Bitmap类型,输出也是同样的类型,注意<pre name="code" class="csharp">ToThinner(Bitmap srcImg) 中的输入图像srcImg必须为像素0和255的二值化的图像. public unsafe Bitmap ToThinner(Bitmap srcImg)         {             int iw = srcImg.Width;