图形算法与实战:1.滤波专题 (4)各向异性扩散滤波

4. 各向异性扩散滤波

本文作者:图像与视觉InSight 行者 杨尚朋 转载请注明

目录

4. 各向异性扩散滤波

4.1 处理效果展示

图像处理前后对比

4.2 各向异性扩散滤波原理

4.2.1 概念

4.2.2 原理

4.3 代码展示


4.1 处理效果展示

图像处理前后对比

处理前 

处理后

4.2 各向异性扩散滤波原理

4.2.1 概念

各向异性扩散滤波(Anisotropic Filter),同中值滤波、高斯滤波一样,也是一种滤除图像噪音的方法。可以用于边缘检测或提取之前的预处理,去除无关噪声,增加边缘的有效性。某些基于各向异性滤波的边缘提取算法,在滤波的同时还能兼顾边缘保持。要了解各向异性扩散滤波,先来了解几个概念:各向异性,各向异性扩散。

各向异性,指物体的全部或者部分物理、化学性质随方向的不同而有所变化的特性。例如石墨单晶的电导率在不同方向的差异可达数千倍,又如天文学上,宇宙微波背景辐射亦拥有些微的各向异性。许多的物理量都具有各向异性,如弹性模量、电导率、在酸中的溶解速度等。

各向异性扩散,是有选择性的平滑(扩散)过程,这种选择是指方向选择的非均匀性,这种平滑过程在均匀的区域不受限制,而在跨越边界部分被抑制,因此在平滑噪声的同时保持图像的边缘特征。

4.2.2 原理

假设图像为I,滤波公式如下:

        I{_{t+1}}= I{{_{t}}^{}}+\lambda (cN{{_{x,y}}\triangledown {{_{N}}^{}}(I{{_{t}}^{}}) }+cS{{_{x,y}}\triangledown {{_{s}}^{}}(I{{_{t}}^{}}) }+cE{{_{x,y}}\triangledown {{_{E}}^{}}(I{{_{t}}^{}}) }+cW{{_{x,y}}\triangledown {{_{W}}^{}}(I{{_{t}}^{}}) } )

从上式可以看出,各向异性扩散滤波依然是求出核窗口内邻域权值,再进行加权相加,作为当前点的灰度值。其中,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);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_32391345/article/details/106750055