OpenCV 高斯模糊

高斯滤波器能够有效的抑制噪声,平滑图像。高斯滤波器相比于均值滤波器对图像个模糊程度较小。

高斯公式

matlab代码

clear;
a=0;sigma=0.8; 
x=-10:0.0001:10;
figure(1)
y=(1/((sqrt(2*pi))*sigma))*exp(-((x-a).^2)/(2*sigma.^2));
plot(x,y,'b','LineWidth',1.5);
hold on;
sigma=1.1
y=(1/((sqrt(2*pi))*sigma))*exp(-((x-a).^2)/(2*sigma.^2));
plot(x,y,'r','LineWidth',1.5);
legend('sigma = 0.8','sigma = 1.1')

从函数的结果来看,sigma值越大,曲线越“胖”;sigma值越小,曲线越“瘦”;对图像过滤来说,sigma值越大,滤波器中心点的权重也就越大,图像也就没有那么模糊。看一下代码原型和实例 

OpenCV高斯函数原型即部分源码解析

函数原型为

//_src _IN_ 输入的源影像
//_dst _OUT_ 输出的目标影像
//kSize 核大小 如果大小为负数的话,那么根据输入的sigma进行计算
//ksize 大小可以为1,3,5,7,最大值为7
//计算的公式为 sigma = 0.3\*((ksize - 1)\*0.5 - 1) + 0.8
//sigmal1 X方向上的sigma值
//sigmal2 Y方向上的sigma值
//如果sigmal值为负数的话,那么使用默认的滤波过滤器
void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
                   double sigma1, double sigma2,
                   int borderType )

 部分函数解析

void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
                   double sigma1, double sigma2,
                   int borderType )
{
    int type = _src.type();//获取通道类型 CV_8UC3 CV_8UC1...
    Size size = _src.size();//获取影像的大小 宽度和高度
    _dst.create( size, type );//创建目标影像,和源影像的大小和类型是相同的

	//borderType 边界类型参照的博客
	//https://blog.csdn.net/zhanggusheng/article/details/70197051
    if( borderType != BORDER_CONSTANT && (borderType & BORDER_ISOLATED) != 0 )
    {
        if( size.height == 1 )
            ksize.height = 1;
        if( size.width == 1 )
            ksize.width = 1;
    }

	//如果内核的宽度和高度都是1的话,那么源影像和目标影像是相同的
    if( ksize.width == 1 && ksize.height == 1 )
    {
        _src.copyTo(_dst);
        return;
    }

#ifdef HAVE_TEGRA_OPTIMIZATION
    Mat src = _src.getMat();
    Mat dst = _dst.getMat();
    if(sigma1 == 0 && sigma2 == 0 && tegra::useTegra() && tegra::gaussian(src, dst, ksize, borderType))
        return;
#endif

#if IPP_VERSION_X100 >= 801 && 0 // these functions are slower in IPP 8.1
    CV_IPP_CHECK()
    {
        int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);

        if ((depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F) && (cn == 1 || cn == 3) &&
                sigma1 == sigma2 && ksize.width == ksize.height && sigma1 != 0.0 )
        {
            IppiBorderType ippBorder = ippiGetBorderType(borderType);
            if (ippBorderConst == ippBorder || ippBorderRepl == ippBorder)
            {
                Mat src = _src.getMat(), dst = _dst.getMat();
                IppiSize roiSize = { src.cols, src.rows };
                IppDataType dataType = ippiGetDataType(depth);
                Ipp32s specSize = 0, bufferSize = 0;

                if (ippiFilterGaussianGetBufferSize(roiSize, (Ipp32u)ksize.width, dataType, cn, &specSize, &bufferSize) >= 0)
                {
                    IppFilterGaussianSpec * pSpec = (IppFilterGaussianSpec *)ippMalloc(specSize);
                    Ipp8u * pBuffer = (Ipp8u*)ippMalloc(bufferSize);

                    if (ippiFilterGaussianInit(roiSize, (Ipp32u)ksize.width, (Ipp32f)sigma1, ippBorder, dataType, 1, pSpec, pBuffer) >= 0)
                    {
#define IPP_FILTER_GAUSS(ippfavor, ippcn) \
        do \
        { \
            typedef Ipp##ippfavor ippType; \
            ippType borderValues[] = { 0, 0, 0 }; \
            IppStatus status = ippcn == 1 ? \
                ippiFilterGaussianBorder_##ippfavor##_C1R(src.ptr<ippType>(), (int)src.step, \
                    dst.ptr<ippType>(), (int)dst.step, roiSize, borderValues[0], pSpec, pBuffer) : \
                ippiFilterGaussianBorder_##ippfavor##_C3R(src.ptr<ippType>(), (int)src.step, \
                    dst.ptr<ippType>(), (int)dst.step, roiSize, borderValues, pSpec, pBuffer); \
            ippFree(pBuffer); \
            ippFree(pSpec); \
            if (status >= 0) \
            { \
                CV_IMPL_ADD(CV_IMPL_IPP); \
                return; \
            } \
        } while ((void)0, 0)

                        if (type == CV_8UC1)
                            IPP_FILTER_GAUSS(8u, 1);
                        else if (type == CV_8UC3)
                            IPP_FILTER_GAUSS(8u, 3);
                        else if (type == CV_16UC1)
                            IPP_FILTER_GAUSS(16u, 1);
                        else if (type == CV_16UC3)
                            IPP_FILTER_GAUSS(16u, 3);
                        else if (type == CV_16SC1)
                            IPP_FILTER_GAUSS(16s, 1);
                        else if (type == CV_16SC3)
                            IPP_FILTER_GAUSS(16s, 3);
                        else if (type == CV_32FC1)
                            IPP_FILTER_GAUSS(32f, 1);
                        else if (type == CV_32FC3)
                            IPP_FILTER_GAUSS(32f, 3);
#undef IPP_FILTER_GAUSS
                    }
                }
                setIppErrorStatus();
            }
        }
    }
