4. 各向异性扩散滤波
本文作者:图像与视觉InSight 行者 杨尚朋 转载请注明
目录
4.1 处理效果展示
图像处理前后对比
处理前
处理后
4.2 各向异性扩散滤波原理
4.2.1 概念
各向异性扩散滤波(Anisotropic Filter),同中值滤波、高斯滤波一样,也是一种滤除图像噪音的方法。可以用于边缘检测或提取之前的预处理,去除无关噪声,增加边缘的有效性。某些基于各向异性滤波的边缘提取算法,在滤波的同时还能兼顾边缘保持。要了解各向异性扩散滤波,先来了解几个概念:各向异性,各向异性扩散。
各向异性,指物体的全部或者部分物理、化学性质随方向的不同而有所变化的特性。例如石墨单晶的电导率在不同方向的差异可达数千倍,又如天文学上,宇宙微波背景辐射亦拥有些微的各向异性。许多的物理量都具有各向异性,如弹性模量、电导率、在酸中的溶解速度等。
各向异性扩散,是有选择性的平滑(扩散)过程,这种选择是指方向选择的非均匀性,这种平滑过程在均匀的区域不受限制,而在跨越边界部分被抑制,因此在平滑噪声的同时保持图像的边缘特征。
4.2.2 原理
假设图像为I,滤波公式如下:
从上式可以看出,各向异性扩散滤波依然是求出核窗口内邻域权值,再进行加权相加,作为当前点的灰度值。其中,t代表迭代次数;△表示梯度算子,四个方向的梯度公式如下:
由上式可以看出,在灰度值变化不大的区域,等号左边的数值可正可负,也变化不大。但是,若在一个方向上出现了边界,即灰度值变化较大,等号左边会出现绝对值较大的数值。
c表示扩散系数,四个方向上的扩散系数计算如下:
由上式可以看出,在灰度值变化不大的区域,等号左边的数值接近或等于1,但是在灰度值变化较大的区域,比如边界,等号左边的数值就会接近或逼近0。
注意:在这里热传导系数K越大,图像越平滑;整个公式需要设置的参数主要有三个,迭代次数 t,根据情况设置;导热系数相关的 k,取值越大,结果越平滑,越不易保留边缘;λ 取值越大,结果越平滑。
讲到这里,我们可以看出各向异性扩散滤波与其他方法的不同。(1)应用方面,该方法滤除噪声时,保边效果好(2)原理方面,将图像看做热量场,所进行的操作完全依照热量场内热的传播原理;(3)核窗口为十字形,而非N×N的矩阵。
4.3 代码展示
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
float k = 15;
float lambda = 0.25;
int N = 5;
void anisotropy_demo(Mat &image, Mat &result);
int main(int argc, char** argv)
{
Mat src = imread("C:\\Users\\SYYSP\\Desktop\\BLOG\\imageMaterial\\GaoQueenOri.jpg");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
imshow("input image", src);
vector<Mat> mv;
vector<Mat> results;
split(src, mv);
for (int n = 0; n < mv.size(); n++)
{
Mat m = Mat::zeros(src.size(), CV_32FC1);
mv[n].convertTo(m, CV_32FC1);
results.push_back(m);
}
int w = src.cols;
int h = src.rows;
Mat copy = Mat::zeros(src.size(), CV_32FC1);
for (int i = 0; i < N; i++)
{
anisotropy_demo(results[0], copy);
copy.copyTo(results[0]);
anisotropy_demo(results[1], copy);
copy.copyTo(results[1]);
anisotropy_demo(results[2], copy);
copy.copyTo(results[2]);
}
Mat output;
normalize(results[0], results[0], 0, 255, NORM_MINMAX);
normalize(results[1], results[1], 0, 255, NORM_MINMAX);
normalize(results[2], results[2], 0, 255, NORM_MINMAX);
results[0].convertTo(mv[0], CV_8UC1);
results[1].convertTo(mv[1], CV_8UC1);
results[2].convertTo(mv[2], CV_8UC1);
Mat dst;
merge(mv, dst);
imshow("result", dst);
imwrite("C:\\Users\\SYYSP\\Desktop\\BLOG\\imageMaterial\\GaoQueenOri_AnisotropicDiffusionFilter.jpg", dst);
waitKey(0);
return 0;
}
void anisotropy_demo(Mat &image, Mat &result)
{
int width = image.cols;
int height = image.rows;
// 四邻域梯度
float n = 0, s = 0, e = 0, w = 0;
// 四邻域系数
float nc = 0, sc = 0, ec = 0, wc = 0;
float k2 = k * k;
for (int row = 1; row < height - 1; row++)
{
for (int col = 1; col < width - 1; col++)
{
// gradient
n = image.at<float>(row - 1, col) - image.at<float>(row, col);
s = image.at<float>(row + 1, col) - image.at<float>(row, col);
e = image.at<float>(row, col - 1) - image.at<float>(row, col);
w = image.at<float>(row, col + 1) - image.at<float>(row, col);
nc = exp(-n * n / k2);
sc = exp(-s * s / k2);
ec = exp(-e * e / k2);
wc = exp(-w * w / k2);
result.at<float>(row, col) = image.at<float>(row, col) + lambda * (n*nc + s * sc + e * ec + w * wc);
}
}
}