引言:
图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波。我们知道微分运算是求信号的变化率,具有加强高频分量的作用。在空域运算中来说,对图像的锐化就是计算微分。由于数字图像的离散信号,微分运算就变成计算差分或梯度。图像处理中有多种边缘检测(梯度)算子,常用的包括普通一阶差分,Robert算子(交叉差分),Sobel算子等等,是基于寻找梯度强度。拉普拉斯算子(二阶差分)是基于过零点检测。通过计算梯度,设置阀值,得到边缘图像。
Canny算法:
Canny边缘检测是从不同视觉对象中提取有用的结构信息并大大减少要处理的数据量的一种技术,目前已广泛应用于各种计算机视觉系统。Canny发现,在不同视觉系统上对边缘检测的要求较为类似,因此,可以实现一种具有广泛应用意义的边缘检测技术。边缘检测的一般标准包括:
1) 以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。
2) 检测到的边缘应精确定位在真实边缘的中心。
3) 图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。
Canny算子求边缘点具体算法步骤如下:
1. 用高斯滤波器平滑图像- GaussianBlur
2.灰度转换 - cvtColor
2. 用一阶偏导有限差分计算梯度幅值和方向 – Sobel/Scharr
3. 对梯度幅值进行非极大值抑制.
非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对于标准3,对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0,对梯度图像中每个像素进行非极大值抑制的算法是:
1) 将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
2) 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
4. 用双阈值算法检测和连接边缘.
在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。为了解决这些杂散响应,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
l推荐的高低阈值比值为 T2: T1 = 3:1/2:1其中T2为高阈值,T1为低阈值。
被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
通过上述步骤即可完成基于Canny算法的边缘提取。
API:
#include "pch.h"
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int threshold_low = 50;
int threshold_low_max = 125;
void Canny_demo(int pos, void* userdata);
Mat src,graysrc,edge;
int main()
{
src = imread("F:\\visual studio\\Image\\building2.jpg");
if (src.empty())
{
cout << "Can't load the image" << endl;
return -1;
}
imshow("src", src);
//边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,
//因此必须采用滤波器来改善与噪声有关的边缘检测器的性能
Mat blursrc;
GaussianBlur(src, blursrc, Size(5, 5), 3, 3);
//转化为灰度图
//可有可无 Canny会自动转化为单通道8bit
cvtColor(blursrc, graysrc, cv::COLOR_BGR2GRAY);
imshow("graysrc", graysrc);
namedWindow("edge", WINDOW_AUTOSIZE);
createTrackbar("Threshold", "edge", &threshold_low, threshold_low_max, Canny_demo);
Canny_demo(threshold_low, 0);
waitKey(0);
}
void Canny_demo(int pos, void* userdata)
{
//Canny边缘检测
Canny(graysrc, edge, pos, 2 * pos, 3, false);
imshow("edge", edge);
//edge在边缘位置为255,其余位置为0,用其作为掩码,使边缘显示它本来的颜色.
/*Mat dst;
src.copyTo(dst, edge);
imshow("edge", dst);*/
}
效果展示: