提示:Emgu CV中有很多滤波函数,但是偏偏没有最简单的最大值、最小值滤波。
今天讲最简单的最大值滤波,也就是像素点Point(X,Y)和周边的像素点进行比较,取最大的值替换掉Point(X,Y)的值。所以,最大值滤波,可以消除椒噪声,也就是去除暗斑,但会增强亮斑。
滤波核也叫滤波器,是Emgu CV中决定如何选择邻域像素点值判断的依据,滤波的过程称为卷积,这个在机器学习领域也是一个常见的操作,意思和这里一样。
滤波核包括两个概念:
假如这里有一张原始图片,是单通道的灰度图,某个像素点P(x,y)值是66,见下图:
假如我的滤波核是15,也就是以66这个像素点为中心,水平方向取1个邻域,垂直方向取5个邻域,那么取出来的数据就是:
最大值滤波时,每个点像素值权重是1,很显然,这里的最大值就是226,目标图片中P(x,y)这个点的值就是226。
如果滤波核是33,那取出来的数据就是:
最大值滤波结果就变成了211,如果滤波核是5*5呢,结果就是254。这就能看出来几个问题:
选择不同的滤波核,有不同的结果。
滤波核越大,选取的范围越大,计算量也越大。
滤波核都是奇数,因为目标点是中心,左右的取值距离和上下的取值距离是相同的。
原始素材定义为srcMat,如下:
这是一个脸上长了雀斑的卡通人物,注意哈,雀斑颜色偏暗,最适合最大值滤波。
C#内最大值滤波代码如下:
int ksizeX = 3;
int ksizeY = 3;
int width = srcMat.Cols;
int height = srcMat.Rows;
Mat dstMat = new Mat(new System.Drawing.Size(width, height), DepthType.Cv8U, 1);
Mat tempMat = srcMat.Clone();
Image<Bgr, int> dstImage = new Image<Bgr, int>(width, height);
Image<Bgr, int> tempImage = tempMat.ToImage<Bgr, int>();
int blueValue;
int greenValue;
int redValue;
int minX = 0 - (ksizeX / 2);
int maxX = (ksizeX / 2) + 1;
int minY = 0 - (ksizeY / 2);
int maxY = (ksizeY / 2) + 1;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; ++j)
{
blueValue = 0;
greenValue = 0;
redValue = 0;
// 在邻域内查找
for (int m = minY; m < maxY; m++)
{
for (int n = minX; n < maxX; n++)
{
if (i + m > -1 && j + n > -1 && i + m < height && j + n < width)
{
if (tempImage.Data[i + m, j + n, 0] > blueValue)
{
blueValue = tempImage.Data[i + m, j + n, 0];
}
if (tempImage.Data[i + m, j + n, 1] > greenValue)
{
greenValue = tempImage.Data[i + m, j + n, 1];
}
if (tempImage.Data[i + m, j + n, 2] > redValue)
{
redValue = tempImage.Data[i + m, j + n, 2];
}
}
}
}
dstImage.Data[i, j, 0] = blueValue;
dstImage.Data[i, j, 1] = greenValue;
dstImage.Data[i, j, 2] = redValue;
}
}
dstMat = dstImage.Mat;
dstMat.ConvertTo(dstMat, DepthType.Cv8U);
CvInvoke.Imshow("Max value fliter, " + dstMat.Size.ToString(), dstMat);
代码很简单,原理如下:
1、滤波核是3*3,也就是以目标点为中心,要取到左上、上、右上、左、右、左下、下、右下共八个点和目标点进行比较。
2、按照原始图像的height和width进行遍历,计算目标图像的值。
3、ksizeX=3,因此minX = -1, maxX = 1。ksizeY=3,因此minY = -1, maxY = 1,假如正在计算Point(100,67),也就是第101行,68列。那么要再进行循环遍历,垂直方向是66,67,68,水平方向就是99,100,101,一共判断九个点,取各个通道的最大值。
4、if (i + m > -1 && j + n > -1 && i + m < height && j + n < width) 这个判断,就是让计算范围不能超过图像的上下左右边界。
看一看,是不是脸上偏暗的黑点消失了。
这里还有一张图片,是利用上一篇的添加噪声的方法,分别添加了2000个黑色椒噪声和2000个白色盐噪声,如下图:
还是利用上述代码,只不过将让 int ksizeX = 5,int ksizeY = 5,输出结果就变成了这样:
是不是就像是我上面说的,最大值滤波去掉暗斑,但是会增强亮斑???
最大值滤波很好理解,用起来效果也非常明显,但没有官方函数,只能自己写。读者们可以自己封装一个函数,方便以后直接调用,大概是这样就可以:
public static void MaxValueFliter(
IInputArray src, // 输入目标图像
IOutputArray dst, // 输出图像,与src具有相同大小和类型。
int kernelX = 3, // X方向滤波核大小,默认是3
int kernelY = 3 // Y方向滤波核大小,默认是3
)
原创不易,请勿抄袭。共同进步,相互学习。