联合双边滤波器(joint bilateral filter) 代码及详细注释【OpenCV】


原理部分可以参看前一篇博客


void jointBilateralFilter(const Mat &src, Mat &dst, int d, double sigma_color,
                          double sigma_space, Mat &joint = Mat(), int borderType =
                              BORDER_REPLICATE)
{
    Size size = src.size();
    if (dst.empty())
        dst = Mat::zeros(src.size(), src.type());

    CV_Assert(
        (src.type() == CV_8UC1 || src.type() == CV_8UC3)
        && src.type() == dst.type() && src.size() == dst.size()
        && src.data != dst.data);
    if (sigma_color <= 0)
        sigma_color = 1;
    if (sigma_space <= 0)
        sigma_space = 1;

    double gauss_color_coeff = -0.5 / (sigma_color * sigma_color);
    double gauss_space_coeff = -0.5 / (sigma_space * sigma_space);

    if (joint.empty())
        src.copyTo(joint);

    const int cn = src.channels();
    const int cnj = joint.channels();

    int radius;
    if (d <= 0)
        radius = cvRound(sigma_space * 1.5);	// 根据 sigma_space 计算 radius
    else
        radius = d / 2;
    radius = MAX(radius, 1);
    d = radius * 2 + 1;	// 重新计算 像素“矩形”邻域的直径d,确保是奇数

	// 扩展 src 和 joint 长宽各2*radius
    Mat jim;
    Mat sim;
    copyMakeBorder(joint, jim, radius, radius, radius, radius, borderType);
    copyMakeBorder(src, sim, radius, radius, radius, radius, borderType);

	// cnj: joint的通道数
    vector<float> _color_weight(cnj * 256);
    vector<float> _space_weight(d * d);	 // (2*radius + 1)^2
    vector<int> _space_ofs_jnt(d * d);
    vector<int> _space_ofs_src(d * d);
    float *color_weight = &_color_weight[0];
    float *space_weight = &_space_weight[0];
    int *space_ofs_jnt = &_space_ofs_jnt[0];
    int *space_ofs_src = &_space_ofs_src[0];

    // initialize color-related bilateral filter coefficients
	// 色差的高斯权重
    for (int i = 0; i < 256 * cnj; i++)
        color_weight[i] = (float) std::exp(i * i * gauss_color_coeff);

    int maxk = 0;	// 0 - (2*radius + 1)^2
    // initialize space-related bilateral filter coefficients
    for (int i = -radius; i <= radius; i++)
    {
        for (int j = -radius; j <= radius; j++)
        {
            double r = std::sqrt((double) i * i + (double) j * j);
            if (r > radius)
                continue;
            space_weight[maxk] = (float) std::exp(r * r * gauss_space_coeff);
            space_ofs_jnt[maxk] = (int) (i * jim.step + j * cnj);			// joint 邻域内的相对坐标 (i, j)【偏移量】, 左上角为(-radius, -radius),右下角为(radius, radius)
            space_ofs_src[maxk++] = (int) (i * sim.step + j * cn);		// src 邻域内的相对坐标 (i, j)
        }
    }
    #pragma omp parallel for
    for (int i = 0; i < size.height; i++)
    {
        const uchar *jptr = jim.data + (i + radius) * jim.step + radius * cnj;	// &jim.ptr(i+radius)[radius]
        const uchar *sptr = sim.data + (i + radius) * sim.step + radius * cn; // &sim.ptr(i+radius)[radius]
        uchar *dptr = dst.data + i * dst.step;												// dst.ptr(i)

		// src 和 joint 通道数不同的四种情况
        if (cn == 1 && cnj == 1)
        {
            for (int j = 0; j < size.width; j++)
            {
                float sum = 0, wsum = 0;
                int val0 = jptr[j];	// jim.ptr(i + radius)[j + radius]

                for (int k = 0; k < maxk; k++)
                {
                    int val = jptr[j + space_ofs_src[k]];	 // jim.ptr(i + radius + offset_x)[j + radius + offset_y]
                    int val2 = sptr[j + space_ofs_src[k]];	// sim.ptr(i + radius + offset_x)[j + radius + offset_y]

					// 根据joint当前像素和邻域像素的 距离权重 和 色差权重,计算综合的权重
                    float w = space_weight[k]
                              * color_weight[std::abs(val - val0)];
                    sum += val2 * w;	// 统计 src 邻域内的像素带权和
                    wsum += w;			// 统计权重和
                }
                // overflow is not possible here => there is no need to use CV_CAST_8U
				// 归一化 src 邻域内的像素带权和,并赋给 dst对应的像素
                dptr[j] = (uchar) cvRound(sum / wsum);
            }
        }
        else if (cn == 3 && cnj == 3)
        {
            for (int j = 0; j < size.width * 3; j += 3)
            {
                float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
                int b0 = jptr[j], g0 = jptr[j + 1], r0 = jptr[j + 2];	// jim.ptr(i + radius)[j + radius][0...2]
                for (int k = 0; k < maxk; k++)
                {
                    const uchar *sptr_k = jptr + j + space_ofs_src[k];	
                    const uchar *sptr_k2 = sptr + j + space_ofs_src[k];

                    int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];	 // jim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]
                    float w = space_weight[k]
                              * color_weight[std::abs(b - b0) + std::abs(g - g0)
                                             + std::abs(r - r0)];
                    sum_b += sptr_k2[0] * w;	// sim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]
                    sum_g += sptr_k2[1] * w;
                    sum_r += sptr_k2[2] * w;
                    wsum += w;
                }
                wsum = 1.f / wsum;
                b0 = cvRound(sum_b * wsum);
                g0 = cvRound(sum_g * wsum);
                r0 = cvRound(sum_r * wsum);
                dptr[j] = (uchar) b0;
                dptr[j + 1] = (uchar) g0;
                dptr[j + 2] = (uchar) r0;
            }
        }
        else if (cn == 1 && cnj == 3)
        {
            for (int j = 0, l = 0; j < size.width * 3; j += 3, l++)
            {
                float sum_b = 0, wsum = 0;
				
                int b0 = jptr[j], g0 = jptr[j + 1], r0 = jptr[j + 2];	// jim.ptr(i + radius)[j + radius][0...2]
                for (int k = 0; k < maxk; k++)
                {
					int val = *(sptr + l + space_ofs_src[k]);	// sim.ptr(i + radius + offset_x)[l + radius + offset_y]

                    const uchar *sptr_k = jptr + j + space_ofs_jnt[k];
                    int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];		// jim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]

                    float w = space_weight[k]
                              * color_weight[std::abs(b - b0) + std::abs(g - g0)
                                             + std::abs(r - r0)];
                    sum_b += val * w;
                    wsum += w;
                }
                wsum = 1.f / wsum;
                b0 = cvRound(sum_b * wsum);
                dptr[l] = (uchar) b0;
            }
        }
        else if (cn == 3 && cnj == 1)
        {
            for (int j = 0, l = 0; j < size.width * 3; j += 3, l++)
            {
                float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
                int val0 = jptr[l];	// jim.ptr(i + radius)[l + radius]
                for (int k = 0; k < maxk; k++)
                {
                    int val = jptr[l + space_ofs_jnt[k]];	// jim.ptr(i + radius + offset_x)[l + radius + offset_y]

                    const uchar *sptr_k = sptr + j + space_ofs_src[k];		// sim.ptr(i + radius + offset_x)[j + radius + offset_y] 

                    float w = space_weight[k]
                              * color_weight[std::abs(val - val0)];

                    sum_b += sptr_k[0] * w;	// sim.ptr(i + radius + offset_x)[j + radius + offset_y] [0...2]
                    sum_g += sptr_k[1] * w;
                    sum_r += sptr_k[2] * w;
                    wsum += w;
                }

                // overflow is not possible here => there is no need to use CV_CAST_8U
                wsum = 1.f / wsum;
                dptr[j] = (uchar) cvRound(sum_b * wsum);
                dptr[j + 1] = (uchar) cvRound(sum_g * wsum);
                dptr[j + 2] = (uchar) cvRound(sum_r * wsum);
            }
        }
    }
}

