函数介绍
中值滤波是一种典型的非线性滤波,是基于排序统计理论的一种能够有效抑制噪声的非线性信号处理技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,让周围的像素值接近真实的值从而消除孤立的噪声点。该方法在取出脉冲噪声、椒盐噪声的同时能保留图像的边缘细节。这些优良特性是线性滤波所不具备的。
中值滤波首先也得生成一个滤波模板,将该模板内的各像素值进行排序,生成单调上升或单调下降的二维数据序列,二维中值滤波输出为g(x, y)=medf{f(x-k, y-1),(k, l∈w)},其中f(x,y)和g(x,y)分别是原图像和处理后图像, w为输入的二维模板,能够在整幅图像上滑动,通常尺寸为33或55区域,也可以是不同的形状如线状、圆形、十字形、圆环形等。通过从图像中的二维模板取出奇数个数据进行排序,用排序后的中值取代要处理的数据即可。
中值滤波对消除椒盐噪声非常有效,能够克服线性滤波器带来的图像细节模糊等弊端,能够有效保护图像边缘信息,是非常经典的平滑噪声处理方法。在光学测量条纹图像的香味分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。
函数原理
顾名思义,中值滤波选择每个像素的邻域像素中的中值作为输出,或者说中值滤波将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。
例如,取3 x 3的函数窗,计算以点[i,j]为中心的函数窗像素中值步骤如下:
- 按强度值大小排列像素点.
- 选择排序像素集的中间值作为点[i,j]的新值.
这一过程如图下图所示.
一般采用奇数点的邻域来计算中值,但如果像素点数为偶数时,中值就取排序像素中间两点的平均值.
参数说明
Imgproc.medianBlur(Mat src, Mat dst, int ksize);
src : 图片。图像为1、3、4通道的图像,当模板尺寸为3或5时,图像深度只能为CV_8U、CV_16U、CV_32F中的一个,如而对于较大孔径尺寸的图片,图像深度只能是CV_8U。
dst : 目标图
ksize : 滤波模板的尺寸大小,必须是大于1的奇数,如3、5、7……
代码实现
自己实现的中值滤波只是大致实现,没有实现对模板尺寸的进行奇偶判断等细节
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.util.Arrays;
import java.util.Stack;
public class OpencvTest {
static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
public static void main(String[] args) {
OpencvTest opencvTest = new OpencvTest();
//读入图片
Mat src = Imgcodecs.imread("G:\\opencvPhoto\\photo\\timg.jpg");
//OpenCV中值滤波对比图
Mat temp = src.clone();
//自己实现的中值滤波
Mat dst = opencvTest.medianBlur(src, 5);
Imgcodecs.imwrite("G:\\opencvPhoto\\result\\myMedianBlur.jpg", dst);
//OpenCV中值滤波
Imgproc.medianBlur(temp, temp, 5);
Imgcodecs.imwrite("G:\\opencvPhoto\\result\\medianBlur.jpg", temp);
//验证自己实现的中值滤波是否与OpenCV的一致
boolean flag = true;
for (int row = 0; row < src.rows(); row++) {
for (int col = 0; col < src.cols(); col++) {
if (dst.get(row, col)[0] != src.get(row, col)[0]
|| dst.get(row, col)[1] != src.get(row, col)[1]
|| dst.get(row, col)[2] != src.get(row, col)[2]) {
flag = false;
break;
}
}
if (!flag){
break;
}
}
System.out.println(flag);
}
/**
* 中值滤波
* @param mat 原图片
* @param ksize 内核尺寸
* @return
*/
public Mat medianBlur(Mat mat, int ksize) {
int extendSize = ksize / 2;
Mat copyMakeBorder = new Mat(mat.size(), mat.type());
Core.copyMakeBorder(mat, copyMakeBorder, extendSize, extendSize, extendSize, extendSize, Core.BORDER_REPLICATE );
Mat medianBlurMat = new Mat(mat.size(), mat.type());
int x = 0;
int y = 0;
for (int row = extendSize; row < copyMakeBorder.rows() - extendSize; row++) {
for (int col = extendSize; col < copyMakeBorder.cols() - extendSize; col++) {
double[] color = new double[mat.channels()];
for (int channel = 0; channel < mat.channels(); channel++) { //每个通道进行滤波
double[] pixArr = new double[ksize * ksize];
int count = 0;
for (int i = extendSize * -1; i <= extendSize; i++) {
for (int j = extendSize * -1; j <= extendSize; j++) {
pixArr[count++] = copyMakeBorder.get(row + i, col + j)[channel];
}
}
Arrays.sort(pixArr); //排序
if ((count & 1) == 0) { //偶数
color[channel] = (pixArr[pixArr.length / 2] + pixArr[pixArr.length / 2 - 1]) / 2; //选取中间两点的平均值
}
else { //奇数
color[channel] = pixArr[pixArr.length / 2]; //选取中位数
}
}
medianBlurMat.put(y, x, color);
x++;
}
y++;
x =0;
}
return medianBlurMat;
}
}
输出
true
图片效果
原图
中值滤波
边界填充说明
https://blog.csdn.net/wangwenjie1997/article/details/101094943
参考链接
https://blog.csdn.net/poem_qianmo/article/details/23184547
https://blog.csdn.net/qq_41553038/article/details/79984462