图像处理之双边滤波效果(Bilateral Filtering for Gray and Color Image)

简介: 图像处理之双边滤波效果(Bilateral Filtering for Gray and Color Image)

图像处理之双边滤波效果(Bilateral Filtering for Gray and Color Image)

基本介绍:


普通的时空域的低通滤波器,在像素空间完成滤波以后,导致图像的边缘部分也变得不那么明显,


整张图像都变得同样的模糊,图像边缘细节丢失。双边滤波器(ABilateral Filter)可以很好的保


留边缘的同时消除噪声。双边滤波器能做到这些原因在于它不像普通的高斯/卷积低通滤波,只考


虑了位置对中心像素的影响,它还考虑了卷积核中像素与中心像素之间相似程度的影响,根据位置


影响与像素值之间的相似程度生成两个不同的权重表(WeightTable),在计算中心像素的时候加以


考虑这两个权重,从而实现双边低通滤波。据说AdobePhotoshop的高斯磨皮功能就是应用了


双边低通滤波算法实现。

1342078704_9839.png

1342078723_2014.jpg

1342078813_2502.png



程序效果:

1342078852_8793.png


看我们的美女lena应用双边滤镜之后 1342078869_9126.png

1342081250_2497.png



程序关键代码解释:


建立距离高斯权重表(Weight Table)如下:

private void buildDistanceWeightTable() {
  int size = 2 * radius + 1;
  cWeightTable = new double[size][size];
  for(int semirow = -radius; semirow <= radius; semirow++) {
    for(int semicol = - radius; semicol <= radius; semicol++) {
      // calculate Euclidean distance between center point and close pixels
      double delta = Math.sqrt(semirow * semirow + semicol * semicol)/ds;
      double deltaDelta = delta * delta;
      cWeightTable[semirow+radius][semicol+radius] = Math.exp(deltaDelta * factor);
    }
  }
}

建立RGB值像素度高斯权重代码如下:

private void buildSimilarityWeightTable() {
  sWeightTable = new double[256]; // since the color scope is 0 ~ 255
  for(int i=0; i<256; i++) {
    double delta = Math.sqrt(i * i ) / rs;
    double deltaDelta = delta * delta;
    sWeightTable[i] = Math.exp(deltaDelta * factor);
  }
}

完成权重和计算与像素×权重和计算代码如下:

for(int semirow = -radius; semirow <= radius; semirow++) {
  for(int semicol = - radius; semicol <= radius; semicol++) {
    if((row + semirow) >= 0 && (row + semirow) < height) {
      rowOffset = row + semirow;
    } else {
      rowOffset = 0;
    }
    
    if((semicol + col) >= 0 && (semicol + col) < width) {
      colOffset = col + semicol;
    } else {
      colOffset = 0;
    }
    index2 = rowOffset * width + colOffset;
    ta2 = (inPixels[index2] >> 24) & 0xff;
        tr2 = (inPixels[index2] >> 16) & 0xff;
        tg2 = (inPixels[index2] >> 8) & 0xff;
        tb2 = inPixels[index2] & 0xff;
        
        csRedWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tr2 - tr))];
        csGreenWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tg2 - tg))];
        csBlueWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tb2 - tb))];
        
        csSumRedWeight += csRedWeight;
        csSumGreenWeight += csGreenWeight;
        csSumBlueWeight += csBlueWeight;
        redSum += (csRedWeight * (double)tr2);
        greenSum += (csGreenWeight * (double)tg2);
        blueSum += (csBlueWeight * (double)tb2);
  }
}

完成归一化,得到输出像素点RGB值得代码如下:

tr = (int)Math.floor(redSum / csSumRedWeight);
tg = (int)Math.floor(greenSum / csSumGreenWeight);
tb = (int)Math.floor(blueSum / csSumBlueWeight);
outPixels[index] = (ta << 24) | (clamp(tr) << 16) | (clamp(tg) << 8) | clamp(tb);

关于什么卷积滤波,请参考:

http://blog.csdn.net/jia20003/article/details/7038938

关于高斯模糊算法,请参考:

http://blog.csdn.net/jia20003/article/details/7234741

最后想说,不给出源代码的博文不是好博文,基于Java完成的双边滤波速度有点慢

可以自己优化,双边滤镜完全源代码如下:

package com.gloomyfish.blurring.study;
/**
 *  A simple and important case of bilateral filtering is shift-invariant Gaussian filtering
 *  refer to - http://graphics.ucsd.edu/~iman/Denoising/
 *  refer to - http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
 *  thanks to cyber
 */
import java.awt.image.BufferedImage;
 
