Canny 算子 c++

、功能简介及其运行

(一)、功能:该程序实现了canny算子求图片的边缘,其中主要包括七大部分:

1、对传入的彩色图片二值化

2、对二值化后的图片进行高斯滤波

3、使用sobel算子对滤波之后的图形分别求x,y方向上的梯度

4、计算出梯度幅值

5、对梯度图像做非极大抑制

6、对非极大抑制后的图像做双阈值处理

7、对双阈值处理后的图像进行连接操作(处理阈值中间的点)

(二)、运行:注意要在release文件夹下运行exe

运行程序后终端会显示所用到的sobel算子矩阵和高斯滤波矩阵,同时会弹出多张图片,他包括:

1、原图片:Lena.jpg

2、二值化后的图片:gray.jpg

3、高斯滤波后的图片:guss,jpg

4、x,y方向梯度的图片:gradit_x.jpg gradint_y.jpg

5、梯度图片:gradint.jpg

6、非极大抑制后的图片:afterNms.jpg

7、双阈值后的图片:afterDT.jpg

8、连接操作后的图片(即最终的canny算法图片)canny.jpg

9、使用opencv自带函数得到的canny算子图片(用于对比)

弹出的同时,会将这些图片保存在当前路径的文件下,命名为上面的命名。

二、开发环境

操作系统:windows

Opencv版本:2.4

编译器:vs2015

语言:c++

请助教运行release下的exe。

三、程序思路

大体思路其实与上面七大部分相同,这里做一个概述,并说明大体实现,具体通过这个简单流程图来说明(流程图viso文件附在压缩包内))

一、具体实现

(1)图像灰度化:

图像灰度画函数:Mat getGray(Mat &image)

作用:返回一张彩色图片的灰度图片

实现方式:才用标准的转化公式:Gray = R*0.299 + G*0.587 + B*0.114

循环的取出每一个像素,对每一个彩色像素做该运算就可得到灰度图片。

(2)获取高斯核

图像灰度画函数:double **getGussKernel(int size, double sigma)

作用:返回一个二维double类型指针,里面是对应size和sigma的高斯卷积核

实现方式:通过查阅资料和博客我们知道高斯卷积核公式:

由这个公式,我们可以容易写出一下核心代码:

最后记得要做归一化,之后返回指针g即可。

(3)卷积函数的两种实现

现在我们得到了一个高斯核,要做卷积了,由于我们知道在求梯度图片的时候同样要用到卷积,所以我们写一个卷积函数。

卷积函数:Mat Convolution1(Mat &image, double **kernel,int size)

Mat Convolution2(Mat &image, double **kernel,int size)

参数:带卷积图像  卷积核  卷积核大小

这两种方法大体上相同,就是在对边缘的处理上一个视边缘的无效邻域视为边缘本身;一个视为0,根据实验效果来看,两者没有太大差别(边缘本身就看不出来)

作用:将图像与卷积核做卷积处理并返回卷积后的图像(不改变原图)

具体实现:采用循环的方式,对每一个像素都对他的八邻域做分别做乘积后相加。需要注意的是不同的边缘控制代码有一些不同。

(需要注意的是,为了方便计算,我们事先把卷积核转化为1维,这样省了不少事,这个主要感谢http://blog.csdn.net/dcrmg/article/details/52344902这篇博客上的灵感,不然处理起来有点混乱。)

最后返回Mat 图像就是卷积之后的图像。

有了这个工具,我们只需要使用这个工具与步骤而获得的高斯核卷积就可以得到高斯滤波后的图像。

(4)获得梯度图像

这里分三步:

(1)获得x方向的梯度图像

方法:使用sobel算子:

sobel_x[3][3] = { { -1,-2,-1 }    ,{ 0,0,0 }    ,{ 1,2,1 } };

与我们之前写好的卷积函数做卷积即可。

(2)获得y方向上的梯度图像

方法:使用sobel算子:

sobel_y[3][3] = { { -1,0,1 }   ,{ -2,0,2 }    ,{ -1,0,1 } };

与我们之前写好的卷积函数做卷积即可。

(3)获得综合梯度的幅值

Sobel函数:

Mat Sobel(Mat &image_x,Mat &image_y)

参数:x方向梯度图像 y方向梯度图像

作用:返回梯度图像

具体实现:根据公式

可以知道新图片的每一像素点都等于x,y梯度方向上的欧氏距离,由此思路可以清晰的写出代码:

最后返回Mat图像即可

(5)非极大抑制

非极大值抑制就是要消除梯度方向上非极大值的点,避免边缘非常“厚”的问题,是canny算法的精髓之一。

Nms函数:

Mat Nms(Mat &image, Mat &image_x, Mat &image_y)

参数:梯度图像  x方向梯度图像 y方向梯度图像

作用:通过计算梯度方向和插值对图像进行非极大抑制

具体实现:我们知道非极大抑制的难点就在于找和当前点梯度方向相同的相邻点,由于很有可能该点处于两个像素的中间,即实际该点没有像素值,但是梯度方向指向该值,所以我们在这里使用插值的方法。通俗的解释就是

假设这里我们以c点为中心,那么c点的梯度方向的邻域点可能落在上图中四个小正方体的外接边上的八个边上,而我们只知道八个顶点的信息,为了取得亚像素的信息,我们采用线性插值的方法,插值点有四种位置,我们需要根据梯度方向的不同来确定具体的位置。根据线性插值的一般公式:

M(dtmp1)=w*M(g2)+(1-w)*M(g1)

其中w=|x方向梯度|/|y方向梯度|(靠x方向时)

或w=|y方向梯度|/|x方向梯度|(靠y方向时)

基于这种考虑,我们通过判断x与y的绝对值大小来判断c2与c4的位置

通过比较x与y是否同号来确定c1,c3的位置。

其他方向同理

在找到其梯度方向上的插值点之后,剩下的就是要与这两个插值点进行比较,若当前点比他们都大,说明当前点为极大值点,保留。否则就置成0。代码实现较简单使用简单的判断即可。(这里没有使用梯度角,而是直接用了x,y方向上的梯度图像来比较主要是为了理解方便,而且梯度角非常容易出错)

(6)双阈值

到上一步,我们得到了大致的边缘图片,但是这幅图片边缘处太密,我们想通过双阈值的方法来处理图片,突出灰度值较大的边缘部分,将灰度值较小的部分置为0.

双阈值函数:

Mat DoubleThreshold(Mat &image, double high_threshold, double low_threshold)

参数: Nms后的图像  高阈值 低阈值

作用:输出对应双阈值之后的图像(没有对中间阈值部分进行连接)

连接函数:

Mat Link1(Mat &image)

作用:对执行完DoubleThreshold函数的图像的中间部分做中间的连接处理,返回最终的边缘函数。

具体实现:实现分两步。

首先第一步很简单,就是遍历图像的每一个像素,并对他的值进行判断,若该值小于低阈值就为0;若高于高阈值就置为255。对于中间的点先不理他,在后面做连接的时候再处理。

第二步 连接:在这里实现了两种连接。后面在实验结果分析会比较哪种比较好。第一种连接方式的原则很简单:

遍历一个经过DoubleThreshold后的图像,对于每一个点再去遍历他的八连通邻域,若其八连通邻域全是0,说明这是一个孤立的边缘点,可以将其去掉;若其八邻域内有非0的点,不管是255还是处于中间的点,则说明这个点可以作为边缘连接点,就将其置为255。这样循环遍历,然后返回。

核心程序截图如下:

第二种才用递归的方式,称其为Link2。

递归的主要特点使每次从当前刚刚变为边缘的图像开始深度优先搜索,这样有不会出现“误伤”的情况。具体实现分为递归函数和主控函数,核心递归函数如图所示:

最终canny为

opencv自带的为

最后附上源代码:

//#include <stdafx.h>
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include<cstring>
#include<math.h>
#include<iostream>
#define PI 3.1415926
using namespace cv;
using namespace std;
char path[20];
double sobel_y[3][3] = { { -1,0,1 }   ,{ -2,0,2 }    ,{ -1,0,1 } };//sobel算子
double sobel_x[3][3] = { { -1,-2,-1 }    ,{ 0,0,0 }    ,{ 1,2,1 } };
int xNum[8] = { 1,1,0,-1,-1,-1,0,1 };
int yNum[8] = { 0,1,1,1,0,-1,-1,-1 };//方向
/*返回该函数的灰度值*/
Mat getGray(Mat &image)
{
    if (!image.data || image.channels() != 3)
    {
        printf("图片属性错误");
        return image;
    }
    Mat gray_image = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    //Mat gray_image(2, image.size, CV_8UC1, Scalar::all(0));
    int image_step = image.step;
    for (int i = 0; i < gray_image.rows; i++)
    {
        for (int j = 0; j < gray_image.cols; j++)
        {
            int base = image_step*i;//基地址
            gray_image.data[i*gray_image.step + j] = 0.114*image.data[base + j * 3 + 0]                 + 0.587*image.data[base + j * 3 + 1] + 0.299*image.data[base + j * 3 + 2];
        }
    }
    return gray_image;
}
/*获取尺寸为size 标准差为sigma的高斯核*/

double **getGussKernel(int size, double sigma)
{
    double temp = 1 / (2 * PI*sigma*sigma);
    int x0 = size / 2;
    int y0 = size / 2;
    double sum = 0;//归一化做准备
    double **g = new double *[size];
    for (int i =0; i < size; i++)
    {
        g[i] = new double[size];
    }
    for (int x = 0; x < size; x++)
    {
        for (int y = 0; y < size; y++)
        {
            g[x][y] = temp*exp(-((x - x0)*(x - x0) + (y - y0)*(y - y0)) / (2 * sigma*sigma));
            sum += g[x][y];
        }
    }
    for (int x = 0; x < size; x++)
    {
        for (int y = 0; y < size; y++)
        {
            g[x][y] = g[x][y] / sum;
            cout << g[x][y] << "  ";
        }
        cout << endl << endl;
    }
    return g;
}
/*对图片做kernel卷积 边缘处理方案一*/
Mat Convolution1(Mat &image, double **kernel,int size)
{
    Mat after_convolution = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    int count = 0;
    double kernel_temp[100];
    for (int i = 0; i<size*size; i++)
    {
        kernel_temp[i] = 0;  //赋初值,空间分配
    }
    for (int i = 0; i < size; i++)//将核转化为一维,方便卷积***
    {
        for (int j = 0; j < size; j++)
        {
            kernel_temp[count] = kernel [i][j];
            count++;
        }
    }
    /*边缘处理才用边缘的点代替值*/
    for (int i = 0; i < image.rows; i++)
    {
        for (int j = 0; j < image.cols; j++)
        {
            int k = 0;//卷积核的索引
            for (int x = -size / 2; x <=size / 2; x++)
            {
                for (int y = -size / 2; y <= size / 2; y++)
                {
                    int r = i + x;
                    int c = j + y;
                    r = r < 0 ? 0 : r;//处理顶部边缘
                    r = r >= image.rows ? image.rows - 1 : r;//处理底部边缘
                    c = c < 0 ? 0 : c;
                    c = c >= image.cols ? image.cols - 1 : c;
                    after_convolution.at<uchar>(i, j) += kernel_temp[k] * image.at<uchar>(r, c);
                    k++;
                }
            }
        }
    }
    return after_convolution;
}
Mat Convolution2(Mat &image, double **kernel, int size)
{
    Mat after_convolution = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    int count = 0;
    double kernel_temp[100];
    for (int i = 0; i<size*size; i++)
    {
        kernel_temp[i] = 0;  //赋初值,空间分配
    }
    for (int i = 0; i < size; i++)//将核转化为一维,方便卷积***
    {
        for (int j = 0; j < size; j++)
        {
            kernel_temp[count] = kernel[i][j];
            count++;
        }
    }
    /*边缘处理用0代替*/
    for (int i = 0; i < image.rows; i++)
    {
        for (int j = 0; j < image.cols; j++)
        {
            int k = 0;//卷积核的索引
            for (int x = -size / 2; x <= size / 2; x++)
            {
                for (int y = -size / 2; y <= size / 2; y++)
                {
                    int r = i + x;
                    int c = j + y;
                    r = r < 0 ? -1 : r;//处理顶部边缘
                    r = r >= image.rows ? - 1 : r;//处理底部边缘
                    c = c < 0 ? -1: c;
                    c = c >= image.cols ? -1 : c;
                    if (r == -1 || c == -1)
                        after_convolution.at<uchar>(i, j) += kernel_temp[k] * 0;
                    else
                        after_convolution.at<uchar>(i, j) += kernel_temp[k] * image.at<uchar>(r, c);
                    k++;
                }
            }
        }
    }
    return after_convolution;
}
//卷积公式加绝对值
Mat Convolution3(Mat &image, double (*kernel)[3], int size)
{
    double sum=0;
    Mat after_convolution = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    int count = 0;
    double kernel_temp[100];
    for (int i = 0; i<size*size; i++)
    {
        kernel_temp[i] = 0;  //赋初值,空间分配
    }
    for (int i = 0; i < size; i++)//将核转化为一维,方便卷积***
    {
        for (int j = 0; j < size; j++)
        {
            kernel_temp[count] = kernel[i][j];
            count++;
            printf("%f  ", kernel[i][j]);
        }
        printf("\n\n");
    }
    /*边缘处理才用边缘的点代替值*/
    for (int i = 0; i < image.rows; i++)
    {
        for (int j = 0; j < image.cols; j++)
        {
            sum = after_convolution.at<uchar>(i, j);
            int k = 0;//卷积核的索引
            for (int x = -size / 2; x <= size / 2; x++)
            {
                for (int y = -size / 2; y <= size / 2; y++)
                {
                    int r = i + x;
                    int c = j + y;
                    r = r < 0 ? 0 : r;//处理顶部边缘
                    r = r >= image.rows ? image.rows - 1 : r;//处理底部边缘
                    c = c < 0 ? 0 : c;
                    c = c >= image.cols ? image.cols - 1 : c;
                    sum += (kernel_temp[k] * image.at<uchar>(r, c));
                    k++;
                }
            }
            after_convolution.at<uchar>(i, j) = abs(sum);
        }
    }
    convertScaleAbs(after_convolution, after_convolution);
    return after_convolution;
}
Mat Sobel(Mat &image_x,Mat &image_y, double *gradint_angle)
{
    Mat after_sobel = Mat::zeros(image_x.size(), CV_32FC1);//创建一幅空图片注意图片类型
    int k = 0;
    for (int i = 0; i < after_sobel.rows; i++)
    {
        for (int j = 0; j < after_sobel.cols; j++)
        {
            double x = image_x.at<uchar>(i, j);
            double y = image_y.at<uchar>(i, j);
            after_sobel.at<float>(i, j) = sqrt(x*x + y*y);
        }
    }
    convertScaleAbs(after_sobel, after_sobel);//转为8位无符号整形
    return after_sobel;
}

Mat Nms1(Mat &image, Mat &image_x, Mat &image_y)
{
    Mat after_nms = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_nms = image.clone();
    double center, temp1, temp2;
    int c1, c2, c3, c4;
    double x, y;//图片在x,y方向的梯度
    double k;
    for (int i = 1; i < image.rows-1; i++)
    {
        for (int j = 1; j < image.cols-1; j++)
        {
            center = image.at<uchar>(i, j);
            x = image_x.at<uchar>(i, j);
            y = image_y.at<uchar>(i, j);
            if (abs(y) > abs(x))//确定c2c4位置 考y轴
            {
                c2 = image.at<uchar>(i - 1, j);//上
                c4 = image.at<uchar>(i - 1, j);
                if (y == 0)
                    k = 0;
                else
                {
                    k = fabs(x) /fabs(y);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j + 1);
                    c3 = image.at<uchar>(i + 1, j - 1);
                }
                else//否则24象限
                {
                    c3 = image.at<uchar>(i + 1, j + 1);
                    c1 = image.at<uchar>(i - 1, j - 1);
                }
            }
            else
            {
                c2 = image.at<uchar>(i , j+1);//上
                c4 = image.at<uchar>(i - 1, j-1);
                if (x == 0)
                    k = 0;
                else
                {
                    k = fabs(y) / fabs(x);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j - 1);
                    c3 = image.at<uchar>(i + 1, j + 1);
                }
                else//否则1,3象限
                {
                    c1 = image.at<uchar>(i + 1, j - 1);
                    c3 = image.at<uchar>(i - 1, j + 1);
                }
            }
            temp1 = k*c1 + (1 - k)*c2;
            temp2 = k*c3 + (1 - k)*c4;

            if (center >= temp1&&center >= temp2)
            {
                //是极大值
                ;
            }
            else
            {
                //不是最大值置0
                after_nms.at<uchar>(i, j) = 0;
            }
        }
    }
    return after_nms;
}
Mat Nms2(Mat &image, Mat &image_x, Mat &image_y)
{
    Mat after_nms = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_nms = image.clone();
    double center, temp1, temp2;
    int c1, c2, c3, c4;
    double x, y;//图片在x,y方向的梯度
    double k;
    for (int i = 1; i < image.rows - 1; i++)
    {
        for (int j = 1; j < image.cols - 1; j++)
        {
            center = image.at<uchar>(i, j);
            x = image_x.at<uchar>(i, j);
            y = image_y.at<uchar>(i, j);
            if (abs(y) > abs(x))//确定c2c4位置 考y轴
            {
                c2 = image.at<uchar>(i, j + 1);//上
                c4 = image.at<uchar>(i, j - 1);
                if (y == 0)
                    k = 0;
                else
                {
                    k = fabs(x) / fabs(y);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j + 1);
                    c3 = image.at<uchar>(i + 1, j - 1);
                }
                else//否则24象限
                {
                    c3 = image.at<uchar>(i + 1, j + 1);
                    c1 = image.at<uchar>(i - 1, j - 1);
                }
            }
            else
            {
                c2 = image.at<uchar>(i - 1, j);//上
                c4 = image.at<uchar>(i + 1, j);
                if (x == 0)
                    k = 0;
                else
                {
                    k = fabs(y) / fabs(x);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j - 1);
                    c3 = image.at<uchar>(i + 1, j + 1);
                }
                else//否则1,3象限
                {
                    c1 = image.at<uchar>(i + 1, j - 1);
                    c3 = image.at<uchar>(i - 1, j + 1);
                }
            }
            temp1 = k*c1 + (1 - k)*c2;
            temp2 = k*c3 + (1 - k)*c4;

            if (center >= temp1&&center >= temp2)
            {
                //是极大值
                ;
            }
            else
            {
                //不是最大值置0
                after_nms.at<uchar>(i, j) = 0;
            }
        }
    }
    return after_nms;
}
Mat Nms3(Mat &image, Mat &image_x, Mat &image_y)
{
    Mat after_nms = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_nms = image.clone();
    double center, temp1, temp2;
    int c1, c2, c3, c4;
    double x, y;//图片在x,y方向的梯度
    double k;
    for (int i = 1; i < image.rows - 1; i++)
    {
        for (int j = 1; j < image.cols - 1; j++)
        {
            center = image.at<uchar>(i, j);
            x = image_x.at<uchar>(i, j);
            y = image_y.at<uchar>(i, j);
            if (abs(y) < abs(x))//确定c2c4位置 考y轴
            {
                c2 = image.at<uchar>(i, j + 1);//上
                c4 = image.at<uchar>(i, j - 1);
                if (y == 0)
                    k = 0;
                else
                {
                    k = fabs(y) / fabs(x);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j + 1);
                    c3 = image.at<uchar>(i + 1, j - 1);
                }
                else//否则24象限
                {
                    c3 = image.at<uchar>(i - 1, j - 1);
                    c1 = image.at<uchar>(i + 1, j + 1);
                }
            }
            else
            {
                c2 = image.at<uchar>(i - 1, j);//上
                c4 = image.at<uchar>(i + 1, j);
                if (x == 0)
                    k = 0;
                else
                {
                    k = fabs(x) / fabs(y);//比例
                }
                if (x*y < 0)//两方向不相同,13象限
                {
                    c1 = image.at<uchar>(i - 1, j - 1);
                    c3 = image.at<uchar>(i + 1, j + 1);
                }
                else//否则1,3象限
                {
                    c1 = image.at<uchar>(i - 1, j + 1);
                    c3 = image.at<uchar>(i + 1, j - 1);
                }
            }
            temp1 = k*c1 + (1 - k)*c2;
            temp2 = k*c3 + (1 - k)*c4;

            if (center >= temp1&&center >= temp2)
            {
                //是极大值
                ;
            }
            else
            {
                //不是最大值置0
                after_nms.at<uchar>(i, j) = 0;
            }
        }
    }
    return after_nms;
}