效果如图:




  • 1
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
双边滤波是一种可以同时考虑空间距离和像素值差异的滤波方法,可以在去除图像噪声的同时保留图像细节。下面是双边滤波的Matlab代码实现: ```matlab function [output_image] = bilateral_filter(input_image, sigma_d, sigma_r) % 双边滤波函数 % input_image: 输入图像 % sigma_d: 空域滤波的标准差 % sigma_r: 像素值差异滤波的标准差 % 将输入图像转换为double类型 input_image = im2double(input_image); % 获取输入图像的大小 [M, N, ~] = size(input_image); % 初始化输出图像 output_image = zeros(M, N, 3); % 定义空域滤波 [X, Y] = meshgrid(-ceil(3*sigma_d):ceil(3*sigma_d)); space_filter = exp(-(X.^2+Y.^2)/(2*sigma_d^2)); % 对于每个像素进行滤波 for i = 1:M for j = 1:N % 计算像素值差异滤波 pixel_filter = exp(-((input_image(i,j,1)-input_image(:,:,1)).^2+... (input_image(i,j,2)-input_image(:,:,2)).^2+... (input_image(i,j,3)-input_image(:,:,3)).^2)/(2*sigma_r^2)); % 计算双边滤波 bilateral_filter = space_filter .* pixel_filter; % 归一化 bilateral_filter = bilateral_filter / sum(bilateral_filter(:)); % 计算输出像素值 output_image(i,j,1) = sum(sum(input_image(:,:,1).*bilateral_filter)); output_image(i,j,2) = sum(sum(input_image(:,:,2).*bilateral_filter)); output_image(i,j,3) = sum(sum(input_image(:,:,3).*bilateral_filter)); end end % 将输出图像转换为uint8类型 output_image = uint8(output_image*255); ``` 使用方法: ```matlab input_image = imread('input_image.png'); sigma_d = 2; sigma_r = 0.1; output_image = bilateral_filter(input_image, sigma_d, sigma_r); imshow(output_image); ``` 其中,`input_image.png`为输入图像的文件名,`sigma_d`为空域滤波的标准差,`sigma_r`为像素值差异滤波的标准差。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShaderJoy

您的打赏是我继续写博客的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值