public class BilateralFilter extends AbstractBufferedImageOp {
  private final static double factor = -0.5d;
  private double ds; // distance sigma
  private double rs; // range sigma
  private int radius; // half length of Gaussian kernel Adobe Photoshop 
  private double[][] cWeightTable;
  private double[] sWeightTable;
  private int width;
  private int height;
  
  public BilateralFilter() {
    this.ds = 1.0f;
    this.rs = 1.0f;
  }
  
  private void buildDistanceWeightTable() {
    int size = 2 * radius + 1;
    cWeightTable = new double[size][size];
    for(int semirow = -radius; semirow <= radius; semirow++) {
      for(int semicol = - radius; semicol <= radius; semicol++) {
        // calculate Euclidean distance between center point and close pixels
        double delta = Math.sqrt(semirow * semirow + semicol * semicol)/ds;
        double deltaDelta = delta * delta;
        cWeightTable[semirow+radius][semicol+radius] = Math.exp(deltaDelta * factor);
      }
    }
  }
  
  /**
   * for gray image
   * @param row
   * @param col
   * @param inPixels
   */
  private void buildSimilarityWeightTable() {
    sWeightTable = new double[256]; // since the color scope is 0 ~ 255
    for(int i=0; i<256; i++) {
      double delta = Math.sqrt(i * i ) / rs;
      double deltaDelta = delta * delta;
      sWeightTable[i] = Math.exp(deltaDelta * factor);
    }
  }
  
  public void setDistanceSigma(double ds) {
    this.ds = ds;
  }
  
  public void setRangeSigma(double rs) {
    this.rs = rs;
  }
 
  @Override
  public BufferedImage filter(BufferedImage src, BufferedImage dest) {
    width = src.getWidth();
        height = src.getHeight();
        //int sigmaMax = (int)Math.max(ds, rs);
        //radius = (int)Math.ceil(2 * sigmaMax);
        radius = (int)Math.max(ds, rs);
        buildDistanceWeightTable();
        buildSimilarityWeightTable();
        if ( dest == null )
          dest = createCompatibleDestImage( src, null );
 
        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        int index = 0;
    double redSum = 0, greenSum = 0, blueSum = 0;
    double csRedWeight = 0, csGreenWeight = 0, csBlueWeight = 0;
    double csSumRedWeight = 0, csSumGreenWeight = 0, csSumBlueWeight = 0;
        for(int row=0; row<height; row++) {
          int ta = 0, tr = 0, tg = 0, tb = 0;
          for(int col=0; col<width; col++) {
            index = row * width + col;
            ta = (inPixels[index] >> 24) & 0xff;
                tr = (inPixels[index] >> 16) & 0xff;
                tg = (inPixels[index] >> 8) & 0xff;
                tb = inPixels[index] & 0xff;
                int rowOffset = 0, colOffset = 0;
                int index2 = 0;
                int ta2 = 0, tr2 = 0, tg2 = 0, tb2 = 0;
            for(int semirow = -radius; semirow <= radius; semirow++) {
              for(int semicol = - radius; semicol <= radius; semicol++) {
                if((row + semirow) >= 0 && (row + semirow) < height) {
                  rowOffset = row + semirow;
                } else {
                  rowOffset = 0;
                }
                
                if((semicol + col) >= 0 && (semicol + col) < width) {
                  colOffset = col + semicol;
                } else {
                  colOffset = 0;
                }
                index2 = rowOffset * width + colOffset;
                ta2 = (inPixels[index2] >> 24) & 0xff;
                    tr2 = (inPixels[index2] >> 16) & 0xff;
                    tg2 = (inPixels[index2] >> 8) & 0xff;
                    tb2 = inPixels[index2] & 0xff;
                    
                    csRedWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tr2 - tr))];
                    csGreenWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tg2 - tg))];
                    csBlueWeight = cWeightTable[semirow+radius][semicol+radius]  * sWeightTable[(Math.abs(tb2 - tb))];
                    
                    csSumRedWeight += csRedWeight;
                    csSumGreenWeight += csGreenWeight;
                    csSumBlueWeight += csBlueWeight;
                    redSum += (csRedWeight * (double)tr2);
                    greenSum += (csGreenWeight * (double)tg2);
                    blueSum += (csBlueWeight * (double)tb2);
              }
            }
            
        tr = (int)Math.floor(redSum / csSumRedWeight);
        tg = (int)Math.floor(greenSum / csSumGreenWeight);
        tb = (int)Math.floor(blueSum / csSumBlueWeight);
        outPixels[index] = (ta << 24) | (clamp(tr) << 16) | (clamp(tg) << 8) | clamp(tb);
                
                // clean value for next time...
                redSum = greenSum = blueSum = 0;
                csRedWeight = csGreenWeight = csBlueWeight = 0;
                csSumRedWeight = csSumGreenWeight = csSumBlueWeight = 0;
                
          }
        }
        setRGB( dest, 0, 0, width, height, outPixels );
        return dest;
  }
  
  public static int clamp(int p) {
    return p < 0 ? 0 : ((p > 255) ? 255 : p);
  }
 
  public static void main(String[] args) {
    BilateralFilter bf = new BilateralFilter();
    bf.buildSimilarityWeightTable();
  }
}