Mat DoubleThreshold(Mat &image, double high_threshold, double low_threshold)
{
    Mat after_threshold = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_threshold = image.clone();
    for (int i = 0; i < image.rows; i++)
    {
        for (int j = 0; j < image.cols; j++)
        {
            double temp = image.at<uchar>(i, j);
            if (temp < low_threshold)
            {
                after_threshold.at<uchar>(i, j) = 0;
            }
            if (temp > high_threshold)
            {
                after_threshold.at<uchar>(i, j) = 255;
            }
        }
    }
    return after_threshold;
}
Mat Link1(Mat &image)
{
    bool flag = false;
    int x = 0;
    int y = 0;
    Mat after_link = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_link = image.clone();
    for (int i = 1; i < image.rows-1; i++)
    {
        for (int j = 1; j < image.cols-1; j++)
        {
            flag = false;
            double temp = image.at<uchar>(i, j);
            if (temp > 0 && temp < 255)//处于阈值中间的
            {

                for (int k = 0; k < 8; k++)//循环判断
                {
                    x = i + xNum[k];
                    y = j + yNum[k];
                    if (after_link.at<uchar>(x, y) != 0)
                    {
                        flag = true;
                        break;
                    }
                }
                if (flag)//周围有不为0的点
                {
                    after_link.at<uchar>(i, j) = 255;
                }
                else
                {
                    after_link.at<uchar>(i, j) = 0;
                }
            }
        }
    }
    return after_link;
}
void Link2(Mat &image,int i,int j)
{
    //printf("i=%d   j=%d\n", i, j);
    bool flag = false;
    int x = 0;
    int y = 0;
    double temp = image.at<uchar>(i, j);
    if (temp > 0 && temp < 254)//处于阈值中间的
    {
        for (int k = 0; k < 8; k++)//循环判断
        {
            x = i + xNum[k];
            y = j + yNum[k];
            if (image.at<uchar>(x, y) != 0)
            {
                flag = true;
                break;
            }
        }
        if (flag)//周围有不为0的点
        {
            image.at<uchar>(i, j) = 255;
            Link2(image, x, y);
        }
        else
        {
            image.at<uchar>(i, j) = 0;
        }
    }
    return;
}
Mat Link3(Mat &image)
{
    Mat after_link = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    after_link = image.clone();
    int k = 0;
    for (int i = 1; i < after_link.rows-1; i++)
    {
        for (int j = 1; j<after_link.cols-1; j++)
        {
            double temp = image.at<uchar>(i, j);
            if (temp > 0 && temp < 255)
            {
                //printf("%d\n", k);
                Link2(after_link, i, j);
                k++;
            }

        }
    }
    return after_link;
}
int main()
{
    Mat image = imread("G:\\image\\Lena.jpg");
    Mat test_image = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    Mat test_image2 = Mat::zeros(image.size(), CV_8UC1);//创建一幅空图片
    if (image.empty())
    {
        printf("图片加载出错");
        return 0;
    }
    double *point;
    Mat gray_image = getGray(image);
    imwrite("..\\gray.jpg", gray_image);
    double *gradint_angle=nullptr;
    double **guss = getGussKernel(5, 5);
    Mat after_guss = Convolution2(gray_image, guss, 5);
    imwrite("..\\guss.jpg", after_guss);
    Mat x = Convolution3(after_guss, sobel_x, 3);
    imwrite("..\\gradint_x.jpg", x);
    Mat y = Convolution3(after_guss, sobel_y,3);
    imwrite("..\\gradint_y.jpg", y);
    Mat z = Sobel(x, y, gradint_angle);
    imwrite("..\\gradint.jpg", z);
    Mat nms = Nms2(z, x, y);
    imwrite("..\\afterNms.jpg", nms);
    Mat t = DoubleThreshold(nms, 120, 60);
    imwrite("..\\afterDT.jpg", t);
    Mat canny = Link1(t);
    imwrite("..\\canny.jpg", canny);
    Mat canny2 = Link3(t);
    GaussianBlur(gray_image, test_image,Size(5,5),10);
    imshow("原图片为", image);
    imshow("灰度图片为", gray_image);
    imshow("使用自己写的高斯滤波后的图片为", after_guss);
    imshow("x方向的梯度", x);
    imshow("y方向的梯度", y);
    imshow("合并的梯度", z);
    imshow("非极大抑制后的结果为", nms);
    imshow("双阈值结果为", t);
    imshow("连接后的结果为", canny);
    imshow("2连接后的结果为", canny2);
    imshow("cv高斯滤波", test_image);

    ///*opencv 自带梯度算法*/
    //Sobel(after_guss, test_image, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
    //convertScaleAbs(test_image, test_image);//使用线性变换转换输入数组元素成8位无符号整型
    //imshow("oX方向Sobel", test_image);
    //Sobel(after_guss, test_image2, CV_16S, 1, 1, 3, 1, 1, BORDER_DEFAULT);
    //convertScaleAbs(test_image2, test_image2);//使用线性变换转换输入数组元素成8位无符号整型
    //imshow("oY方向Sobel", test_image2);
    //imshow("使用opencv高斯滤波后的图片为", test_image);
    //addWeighted(test_image, 0.5, test_image2, 0.5, 0, test_image);
    //imshow("整体方向Sobel", test_image);
    ///****************************************/
    Canny(image, test_image, 150, 50,3);
    imshow("cvcanny", test_image);
    waitKey();
    return 0;
}
时间: 2024-11-08 20:16:45

