图像锐化:边缘检测
图像锐化通常被用于景物边界的检测与提取,其主要目的是突出图像中的细节或增强被模糊了的细节。这里主要利用空间微分来完成锐化处理。
这里是利用c++和opencv完成的,有兴趣的可以联系我要源码。
实现方法:
一阶微分:
从数学微分的角度看,一节微分是描述“数据的变化率”,二阶微分是描述“数据变化率的变化率”,但是处理数字图像时,数据是离散的,幅值有限,因此采用一阶差分来定义微分分子,对于一元函数f(t),一阶微分算子定义如下:
其中∇f(t)其实就是∂f⁄∂t偏导,Δt=1表示t像素位置的偏移量。相减的结果反映了图像变化率的大小,在像素值保持不变的区域,相减结果为0,即为黑色。在像素变化剧烈的区域,差值越大,像素越亮,垂直边缘得到增强。
对于二元图像f(x,y),一阶微分的定义通过梯度来实现,定义如下:
可以看到,一节微分梯度运算分别由沿x与y方向计算微分的结果构成。
1.一阶微分算子实现:
(1)差分边缘检测:
代码实现:
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
//x垂直边缘:考虑到超出界限,所以取结果的绝对值
edgeX.at<uchar>(i,j) = abs(new_image.at<uchar>(i, j) - new_image.at<uchar>(i - 1, j));
//y水平边缘
edgeY.at<uchar>(i,j) = abs(new_image.at<uchar>(i, j) - new_image.at<uchar>(i, j-1));
//对xy同时微分
edgeXY.at<uchar>(i,j)=sqrt(pow(new_image.at<uchar>(i,j)-new_image.at<uchar>(i-1,j),2)+pow(new_image.at<uchar>(i,j) - new_image.at<uchar>(i,j-1),2));
}
结果:
水平检测:能看到图中很多水平边缘都被检测出来了。
垂直检测:
两者结合:
(2)Roberts交叉微分算子
交叉微分算子是在物体的对角线上求梯度,其公式定义如下:
代码实现:
for (int i = 0; i < row-1; i++) {
for (int j = 0; j < col-1; j++) {
edgeXY.at<uchar>(i, j) =abs(new_image.at<uchar>(i+1, j+1) - new_image.at<uchar>(i,j))
+ abs(new_image.at<uchar>(i+1, j) - new_image.at<uchar>(i, j +1));
}
}
结果:
(3)Sobel微分算子
Sobel微分算子是一种奇数大小的模板大小的全方向微分算子,定义如下:
代码实现:
for (int i = 1; i < row - 1; i++) {
for (int j = 1; j < col - 1; j++) {
//x边缘梯度
edgeX.at<uchar>(i,j)=abs(new_image.at<uchar>(i+1,j-1)-new_image.at<uchar>(i-1,j-1))+abs(new_image.at<uchar>(i+1,j)-new_image.at<uchar>(i-1,j))*2+abs(new_image.at<uchar>(i+1,j+1)-new_image.at<uchar>(i-1,j+1));
//y边缘梯度
edgeY.at<uchar>(i,j)=abs(new_image.at<uchar>(i-1,j+1)- new_image.at<uchar>(i-1,j-1))+abs(new_image.at<uchar>(i,j+1)- new_image.at<uchar>(i , j- 1)) * 2
+abs(new_image.at<uchar>(i+1,j+1)-new_image.at<uchar>(i+1,j-1));
//总梯度
edgeXY.at<uchar>(i,j)=sqrt(pow(edgeX.at<uchar>(i,j),2)+pow(edgeY.at<uchar>(i,j),2));
}
}
结果:
很明显能看到Sobel算子对噪声有一定的抑制能力,但是不能完全排除虚假边缘,容易出现多像素宽度。
4)Priwitt微分算子
Priwitt微分算子与Sobel微分算子的思路相似,都是在一个奇数大小的模板中定义其微分运算,其微分算子定义如下:
代码实现:
for (int i = 1; i < row - 1; i++) {
for (int j = 1; j < col - 1; j++) {
//x边缘梯度
edgeX.at<uchar>(i,j)=abs(new_image.at<uchar>(i+1,j-1)-new_image.at<uchar>(i-1,j-1))+abs(new_image.at<uchar>(i+1,j)-new_image.at<uchar>(i-1,j))+abs(new_image.at<uchar>(i+1,j+1)-new_image.at<uchar>(i-1,j+1));
//y边缘梯度
edgeY.at<uchar>(i,j)=abs(new_image.at<uchar>(i-1,j+1)-new_image.at<uchar>(i-1,j-1))+abs(new_image.at<uchar>(i,j+1)-new_image.at<uchar>(i,j-1))
+abs(new_image.at<uchar>(i+1,j+1)-new_image.at<uchar>(i+1,j-1));
//总梯度
edgeXY.at<uchar>(i,j)=sqrt(pow(edgeX.at<uchar>(i,j),2)+pow(edgeY.at<uchar>(i,j),2));
}
}
结果:
上图所示Priwitt微分算子的处理效果,虽然从视觉无法分辨与Sobel微分算子的区别,但其实模板系数的改变在一定程度上增加了抗干扰性。
二、二阶微分算子实现
(1)拉普拉斯算子
二阶微分关心的是图像灰度的突变而不强调灰度缓慢变化的区域,对边缘的定位能力更强。
Laplacian算子是各项同性的,即具有旋转不变性,在一阶微分里,我们是用|dx|+|dy|来近似一个点的梯度的,当图像旋转一个角度时,这个值就变化了,但对于Laplace算子来说不管图像怎么旋转,得到的响应是一样的。其定义如下:
代码实现:
Laplacian(new_image, dst, CV_16S, 3);
convertScaleAbs(dst, dst1);
结果:
由此可见,从视觉上看,基本的拉普拉斯算子对图像的蜕化效果与Sobel区别不大。但能够观察到图像中有一些不连续的检测边缘,可见Laplacian算子丢失了一部分的边缘方向信息。这是由于Laplacian算子对阶跃型边缘点定位准确,但对噪声非常敏感,使得噪声加强,噪声能力差。
(2)Canny算子
Canny算法不是像Roberts、Sobel等这样简单梯度算子或锐化模板,它是在梯度算子基础上,引入了一种能获得抗噪性能好、定位精度高的单像素边缘的计算策略Canny算子,在一阶微分的基础上,增加了非最大值抑制和双阈值检测。
Canny 边缘检测分可为如下几个步骤:
步骤 1:应用高斯滤波去除图像噪声,滤波器的大小也是可变的,高斯核的大小对于边缘检测的效果具有很重要的作用。滤波器的核越大,边缘信息对于噪声的敏感度就越低。不过,核越大,边缘检测的定位错误也会随之增加。通常来说,一个5×5的核能够满足大多数的情况。
步骤 2:计算梯度的幅度与方向。
设高斯函数为:
代码实现:
Mat image_1 = imread("E:/photo/test_jp.jpg");
Mat new_image;
Mat dst;
//先对图像进行高斯平滑处理
GaussianBlur(image_1, dst, Size(5, 5), 0);
cvtColor(image_1, new_image, COLOR_RGB2GRAY); //转变为灰度图
imshow("灰度图", new_image);
Mat dst1;
//Canny算子实现
Canny(dst, dst1, 80, 200);
结果①:这里设置Th_1=80,Th_2=200。
结果②:这里设置Th_1=40,Th_2=100。
根据结果的对比能观察到,较小的阈值能提取到更多的边缘信息。
(3)LOG滤波方法
LOG算子是根据图像的信噪比来求出检测边缘的最优滤波器,该方法首先通过高斯函数对图像进行平滑处理,然后采用拉普拉斯算子,根据二阶导数的过零点来检测图像的边界。
用高斯函数:
代码实现:
//高斯滤波
GaussianBlur(image_1, dst, Size(3, 3), 0);
cvtColor(dst, new_image, COLOR_RGB2GRAY); //转变为灰度图
imshow("灰度图", new_image);
Laplacian(new_image, dst, CV_16S, 3);
Mat dst1;
convertScaleAbs(dst, dst1);
结果:
由结果可以看出。该方法能很好的检测出边缘,抗干扰能力强,边界定位精度高,连续性好。但同时也平滑掉了尖锐的边缘,无法检测尖锐边缘。