图像增强是对图像进行处理,使其比原始图像更适合特定的任务。
图像平滑是一种区域增强的算法。
邻域平均法
利用卷积运算对图像领域的像素灰度进行平均,从而达到减小噪声影响、降低图像对比度的目的。主要缺点是在降低噪声的同时使图像变得模糊,卷积核越大,去噪声能力强的同时图像也会越模糊。
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];
}
}
}
}