Canny 算子 c++的相关文章

学习 opencv---(11)OpenC 边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器

本篇文章中,我们将一起学习OpenCV中边缘检测的各种算子和滤波器--Canny算子,Sobel算子,Laplace算子以及Scharr滤波器.文章中包含了五个浅墨为大家准备的详细注释的博文配套源代码.在介绍四块知识点的时候分别一个,以及最后的综合示例中的一个.文章末尾提供配套源代码的下载. **** 给大家分享一个OpenCv中写代码是节约时间的小常识.其实OpenCv中,不用nameWindow,直接imshow就可以显示出窗口.大家看下文的示例代码就可以发现,浅墨在写代码的时候并没有用na

【OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们将一起学习OpenCV中

【opencv入门之九】Opencv边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器

参考网站: http://blog.csdn.net/poem_qianmo/article/details/25560901 1.边缘检测步骤 1)滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感.( 通常用高斯滤波 ) 2)增强:增强边缘的基础是确定图像各点领域强度的变化值.增强算法可以将图像灰度点领域强度值有显著变化的点凸显出来.( 可以通过计算梯度幅值来确定 ) 3)检测:经过增强的图像,往往领域中有很多点的梯度值比较大,而特定的应用中,这些点并不是我们要找

Opencv图像识别从零到精通(21)-----canny算子边缘检测

