1.滤波:边缘检测算法主要基于图像强度的一阶和二阶导数,导数对噪声敏感,所以要滤波
2.增强:确定图像各点邻域强度的变化值,将有显著变化的点凸显,可通过计算梯度幅值确定
3.检测:某些特定应用中梯度值较大点不为边缘点,通过阈值化方法检测进行取舍
1.主要评价标准:
(1)低错误率:减少噪声误报
(2)高定位性:接近实际边缘
(3)最小响应:边缘只标识一次
2.步骤:
(1)消除噪声:高斯滤波
(2)计算梯度幅值和方向
1)运用一对卷积阵列分别作用于x和y方向
2)计算梯度幅值和方向(按照Sobel滤波器步骤进行)
(3)非极大值抑制:排除非边缘像素,仅保留一些细线条
(4)滞后阈值:高阈值和低阈值(推荐高低阈值比2:1到3:1)
1)像素幅值超过高阈值,保留为边缘像素
2)像素幅值小于低阈值,排除
3)像素幅值在高低之间,该像素连接到一个高于高阈值的像素时被保留
3.函数:Canny()函数
4.函数原型:
void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
5.参数说明:
(1)输入图像
(2)输出的边缘图
(3)第一个滞后性阈值,较小值用于边缘连接
(4)第二个滞后性阈值,较大值用于控制强边缘初始段
(5)应用Sobel算子的孔径大小,默认值3
(6)计算图像梯度幅值的标识,默认false
6.调用示例:
//将原图转成灰度图,降噪,用canny,将边缘图作为掩模拷贝到效果图上,得到彩色边缘图
#include
#include
#include
using namespace cv;
int main()
{
//载入原图
Mat srcImage = imread("love.jpg");
imshow("【原始图】", srcImage);
Mat edgeImage, grayImage,dstImage;
//【1】创建与src同类型和大小矩阵
dstImage.create(srcImage.size(), srcImage.type());
//【2】将原图转为灰度图
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
//【3】使用3*3内核降噪
blur(grayImage, edgeImage, Size(3, 3));
//【4】运行canny算子
Canny(edgeImage, edgeImage, 3, 9, 3);
//【5】将dstImage中所有元素设为0
dstImage = Scalar::all(0);
//【6】使用Canny算子输出的边缘图作为掩码将原图拷到目标图
srcImage.copyTo(dstImage, edgeImage);
//【7】显示效果图
imshow("【效果图】Canny边缘检测", dstImage);
waitKey(0);
return 0;
}
1.主要用于边缘检测的离散微分算子(discrete differentiation operator),结合高斯平滑和微分求导,计算图像灰度函数的近似梯度
2.计算过程:
(1)分别在x和y两个方向求导
水平:图像I与奇数大小的内核进行卷积,如内核大小为3:
垂直:图像I与奇数大小的内核进行卷积,如内核大小为3:
(2)在图像每一点,结合以上两个结果求出近似梯度
或
3.函数:Sobel()函数
4.函数原型:
void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT)
5.参数说明:
(1)输入图像
(2)目标图像
(3)输出图像深度,支持src.depth()和ddepth的组合:
(4)x方向的差分阶数
(5)y方向的差分阶数
(6)Sobel核大小,取值:1、3(默认)、5、7,取值为1时一般使用3*1或1*3内核
内核大小为3时,Sobel内核可能有明显误差,Scharr函数更精确,内核为:
(7)计算导数时可选的缩放因子,默认1(无缩放)
(8)在结果存入目标图之前可选的delta值,默认0
(9)边界模式,默认BORDER_DEFAULT
使用sobel()函数时,取【xorder=1,yorder=0,ksize=3】计算图像X方向导数,取【xorder=0,yorder=1,ksize=3】计算图像Y方向导数
6.调用示例:
#include
#include
#include
using namespace cv;
int main()
{
//【0】创建grad_x和grad_y矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y, dstImage;
//【1】载入原图
Mat srcImage = imread("love.jpg");
//【2】显示原图
imshow("【原始图】sobel边缘检测", srcImage);
//【3】求X方向梯度
Sobel(srcImage, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
imshow("【效果图】x方向Sobel", abs_grad_x);
//【4】求Y方向梯度
Sobel(srcImage, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
imshow("【效果图】y方向Sobel", abs_grad_y);
//【5】合并梯度(近似)
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dstImage, -1);
//【6】显示效果图
imshow("【效果图】整体方向Sobel", dstImage);
waitKey(0);
return 0;
}
1.二阶导数可以用来检测边缘, Laplacian算子是n维欧几里得空间中的一个二阶微分算子,定义为梯度grad的散度div,一幅图像减去它的Laplacian算子可以增强对比度
2.如果f是二阶可微的实函数,则f的拉普拉斯算子定义为笛卡尔坐标系xi中的所有非混合二阶偏导数求和:
3.函数:Laplacian()函数
4.函数原型:
void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize=1 , double scale=1, double delta=0, int borderType=BORDER_DEFAULT)
5.参数说明:
(1)输入图像
(2)输出的边缘图
(3)输出图像深度
(4)计算二阶导数的滤波器的孔径尺寸,大小必须为正奇数,默认1
(5)计算拉普拉斯值得时候可选的比例因子,默认1
(6)在结果存入目标图之前可选的delta值,默认0
(7)边界模式,默认BORDER_DEFAULT
6.函数主要利用sobel算子的运算,通过加上sobel算子运算的图像x方向和y方向上的导数,来得到载入图像的拉普拉斯变换,sobel算子如下:
ksize=1时,Laplacian()函数采用3*3孔径:
7.调用示例:
#include
#include
#include
using namespace cv;
int main()
{
//【0】变量定义
Mat srcImage, grayImage, dstImage, abs_dstImage;
//【1】载入原图
srcImage = imread("love.jpg");
//【2】显示原图
imshow("【原始图】图像Laplace变换", srcImage);
//【3】使用高斯滤波消除噪声
GaussianBlur(srcImage, srcImage, Size(3, 3), 0, 0, BORDER_DEFAULT);
//【4】转换为灰度图
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
//【5】使用Laplace函数
Laplacian(grayImage, dstImage, CV_16S, 3, 1, 0, BORDER_DEFAULT);
//【6】计算绝对值,将结果转换成8位
convertScaleAbs(dstImage, abs_dstImage);
//【7】显示效果图
imshow("【效果图】图像Laplacian变换", abs_dstImage);
waitKey(0);
return 0;
}
1.计算图像差分:Scharr()函数,与Sobel基本一样,没有ksize核大小:
Scharr(src,dst,ddepth,dx,dy,scale,delta,broderType)
=
Sobel(src,dst,ddepth,dx,dy,CV_SCHARR,scale,delta,borderType)
2.调用示例:
#include
#include
#include
using namespace cv;
int main()
{
//创建grad_x和grad_y矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y, dstImage;
//载入原图
Mat srcImage = imread("love.jpg");
//显示原图
imshow("【原始图】Scharr滤波器", srcImage);
//求x方向梯度
Scharr(srcImage, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
imshow("【效果图】X方向Scharr", abs_grad_x);
//求y方向梯度
Scharr(srcImage, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
imshow("【效果图】Y方向Scharr", abs_grad_y);
//合并梯度(近似)
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dstImage);
//显示效果图
imshow("【效果图】合并梯度后Scharr", dstImage);
waitKey(0);
return 0;
}
/*
效果:
实现canny边缘检测,滑动条控制阈值
实现Sobel边缘检测,滑动条控制内核大小
实现Scharr滤波器
*/
#include
#include
#include
using namespace cv;
//全局变量
Mat g_srcImage, g_grayImage, g_dstImage;
//Canny边缘检测相关变量
Mat g_cannyDetectedEdges;
int g_cannyLowThreshold = 1;//Trackbar位置参数
//Sobel边缘检测相关变量
Mat g_sobelGradient_X, g_sobelGradient_Y;
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
int g_sobelKernelSize = 1;//Trackbar位置参数
//Scharr滤波器相关变量
Mat g_scharrGradient_X, g_scharrGradient_Y;
Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;
//全局函数
static void on_Canny(int, void*);//canny边缘检测窗口滚动条回调函数
static void on_Sobel(int, void*);//Sobel边缘检测窗口滚动条回调函数
void Scharr();//封装了Scharr边缘检测相关代码的函数
int main()
{
//改变console字体颜色
system("color 2F");
//载入原图
g_srcImage = imread("girl.jpg");
if (!g_srcImage.data)
{
printf("载入原图失败~!\n");
return false;
}
//显示原图
namedWindow("【原始图】");
imshow("【原始图】", g_srcImage);
//创建与src同类型和大小的矩阵
g_dstImage.create(g_srcImage.size(), g_srcImage.type());
//将原图转换为灰度图
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
//创建显示窗口
namedWindow("【效果图】Canny边缘检测", WINDOW_AUTOSIZE);
namedWindow("【效果图】Sobel边缘检测", WINDOW_AUTOSIZE);
namedWindow("【效果图】Scharr滤波器", WINDOW_AUTOSIZE);
//创建滑动条
createTrackbar("参数值", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny);
createTrackbar("参数值", "【效果图】Sobel边缘检测", &g_sobelKernelSize, 3, on_Sobel);
//调用回调函数
on_Canny(g_cannyLowThreshold, 0);
on_Sobel(g_sobelKernelSize, 0);
//调用封装了Scharr边缘检测代码的函数
Scharr();
while ((char)waitKey(1) != 27) {}
return 0;
}
//canny边缘检测窗口滚动条回调函数
static void on_Canny(int, void*)
{
//3*3内核降噪
blur(g_grayImage, g_cannyDetectedEdges, Size(3, 3));
//运行Canny算子
Canny(g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold * 3, 3);
//将g_dstImage内所有元素设为0
g_dstImage = Scalar::all(0);
//使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩模,将原图拷贝到目标图
g_srcImage.copyTo(g_dstImage, g_cannyDetectedEdges);
//显示效果图
imshow("【效果图】Canny边缘检测", g_dstImage);
}
static void on_Sobel(int, void*)
{
//求X方向梯度
Sobel(g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, 2*g_sobelKernelSize+1, 1, 1, BORDER_DEFAULT);
convertScaleAbs(g_sobelGradient_X, g_sobelAbsGradient_X);
//求Y方向梯度
Sobel(g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, 2*g_sobelKernelSize+1, 1, 1, BORDER_DEFAULT);
convertScaleAbs(g_sobelGradient_Y, g_sobelAbsGradient_Y);
//合并梯度
addWeighted(g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage);
//显示效果图
imshow("【效果图】Sobel边缘检测", g_dstImage);
}
void Scharr()
{
//求X方向梯度
Scharr(g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(g_scharrGradient_X, g_scharrAbsGradient_X);
//求Y方向梯度
Scharr(g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(g_scharrGradient_Y, g_scharrAbsGradient_Y);
//合并梯度
addWeighted(g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage);
//显示效果图
imshow("【效果图】Scharr滤波器", g_dstImage);
}