OpenCV 均值/标准差滤波器

20

我希望有人试过这么荒唐的想法。我的目标是根据每个像素周围小窗口的标准差,对输入图像进行分割。基本上,这在数学上类似于高斯或方形滤波器,它将应用于编译时(甚至运行时)用户指定的每个像素周围的窗口大小,并且目标数组将包含每个像素处的SD信息,在与原始图像相同大小的图像中。

我的想法是在HSV空间中执行此操作,以便可以轻松地找到色彩均匀的区域(即在色调和饱和度平面上具有小的局部SD),并从图像中提取它们进行更深入的处理。

那么问题是,是否有人曾经构建过这样的自定义滤波器?我不知道如何在简单的箱式滤波器内执行SD操作,例如高斯和模糊滤波器中使用的那些滤波器,所以我猜我必须使用FilterEngine结构。另外,我忘了提到我是在C++中进行此操作。

非常感谢您的建议和思考。

2个回答

50

维基百科对标准差有一个很好的解释,您可以将其用于标准差过滤。

基本上,它归结为使用盒式滤波器模糊图像,使用盒式滤波器模糊图像的平方,并取它们之间的差的平方根。

更新:这可能更容易理解,可以使用维基百科上的公式... enter image description here

可以将OpenCV的blur函数视为感兴趣邻域的期望值(即E[X] a.k.a.样本均值)。 在这种情况下,随机样本X由局部邻域中的图像像素表示。 因此,通过使用上述等价性,我们有类似于sqrt(blur(img^2) - blur(img)^2)在OpenCV中。 以这种方式进行操作可以计算局部均值和标准差。

此外,如果您对数学证明感兴趣,以下是一个等价的数学公式(计算方差的计算公式)

以下是在OpenCV中执行此操作的方法:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

Mat mat2gray(const Mat& src)
{
    Mat dst;
    normalize(src, dst, 0.0, 1.0, NORM_MINMAX);
    return dst;
}

int main()
{
    Mat image = imread("coke-can.jpg", 0);

    Mat image32f;
    image.convertTo(image32f, CV_32F);

    Mat mu;
    blur(image32f, mu, Size(3, 3));

    Mat mu2;
    blur(image32f.mul(image32f), mu2, Size(3, 3));

    Mat sigma;
    cv::sqrt(mu2 - mu.mul(mu), sigma);

    imshow("coke", mat2gray(image32f));
    imshow("mu", mat2gray(mu));
    imshow("sigma",mat2gray(sigma));
    waitKey();
    return 0;
}

这会生成以下图像:

原始图像

输入图像描述

平均值图像

输入图像描述

标准差图像

输入图像描述

希望对你有所帮助!


1
这太棒了,谢谢!我一直在思考将平方差值作为单个滤波器核求和的过程,但我发现将它们分开可能效果更好。不过有一个问题,难道你不必先取每个元素与局部均值之间的差异,然后再平方该差异吗?你提到的页面上的SD公式是(sum((x-mu)^2)^(1/2)。我认为你写的是(sum((x^2)-(mu^2)))^(1/2)。然而,可以说你的代码产生了相当准确的结果。 - gankoji
@gankoji 太棒了!很高兴你觉得这个示例有帮助。基本上,我使用了维基百科文章中显示的数学等价性。我还更新了答案,希望能更完整地解释这个问题。 - mevatron
@gankoji 我更新了答案,并附上了等式推导的链接,以防你对数学感兴趣 :) - mevatron
@mevatron 你好,非常棒的教程...我需要计算两个边缘检测图像之间的平均差异...我对OpenCV很陌生,但在Matlab中已经完成了,请帮我用公式在OpenCV中实现。 - user960439
@mevatron:理论 + 代码 + 示例 = "太棒了!!" :) - G453
显示剩余2条评论

1
如果您希望更广泛地使用此功能,则可能会产生NaN值。
接近零的值有时可能是“负数”。
Mat sigma;
cv::sqrt(mu2 - mu.mul(mu), sigma);

正确的方式应该是。
Mat sigma;
cv::sqrt(cv::abs(mu2 - mu.mul(mu)), sigma);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接