最后来看看canny算子,这个是被成为最好的算子,因为过程多,有准测,后面会列出来,也是边缘检测的最后一个,所以这里作为结尾,来看看各个边缘检测的效果. 边缘检测结果比较 Roberts算子检测方法对具有陡峭的低噪声的图像处理效果较好,但是利用roberts算子提取边缘的结果是边缘比较粗,因此边缘的定位不是很准确. Sobel算子检测方法对灰度渐变和噪声较多的图像处理效果较好,sobel算子对边缘定位不是很准确,图像的边缘不止一个像素. Prewitt算子检测方法对灰度渐变和噪声较多的图像处理效

OpenCV2学习笔记(七):使用Canny算子检测轮廓

在:http://blog.csdn.net/liyuefeilong/article/details/43927909 中,主要讨论了使用sobel算子和拉普拉斯变换进行边缘检测.其中主要使用了了对梯度大小进行阈值化以得到二值的边缘图像的方法.在一幅图像中,边缘往往包含着重要的视觉信息,因为它们描绘出图像元素的轮廓.然而,仅仅使用简单的二值边缘图像有两大缺陷: 使用这种方法检测得到的边缘过粗,这意味着难以实现物体的精确定位. 难以找到这样的阀值,既能足够低检测到所有重要的边缘,同时也不至于包含