转载文章请务必注明出自本博客

相关文章
|
机器学习/深度学习 算法 测试技术
低照度增强算法(图像增强+目标检测+代码)
低照度增强算法(图像增强+目标检测+代码)
|
机器学习/深度学习 人工智能 数据可视化
ShuffleNet:极致轻量化卷积神经网络(分组卷积+通道重排)
我们引入了一个高效计算的CNN结构名字叫做shuffleNet,这个结构被设计用来解决部署算力非常有限的移动设备问题,这个新的结构使用了两个新的操作,pointwise group convolution 和 channel shuffle能够在极大减少计算量的同时保持一定的精度。我们在ImageNet classification和MS COCO目标检测数据集上做实验论证了ShuffleNet和其他的结构相比有着很好的性能。比如,相比于mobilenet,shufflenet在ImageNet 分类任务上有着更低的top-1错误率(错误率是7.8%)需要的计算量为40MFLOPs。在一个AR
4020 0
ShuffleNet:极致轻量化卷积神经网络(分组卷积+通道重排)
|
机器学习/深度学习 人工智能 算法
深度学习入门:理解神经网络与反向传播算法
【9月更文挑战第20天】本文将深入浅出地介绍深度学习中的基石—神经网络,以及背后的魔法—反向传播算法。我们将通过直观的例子和简单的数学公式,带你领略这一技术的魅力。无论你是编程新手,还是有一定基础的开发者,这篇文章都将为你打开深度学习的大门,让你对神经网络的工作原理有一个清晰的认识。
|
11月前
|
Rust Java 测试技术
还在用 Jmeter 做压测?试试 oha 吧!你会毫不犹豫的爱上它!
在 Web 服务与 API 性能测试中,选择合适的工具至关重要。本文介绍基于 Rust 的高效性能测试工具 **OHA**,并与经典工具 **JMeter** 对比。OHA 以其高性能、低资源占用和简洁易用的特点脱颖而出,适合高并发场景下的快速测试。而 JMeter 功能丰富、支持多协议,适合复杂测试需求。两者各有优势,选择需根据具体场景决定。OHA 安装简单,命令行操作便捷,是性能测试的新利器。
545 0
还在用 Jmeter 做压测?试试 oha 吧!你会毫不犹豫的爱上它!
|
算法 数据处理 Python
高精度保形滤波器Savitzky-Golay的数学原理、Python实现与工程应用
Savitzky-Golay滤波器是一种基于局部多项式回归的数字滤波器,广泛应用于信号处理领域。它通过线性最小二乘法拟合低阶多项式到滑动窗口中的数据点,在降噪的同时保持信号的关键特征,如峰值和谷值。本文介绍了该滤波器的原理、实现及应用,展示了其在Python中的具体实现,并分析了不同参数对滤波效果的影响。适合需要保持信号特征的应用场景。
2042 11
高精度保形滤波器Savitzky-Golay的数学原理、Python实现与工程应用
|
小程序 JavaScript
微信小程序中的页面跳转(通过点击按钮、调用方法的形式)
这篇文章讲解了微信小程序中页面跳转的方法,包括使用按钮点击事件和调用方法实现跳转,以及推荐使用`this.pageRouter.navigateTo`代替`wx.navigateTo`的方式。
微信小程序中的页面跳转(通过点击按钮、调用方法的形式)
|
机器学习/深度学习 PyTorch 算法框架/工具
|
算法 C语言
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
1050 0
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
|
UED
【Qt 学习笔记】Qt窗口 | 工具栏 | QToolBar的使用及说明
【Qt 学习笔记】Qt窗口 | 工具栏 | QToolBar的使用及说明
2828 2
|
缓存 小程序 前端开发
使用 Snapdragon Profiler 分析 Android 应用的 GPU 内存泄露
前几天同事发现一个正在开发的小程序在反复进入退出时,应用的 GPU 内存占用会一直上涨直到触发 OOM,因为小程序使用了内核作为渲染引擎,所以怀疑是内核发生内存泄露,让我帮忙分析看看。 > Snapdragon Profiler Snapshot Capture 进入小程序后,使用 [Snapdragon Profiler][1] Snapshot Capture 抓取了当前帧的
3505 0