OpenCV 边缘检测

Posted AI异构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 边缘检测相关的知识,希望对你有一定的参考价值。

OpenCV 边缘检测

Canny算子

Canny 边缘检测算子,其算法步骤大体如下:
1) 用高斯滤波器对输入图像做平滑处理 (大小为 5x5 的高斯核)

2) 计算图像的梯度强度和角度方向 ( x 和 y 方向上的卷积核)

OpenCV 边缘检测


角度方向近似为四个可能值,即 0, 45, 90, 135

3) 对图像的梯度强度进行非极大抑制
可看做边缘细化:只有候选边缘点被保留,其余的点被移除

4) 利用双阈值检测和连接边缘
若候选边缘点大于上阈值,则被保留;小于下阈值,则被舍弃;处于二者之间,须视其所连接的像素点,大于上阈值则被保留,反之舍弃

OpenCV 中,Canny 函数如下:

void cv::Canny (
   InputArray    image,    // 输入图像 (8位)
   OutputArray   edges,    // 输出图像 (单通道,8位)
   double      threshold1,  // 下阈值
   double      threshold2,  // 上阈值
   int         apertureSize = 3,
   bool        L2gradient = false
)

一般 上阈值 / 下阈值 = 2 ~ 3
L2gradient 默认 flase,表示图像梯度强度的计算采用近似形式;若为 true ,则表示采用更精确的形式。

Sobel算子

假定输入图像矩阵为 I,卷积核大小为 3x3,则水平一阶导数 Gx 和垂直一阶导数 Gy 分别为:

OpenCV 边缘检测

输出的图像矩阵 G 为:

OpenCV 边缘检测

OpenCV 中,Sobel 函数如下:

void cv::Sobel   (
   InputArray  src,    // 输入图像
   OutputArray  dst,   // 输出图像
   int      ddepth,    // 输出图像深度,-1 表示等于 src.depth()
   int      dx,        // 水平方向的阶数
   int      dy,        // 垂直方向的阶数
   int     ksize = 3,    // 卷积核的大小,常取 1, 3, 5, 7 等奇数
   double  scale = 1,    // 缩放因子,应用于计算结果
   double  delta = 0,    // 增量数值,应用于计算结果
   int borderType = BORDER_DEFAULT // 边界模式
)

dx 和 dy 表示阶数,一般取 0 或 1,但不超过 2;scale = 1,表示计算结果不缩放;delat = 0,表示计算结果无增量。

Laplace算子

索贝尔算子 (Sobel) 和拉普拉斯算子 (Laplace) 都是用来对图像进行边缘检测的,不同之处在于,前者是求一阶导,后者是求二阶导。

OpenCV 边缘检测

OpenCV 中对应的函数为 Laplacian

void cv::Laplacian (
   InputArray     src,
   OutputArray    dst,
   int       ddepth,
   int       ksize = 1,
   double    scale = 1,
   double    delta = 0,
   int       borderType = BORDER_DEFAULT
)

Scharr算子

当卷积核大小为 3x3 时,使用 sobel 卷积核来计算并不是很精确,此时常用 Scharr 卷积核来代替,如下:

OpenCV 边缘检测

而 Sharr 函数,本质上就是令 ksize = 3 且使用 Scharr 卷积核的 Sobel 函数。

void cv::Scharr (
   InputArray  src,
   OutputArray  dst,
   int      ddepth,
   int      dx,
   int      dy,
   double  scale = 1,
   double  delta = 0,
   int     borderType = BORDER_DEFAULT
)

对于 Scharr 函数,要求 dx 和 dy 都 >= 0 且 dx + dy == 1,假如 dx 和 dy 都设为 1,则会抛出异常。

因此,对于 Sobel 和 Scharr 函数,通常各自求其 x 和 y 方向的导数,然后通过加权来进行边缘检测。

参考代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;

int main(int argc, char** argv) {
   Mat src;
   src = imread("cat.jpg");
   if (!src.data) {
       printf("could not load image...\n");
       return -1;
   }
   namedWindow("input image", CV_WINDOW_AUTOSIZE);
   imshow("input image", src);

   //自定义kernel滤波
   Mat kernelImage;
   Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
   filter2D(src, kernelImage, src.depth(), kernel);
   namedWindow("kernelImage", CV_WINDOW_AUTOSIZE);
   imshow("kernelImage", kernelImage);
   imwrite("./kernelImage.jpg",kernelImage);

   // canny算子边缘检测
   Mat canyImage;
   Canny(src, canyImage, 3, 9, 3);
   imshow("canyImage", canyImage);
   imwrite("./canyImage.jpg",canyImage);

   // Sobel算子边缘检测
   Mat sobel_x;
   Mat sobel_y;
   Mat sobelImage;
   Sobel(src, sobel_x,CV_16S,1,0,3,1,1,BORDER_DEFAULT);
   convertScaleAbs(sobel_x, sobel_x);
   Sobel(src, sobel_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
   convertScaleAbs(sobel_y, sobel_y);
   addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0, sobelImage);
   imshow("sobel_x", sobel_x);
   imshow("sobel_y", sobel_y);
   imshow("sobelImage", sobelImage);
   imwrite("./sobel_x.jpg",sobel_x);
   imwrite("./sobel_y.jpg",sobel_y);
   imwrite("./sobelImage.jpg",sobelImage);

   // laplacian算子边缘检测
   Mat laplacianImage;
   Laplacian(src, laplacianImage, CV_16S, 3, 1, 0, BORDER_DEFAULT);
   convertScaleAbs(laplacianImage, laplacianImage);
   imshow("laplacianImage", laplacianImage);
   imwrite("./laplacianImage.jpg",laplacianImage);

   // Schar算子边缘检测
   Mat scharr_x;
   Mat scharr_y;
   Mat scharrImage;
   Scharr(src, scharr_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
   convertScaleAbs(scharr_x, scharr_x);
   Scharr(src, scharr_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
   convertScaleAbs(scharr_y, scharr_y);
   addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0, scharrImage);
   imshow("scharr_x", scharr_x);
   imshow("scharr_y", scharr_y);
   imshow("scharrImage", scharrImage);
   imwrite("./scharr_x.jpg",scharr_x);
   imwrite("./scharr_y.jpg",scharr_y);
   imwrite("./scharrImage.jpg",scharrImage);

   waitKey(0);
   return 0;
}

参考

[OpenCV 之 边缘检测]https://www.cnblogs.com/xinxue/p/5348743.html
[OpenCV3编程入门读书笔记5-边缘检测]https://www.cnblogs.com/justkong/p/7297836.html


-长按关注-


以上是关于OpenCV 边缘检测的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV——Canny边缘检测

OpenCV学习代码记录——canny边缘检测

Python+OpenCv实现图像边缘检测(滑动调节阈值)

OpenCV 中的边缘检测

openCV Canny 边缘检测改进

实战深度学习OpenCV:canny边缘检测