欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenCV图像处理教程C++(十五)边缘检测算法--sobel算子、拉普拉斯算子、Canny算子

程序员文章站 2022-07-14 15:25:38
...

1、滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波器方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的 高斯核,然后基于高斯核函数对图像灰度矩阵反每一点进行加权就和。
2、增强:增强边缘的基础是确定图像各点领域强度的变化值,增强算法可以将图像灰度点领域强度值有显著变化的点凸显出来,具体编程时可以通过计算梯度幅度值来确定。
3、检测:经过增强的图像,往往领域有很多点的梯度值比较大,而在特特定的应用中,这些点并不是我们要找的边缘点所以应该采用某种方法来对这些点进行取舍,实际中通过阈值化方法来检测。
sobel算子:
离散微分算子用来计算图像灰度的近似梯度
sobel算子的功能集合高斯平滑核微分求导
又被称为一阶微分算子,求导算子,在水平和垂直两个方向求导,得到图像得到图像x方向和y方向梯度图像。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。

Sobel (
InputArray src,//输入图
OutputArray dst,//输出图
int ddepth,//输出图像的深度
int dx,    // x 方向上的差分阶数
int dy,     // y方向上的差分阶数
int ksize=3, // 有默认值3,表示Sobel核的大小;必须取1,3,5或7
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT );

当内核大小为 3 时, 我们的Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值而已)
为解决这一问题,OpenCV提供了Scharr 函数,但该函数仅作用于大小为3的内核该函数的运算与Sobel函数一样快,但结果却更加精确
因为Sobel算子结合了高斯平滑和分化(differentiation),因此结果会具有更多的抗噪性。大多数情况下,我们使用sobel函数
时,取【xorder = 1,yorder = 0,ksize = 3】来计算图像X方向的导数,
【xorder = 0,yorder = 1,ksize = 3】来计算图像y方向的导数。

步骤:
高斯平滑
转灰度
求梯度xy
振幅图像(合并近似梯度)
代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

int main() {
    Mat src, gaosrc, graysrc, xdst, ydst, dst;
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\z4.png");
    imshow("input", src);
    GaussianBlur(src, gaosrc, Size(3, 3), 0,0);
    imshow("gaosioutput", gaosrc);
    cvtColor(gaosrc, graysrc, CV_RGB2GRAY);
    imshow("grayoutput", graysrc);
    Sobel(graysrc, xdst, -1, 1, 0);
    imshow("xdst", xdst);
    Sobel(graysrc, ydst, -1, 0, 1);
    imshow("ydst", ydst);
    addWeighted(xdst, 0.5, ydst, 0.5, 1, dst);
    imshow("OUTPUT", dst);
    waitKey(0);
}

结果:
OpenCV图像处理教程C++(十五)边缘检测算法--sobel算子、拉普拉斯算子、Canny算子

拉普拉斯算子:
在二阶导数的时候,最大变化处的值为0即边缘是0,通过二阶导数计算
拉普拉斯算子是最简单的各向同性微分算子,具有旋转不变性。一个二维图像函数 的拉普拉斯变换是各向同性的二阶导数。
如果在图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。
一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘线的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定。
对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。
同梯度算子一样,拉普拉斯算子也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。
图像模糊的实质就是图像受到平均运算或积分运算,因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。
由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。

Laplacian(InputArray src,
OutputArray dst, 
int ddepth, 
int ksize=1,   用于计算二阶导数的滤波器的孔径尺寸,大小必须为正奇数,且有默认值1
double scale=1,
double delta=0, 
intborderType=BORDER_DEFAULT );
Laplacian( )函数其实主要是利用sobel算子的运算。它通过加上sobel算子运算出的图像x方向和y方向上的导数,

步骤:
高斯模糊-去噪声
转为灰度图像
拉普拉斯-二阶导数计算
取绝对值convertScaleAbs()
显示结果

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

int main() {
    Mat src, gaosrc, graysrc, ladst, dst;
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\face.jpg");
    imshow("input", src);
    GaussianBlur(src, gaosrc, Size(3, 3), 0);
    imshow("gaosrc", gaosrc);
    cvtColor(gaosrc, graysrc, CV_RGB2GRAY);
    imshow("graysrc", graysrc);
    Laplacian(graysrc, ladst, -1);
    imshow("ladst", ladst);
    convertScaleAbs(ladst, dst);
    imshow("dst", dst);
    waitKey(0);
}

结果:
OpenCV图像处理教程C++(十五)边缘检测算法--sobel算子、拉普拉斯算子、Canny算子

Canny算子:
Canny边缘检测算法以Canny的名字命名,被很多人推崇为当今最优的边缘检测的算法。
Canny 的目标是找到一个最优的边缘检测算法,让我们看一下最优边缘检测的三个主要评价标准:
低错误率:标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报
高定位性:标识出的边缘要与图像的实际边缘尽可能接近
最小响应:图像的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘

Canny(InputArray image,
 OutputArray edges, 
 double threshold1, 第一个滞后性阈值 低阈值
 double threshold2, 第二个滞后性阈值 高阈值
 int apertureSize=3, 表示应用Sobel算子的孔径大小,其有默认值3
 bool L2gradient=false )

步骤:
高斯模糊–消除噪声
灰度转换
计算梯度–按照Sobel滤波器的步骤
非最大信号抑制–这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)
高低阈值输出二值图像:

  • 如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素
  • 如果某一像素位置的幅值小于 低 阈值, 该像素被排除
  • 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留

Tips:对于Canny函数的使用,推荐的高低阈值比在2:1到3:1之间

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;
//转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色的边缘图
Mat src, gaosrc, graysrc, cadst, dst;
int  t1_value = 50;
void getCannyChange(int, void*) {
    GaussianBlur(src, gaosrc, Size(3, 3), 0);
    cvtColor(gaosrc, graysrc, CV_RGB2GRAY);
    Canny(graysrc, cadst, t1_value, t1_value*2);
    dst.create(src.size(), src.type());// 创建与src同类型和大小的矩阵(dst)
    dst = Scalar::all(0);//所有元素设置为0
    src.copyTo(dst, cadst);//使用Canny算子输出的边缘图cadst作为掩码,来将原图src拷到目标图dst中
    imshow("dst", dst);
    imshow("cadst", cadst);
}

int main() {
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\6.jpeg");
    imshow("input", src);
    getCannyChange(0, 0);
    createTrackbar("阈值大小:", "dst", &t1_value, 255, getCannyChange);
    waitKey(0);
}

结果:
OpenCV图像处理教程C++(十五)边缘检测算法--sobel算子、拉普拉斯算子、Canny算子