opencv——边缘检测算法(总结)
前⾔
耐⼼看完⼀定会有收获的,⼤部分内容也会在代码中体现,结合理论知识和代码进⾏理解会更有效。代码⽤opencv4.5.1(c++)版实现
⼀、边缘检测算法
边缘检测算法是指利⽤灰度值的不连续性质,以灰度突变为基础分割出⽬标区域。对铝铸件表⾯进⾏成像后会产⽣⼀些带缺陷的区域,这些区域的灰度值⽐较低,与背景图像相⽐在灰度上会有突变,这是由于这些区域对光线产⽣散射所引起的。因此边缘检测算⼦可以⽤来对特征的提取。
1、⼀阶算⼦
⼀种是基于⼀阶微分的算⼦,也称基于搜索的算⼦,⾸先通过⼀阶导数计算边缘强度,然后采⽤梯度的⽅向来对边缘的局部⽅向进⾏寻,同时根据该⽅向来寻出局部梯度模的最⼤值,由此定位边缘,如Roberts Cross算⼦,Prewitt算⼦Sobel算⼦,Kirsch算⼦,Canny算⼦,罗盘算⼦等;
图像中的边缘区域,像素值会发⽣“跳跃”,对这些像素求导,在其⼀阶导数在边缘位置为极值,这就是Sobel算⼦使⽤的原理——极值处就是边缘。
2、⼆阶算⼦
另⼀种是基于⼆阶微分的算⼦,也称基于零交叉的算⼦,通过寻由图像得到的⼆阶导数的过零点来定位检测边缘,如Marr-Hildreth算⼦,Laplacian算⼦,LOG算⼦等。如果对像素值求⼆阶导数,会发现边缘处的导数值为0。
⼆、⼀阶算⼦分析
⼀阶微分算⼦进⾏边缘检测的思路⼤致就是通过指定⼤⼩的核(kernal)(也称为算⼦)与图像进⾏卷积,将得到的梯度进⾏平⽅和或者最⼤值作为新的梯度赋值给对应的像素点,不同的⼀阶微分算⼦主要的不同在于其算⼦即核的元素不同以及核的⼤⼩不⼀样
以下是连续函数的⼀阶导数求导公式:因为图像是⼀个⾯,就相当于是灰度值关于x,y两个⽅向的函数,要求某⼀点的导数,则是各个⽅向的偏导数的平⽅和再进⾏开⽅运算。
离散函数的⼀阶导数公式:
y'=[y(x0+h)-y(x0-h)]/(2h);这是⼀维函数的⼀阶求导,h是步长,在图像处理中⼀般为1
⾸先复习⼀下什么是卷积?
卷积就是对应的元素相乘再进⾏累加的过程
实例图⽚:
1、Roberts算⼦
Robert算⼦是⽤于求解对⾓线⽅向的梯度,因为根据算⼦GX和GY的元素设置可以看到,只有对⾓线上的元素⾮零,其本质就是以对⾓线作为差分的⽅向来检测。检测垂直边缘的效果好于斜向边缘,定位精度⾼,对噪声敏感,⽆法抑制噪声的影响。 
代码⽰例:
#include<opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
Mat roberts(Mat srcImage);
int main(int argc, char** argv)
{
Mat src,src_binary,src_gray;
src = imread("D:/opencv练习图⽚/薛之谦.jpg");
imshow("原图", src);
cvtColor(src, src_gray, COLOR_BGR2GRAY);
GaussianBlur(src_gray, src_binary, Size(3, 3),0, 0, BORDER_DEFAULT);    Mat dstImage = roberts(src_binary);
imshow("dstImage", dstImage);
waitKey(0);
return0;
}
//roberts 边缘检测
Mat roberts(Mat srcImage)
htmlborder{
Mat dstImage = srcImage.clone();
int nRows = ws;
int nCols = ls;
for (int i = 0; i < nRows - 1; i++) {
for (int j = 0; j < nCols - 1; j++) {
//根据公式计算
int t1 = (srcImage.at<uchar>(i, j) -
srcImage.at<uchar>(i + 1, j + 1))*
(srcImage.at<uchar>(i, j) -
srcImage.at<uchar>(i + 1, j + 1));
int t2 = (srcImage.at<uchar>(i + 1, j) -
srcImage.at<uchar>(i, j + 1))*
(srcImage.at<uchar>(i + 1, j) -
srcImage.at<uchar>(i, j + 1));
//计算g(x,y)
dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
}
}
return dstImage;
}
效果展⽰:
2、Sobel算⼦
Sobel算⼦是主要⽤于边缘检测的离散微分算⼦,它结合了⾼斯平滑和微分求导,⽤于计算图像灰度函数的近似梯度。
因为Sobel算⼦结合了⾼斯平滑和分化,因此结果会具有更多的抗噪性。
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 // 边界模式
)
3、Prewitt算⼦
Prewitt算⼦利⽤像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作⽤。其原理是在图像空间利⽤两个⽅向模板与图像进⾏邻域卷积来完成的。
这两个⽅向模板⼀个检测⽔平边缘,⼀个检测垂直边缘。
Prewitt算⼦定位精度不如Sobel算⼦,在真正的使⽤中,⼀般不会⽤到这个算⼦,效果较差,因此不多做分析。
4、Canny算⼦
可以说,Canny 边缘检测算法是被业界公认的性能最为优良的边缘检测算法之⼀。Canny算法不是像Roberts、Prewitt、Sobel等这样简单梯度算⼦或锐化模板,它是在梯度算⼦基础上,引⼊了⼀种能获得抗噪性能好、定位精度⾼的单像素边缘的计算策略。
Canny 算⼦,在⼀阶微分的基础上,增加了⾮最⼤值抑制和双阈值检测,是边缘检测算⼦中最常⽤的⼀种,常被其它算⼦作为标准算⼦来进⾏优劣⽐较。
4.1算法步骤
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,则表⽰采⽤更精确的形式
5、Laplace(拉普拉斯算⼦)
索贝尔算⼦ (Sobel) 和拉普拉斯算⼦ (Laplace) 都是⽤来对图像进⾏边缘检测的,不同之处在于,前者是求⼀阶导,后者是求⼆阶导。
常⽤算⼦模块:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。