OpenCV 边缘检测
Canny算子
Canny 边缘检测算子,其算法步骤大体如下:
1) 用高斯滤波器对输入图像做平滑处理 (大小为 5x5
的高斯核)
2) 计算图像的梯度强度和角度方向 ( x
和 y
方向上的卷积核)
角度方向近似为四个可能值,即 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
分别为:
输出的图像矩阵 G
为:
OpenCV 中,Sobel 函数如下:
代码语言:javascript复制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 中对应的函数为 Laplacian
代码语言:javascript复制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
卷积核来代替,如下:
而 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
方向的导数,然后通过加权来进行边缘检测。
参考代码
代码语言:javascript复制#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