#endif
	//创建高斯内核函数
    Mat kx, ky;
	//创建高斯内核函数
    createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2);
	//_src是源影像
	//_dst是目标影像
	//CV_MAT_DEPTH(type) 是CV_8U或者CV_16U
	//kx是滤波内核
	//ky是滤波内核
	//
    sepFilter2D(_src, _dst, CV_MAT_DEPTH(type), kx, ky, Point(-1,-1), 0, borderType );
}
static void createGaussianKernels( Mat & kx, Mat & ky, int type, Size ksize,
                                   double sigma1, double sigma2 )
{
    int depth = CV_MAT_DEPTH(type);//获取影像的深度 CV_8U、CV_16U...
    if( sigma2 <= 0 )
        sigma2 = sigma1;//如果sigma2小于0的话,那么sigma2和sigma相同

    // automatic detection of kernel size from sigma
	//自动决定sigma的大小
    if( ksize.width <= 0 && sigma1 > 0 )//如果内核的宽度小于0并且sigma1大于的0的话
		//如果数据类型为CV_8U的话 那么宽度大小为
		//(sigmal*3*2+1)|1 
		//'|'的操作原因是内核的宽度必须是奇数
        ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
    if( ksize.height <= 0 && sigma2 > 0 )//和内核的宽度获取是相同的
        ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;

	//内核的宽度必须大于0,并且是奇数的。
	//内核的高度也是相同的
    CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
        ksize.height > 0 && ksize.height % 2 == 1 );

	//sigma值是大于等于0的
    sigma1 = std::max( sigma1, 0. );
    sigma2 = std::max( sigma2, 0. );

	//获取x方向的高斯内核
    kx = getGaussianKernel( ksize.width, sigma1, std::max(depth, CV_32F) );
	//如果内核的宽度和高度相同,并且sigma1和sigma2几乎相同的话,那么就是使用x方向的内核
    if( ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON )
        ky = kx;
    else
        ky = getGaussianKernel( ksize.height, sigma2, std::max(depth, CV_32F) );
}
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
{
    const int SMALL_GAUSSIAN_SIZE = 7;
    static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
    {
        {1.f},//内核大小为1
        {0.25f, 0.5f, 0.25f},//内核大小为3
        {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},//内核大小为5
        {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}//内核大小为7
    };

	//如果sigma小于0的话,那么内核数组规则为
	//1、奇数
	//2、小于等于7
	//3、sigma 小于等于0
	//n>>1 就是n/2
    const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
        small_gaussian_tab[n>>1] : 0;

	//断言,数据类型必须为CV_32F 或者CV_64F,也就是float或者double类型
    CV_Assert( ktype == CV_32F || ktype == CV_64F );
	//内核矩阵为n行1列
    Mat kernel(n, 1, ktype);
	//获取内核数据的指针,分别指向float或者double类型
    float* cf = kernel.ptr<float>();
    double* cd = kernel.ptr<double>();

	//如果sigma是大于0的话,就是sigma的大小
	//如果sigma小于等于0的话,就是默认的大小

    double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
	//-sigma *sigmma/2
    double scale2X = -0.5/(sigmaX*sigmaX);
	//求和,是为了进行归一化处理
    double sum = 0;

    int i;

    for( i = 0; i < n; i++ )
    {
        double x = i - (n-1)*0.5;//例子如果n为3的话,那么x的值分别为-1、0、1。如果是5的话,那么x的值为-2,-1,0,1,2
        double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
        if( ktype == CV_32F )
        {
            cf[i] = (float)t;
            sum += cf[i];
        }
        else
        {
            cd[i] = t;
            sum += cd[i];
        }
    }

    sum = 1./sum;
    for( i = 0; i < n; i++ )
    {
        if( ktype == CV_32F )
            cf[i] = (float)(cf[i]*sum);
        else
            cd[i] *= sum;
    }

    return kernel;
}

 

 

 

//包含OpenCV的头文件
//参照github https://github.com/yoyoyo-yo/Gasyori100knock 
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace  std;
//使用OpenCV的命名空间
using namespace cv;
//
//频道改变
int main()
{
	//读取源影像
	Mat Src = imread("C:/Users/GuSheng/Desktop/标准测试图片/Fig0638(a)(lenna_RGB).tif", IMREAD_COLOR);
	if (Src.empty())
	{
		return 0;
	}
	//创建目标影像,影像格式、大小和源影像相同
	//sigma = 0.8
	Mat Dst1 = Mat(Src.size(), Src.type());
	//sigma = 1.5
	Mat Dst2 = Mat(Src.size(), Src.type());
	//创建目标影像失败
	if (Dst1.empty() || Dst2.empty())
	{
		return 0;
	}
	GaussianBlur(Src, Dst1, cv::Size(5, 5), 0.8,0.8);
	GaussianBlur(Src, Dst2, cv::Size(5, 5), 1.5, 1.5);
	namedWindow("Src", WINDOW_AUTOSIZE);
	namedWindow("Dst sigma = 0.8", WINDOW_AUTOSIZE);
	namedWindow("Dst sigma = 1.5", WINDOW_AUTOSIZE);
	imshow("Src", Src);
	imshow("Dst sigma = 0.8", Dst1);
	imshow("Dst sigma = 1.5", Dst2);
	waitKey(0);
	return 0;
}
源影像

                               

sigma = 0.8
sigma = 1.5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值