canny 算子python实现

1. Canny介绍 Canny算子与Marr(LoG)边缘检测方法类似,也属于是先平滑后求导数的方法.John Canny研究了最优边缘检测方法所需的特性,给出了评价边缘检测性能优劣的三个指标: 1  好的信噪比,即将非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低: 2  高的定位性能,即检测出的边缘点要尽可能在实际边缘的中心: 3  对单一边缘仅有唯一响应,即单个边缘产生多个响应的概率要低,并且虚假响应边缘应该得到最大抑制. 2. Canny检测实现过程 第一步:灰度化  第

Opencv对图像做边缘检测——canny算子

图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘. Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法.Canny 边缘检测的数学原理和算法实现这里就不再了,有兴趣的读者可以查阅专业书籍,本文主要介绍如何在OpenCV中对图像进行Canny 边缘检测,下面就来看看这个函数的原型. 一. 主要函数 1.1 cvCanny 函数功能:采用Canny方法对图像进行边缘检测 函数原型:

canny算子求图像边缘,edgebox那部分

过程: 1.      彩色图像转换为灰度图像    2.      对图像进行高斯模糊    3.      计算图像梯度,根据梯度计算图像边缘幅值与角度(这里其实用到了微分边缘检测算子来计算梯度幅值方向) 求x,y两个方向的梯度 求幅值与角度 4.      非最大信号压制处理(边缘细化) 也就是把角度分成4个值 得到角度之后,比较中心像素角度上相邻两个像素,如果中心像素小于其中任意一个,则舍弃该边缘像素点,否则保留. 5.      双阈值边缘连接处理 双阈值选择与边缘连接方法通过假设两个

算法解剖系列(1)-Canny边缘检测原理

Canny边缘检测算子 基本原理 须满足条件:抑制噪声:精确定位边缘. 从数学上表达了三个准则[信噪比准则(低错误率).定位精度准则.单边缘响应准则],并寻找表达式的最佳解. 属于先平滑后求导的方法. 算法实现步骤 1.使用高斯滤波平滑图像 令f(x,y)表示数据(输入源数据),G(x,y)表示二维高斯函数(卷积操作数),fs(x,y)为卷积平滑后的图像. G(x,y)=12πσ2e?(x2+y2)2σ2 fs(x,y)=f(x,y)?G(x,y) Guess过程 用坐标点(x,y)表示一个3×