图像滤波

传统算法

Posted by JAN on August 30, 2019

图像增强是对图像进行处理,使其比原始图像更适合特定的任务。

图像平滑是一种区域增强的算法。

邻域平均法

利用卷积运算对图像领域的像素灰度进行平均,从而达到减小噪声影响、降低图像对比度的目的。主要缺点是在降低噪声的同时使图像变得模糊,卷积核越大,去噪声能力强的同时图像也会越模糊。

out = cv2.blur(in, (5, 5))

方框滤波的原理和邻域平均类似,区别在于函数层面,有一个参数用于控制比如说3x3的卷积核是否除以9.

out = cv2.boxFilter(in, -1, (5, 5), normalize=True)

高斯滤波

为了克服图像模糊的弊端,提出了许多其他滤波方法,出发点是如何选择邻域的大小、形状、方向和权重系数等。高斯滤波和邻域平均的主要区别是权重系数,让邻近的像素具有更高的重要度。

out = cv2.GaussianBlur(in, ksize, sigmaX)

ksize表示卷积核大小,sigmaX控制X方向方差,用于控制权重系数。sigmaX小,表示高斯曲线越尖,滤波效果上模糊程度小,sigmaX大,表示高斯曲线平缓,滤波效果上模糊程度大。

中值滤波

用一个包含奇数个点的窗口在图像上扫描,把窗口中的值按照升序或者降序排序,取位于中间的灰度值替代该点的灰度值。

out = cv2.medianBlur(in, ksize)

ksize必须是大于1的奇数。不同的窗口形状产生不同的滤波效果,方形或圆形的窗口适合外轮廓线较长的物体图形,十字形窗口适合有尖顶角状的物体图形。对于细节较多的图像,可以用多次不同的中值滤波。

中值滤波擅长保护边缘和去除椒盐噪声。

#include <memory.h>

typedef double element;
void medianBlur(const element* signal, element* result, int N) {
	if (!signal || N < 1) return;
	if (N == 1) {
		if (result) result[0] = signal[0];
		else return;
	}
	element* extension = new element[N + 4];
	if (!extension) return;
	memcpy(extension+2, signal, N*sizeof(element));
	for (int i = 0; i < 2; ++i) {
		extension[i] = signal[1 - i];
		extension[N + 3 - i] = signal[N - 2 + i];
	}
	_medianBlur(extension, result, N + 4);
	delete[] extension;
}
void _medianBlur(const element* signal, element* result, int N) {
	for (int i = 2; i < N - 2; ++i) {
		element window[5];
		for (int j = 0; j < 5; ++j) window[j] = signal[i - 2 + j];
		int left = 0, right = 4;
		int mid = 2;
		int div = Partition(window, left, right);
		while (div != mid) {
			if (mid < div) div = Partition(window, left, div - 1);
			else div = Partition(window, div + 1, right);
		}
		result[i - 2] = window[mid];
	}
}
int Partition(element* arr, int left, int right) {
	int tmp = arr[left];
	while (left < right) {
		while (left < right && tmp <= arr[right]) --right;
		swap(arr[left], arr[right]);
		while (left < right && arr[left] < tmp) ++left;
		swap(arr[left], arr[right]);
	}
	arr[left] = tmp;
	return left;
}
// N: height of image
// M: width of image

void medianBlur(const element* signal, element* result, int N, int M) {
	if (!signal || N < 1 || M < 1) return;
	element* extension = new element[(N + 2) * (M + 2)];
	if (!extension) return;
	for (int i = 0; i < N; ++i) {
		memcpy(extension + (i + 1) * (M + 2) + 1, signal + i * M, M*sizeof(element));
		extension[(i + 1) * (M + 2)] = signal[i * M];
		extension[(i + 1) * (M + 2) + M + 1] = signal[i * M + M - 1];
	}
	memcpy(extension, extension + M + 2, (M + 2) * sizeof(element));
	memcpy(extension + (N + 1) * (M + 2), extension + N * (M + 2), (M + 2) * sizeof(element));
	_medianBlur(extension, result, N + 2, M + 2);
	delete[] extension;
}
void _medianBlur(const element* signal, element* result, int N, int M) {
	for (int i = 1; i < N - 1; ++i) {
		for (int j = 1; j < M - 1; ++j) {
			element window[9];
			int k = 0;
			for (int n = i - 1; n < i + 2; ++n) {
				for (int m = j - 1; m < j + 2; ++m) {
					window[k++] = signal[n * M + m];
				}
			}
			int left = 0, right = 8;
			int mid = 4;
			int div = Partition(window, left, right);
			while (div != mid) {
				if (mid < div) div = Partition(window, left, div - 1);
				else div = Partition(window, div + 1, right;)
			}
			result[(i - 1) * (M - 2) + j - 1] = window[mid];
		}
	}
}
void medianBlur(const element* signal, element* result, int N, int M) {
	if (!signal || N < 1 || M < 1) return;
	element* extension = new element[(N + 2) * (M + 2) * 3];
	if (!extension) return;
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			for (int k = 0; k < 3; ++k) {
				extension[(i + 1) * (M + 2) * 3 + (j + 1) * 3 + k] = signal[i * M * 3 + j * 3 + k];
			}
		}
		for (int k = 0; k < 3; ++k) {
			extension[(i + 1) * (M + 2) * 3 + k] = extension[(i + 1) * (M + 2) * 3 + 3 + k];
		}
		for (int k = 0; k < 3; ++k) {
			extension[(i + 1) * (M + 2) * 3 + (M + 1) * 3 + k] = extension[(i + 1) * (M + 2) * 3 + M * 3 + k];
		}
	}
	for (int j = 0; j < M + 2; ++j) {
		for (int k = 0; k < 3; ++k) {
			extension[j * 3 + k] = extension[(M + 2) * 3 + j * 3 + k];
		}
	}
	for (int j = 0; j < M + 2; ++j) {
		for (int k = 0; k < 3; ++k) {
			extension[(N + 1) * (M + 2) * 3 + j * 3 + k] = extension[N * (M + 2) * 3 + j * 3 + k];
		}
	}
	_medianBlur(extension, result, N + 2, M + 2);
	delete[] extension;
}
void _medianBlur(const element* signal, element* result, int N, int M) {
	for (int k = 0; k < 3; ++k) {
		for (int i = 1; i < N - 1; ++i) {
			for (int j = 1; j < M - 1; ++j) {
				element window[9];
				int index = 0;
				for (int n = i - 1; n < i + 2; ++n) {
					for (int m = j - 1; m < j + 2; ++m) {
						window[index++] = signal[n * M * 3 + m * 3 + k];
					}
				}
				int left = 0, right = 8;
				int mid = 4;
				int div = Partition(window, left, right);
				while (mid != div) {
					if (mid < div) div = Partition(window, left, div - 1);
					else div = Partition(window, div + 1, right);
				}
				result[(i - 1) * (M - 2) * 3 + (j - 1) * 3 + k] = window[mid];
			}
		}
	}
}