001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit http://imagingbook.com for additional details. 008 *******************************************************************************/ 009 010package imagingbook.pub.edgepreservingfilters; 011 012import ij.process.ImageProcessor; 013import imagingbook.lib.image.ImageAccessor; 014 015/** 016 * XY-Separated version of bilateral filter using Gaussian domain and 017 * range kernels. This filter can be applied to all image types. 018 * @author W. Burger 019 * @version 2013/05/30 020 */ 021public class BilateralFilterSeparable extends BilateralFilter { 022 023 enum Direction { 024 Horizontal, Vertical 025 } 026 027 private Direction direction; 028 private float[] Hr; // domain kernel is one-dimensional here! 029 030 public BilateralFilterSeparable() { 031 super(); 032 } 033 034 public BilateralFilterSeparable(double sigmaD, double sigmaR) { 035 super(sigmaD, sigmaR); 036 } 037 038 public BilateralFilterSeparable(Parameters params) { 039 super(params); 040 initialize(); 041 } 042 043 private void initialize() { 044 Hr = makeDomainKernel1D(params.sigmaD, K); 045 } 046 047 // overrides the corresponding method in GenericFilter 048 public void applyTo(ImageProcessor target) { 049 // apply this filter twice, with 'direction' set to different values: 050 for (Direction d : Direction.values()) { 051 direction = d; 052 //IJ.showStatus("filter direction: " + direction.name()); 053 super.applyTo(target); // call original method 054 } 055 } 056 057 // ------------------------------------------------------ 058 059 public float filterPixel(ImageAccessor.Scalar I, int u, int v) { 060 float a = I.getVal(u, v); 061 float S = 0; 062 float W = 0; 063 if (direction == Direction.Horizontal) { 064 for (int m = -K; m <= K; m++) { 065 float b = I.getVal(u + m, v); 066 float wd = Hr[m + K]; // domain weight 067 float wr = similarityGauss(a, b); // range weight 068 float w = wd * wr; 069 S = S + w * b; 070 W = W + w; 071 } 072 } 073 else { // (direction == Direction.Vertical) 074 for (int n = 0; n <= K; n++) { 075 float b = I.getVal(u, v + n); 076 float wd = Hr[n + K]; // domain weight 077 float wr = similarityGauss(a, b); // range weight 078 float w = wd * wr; 079 S = S + w * b; 080 W = W + w; 081 } 082 } 083 return S / W; 084 } 085 086 public float[] filterPixel(ImageAccessor.Rgb I, int u, int v) { 087 //final int[] a = new int[3]; 088 //final int[] b = new int[3]; 089 final float[] S = new float[3]; // sum of weighted RGB (initialized to zero) 090 float W = 0; // sum of weights 091 final float[] a = I.getPix(u, v); 092 093 if (direction == Direction.Horizontal) { 094 for (int m = -K; m <= K; m++) { 095 final float[] b = I.getPix(u + m, v); 096 float wd = Hr[m + K]; 097 float wr = similarityGauss(a, b); 098 float w = wd * wr; 099 S[0] = S[0] + w * b[0]; 100 S[1] = S[1] + w * b[1]; 101 S[2] = S[2] + w * b[2]; 102 W = W + w; 103 } 104 } 105 else { // (direction == Direction.Vertical) 106 for (int n = -K; n <= K; n++) { 107 final float[] b = I.getPix(u, v + n); 108 float wd = Hr[n + K]; 109 float wr = similarityGauss(a, b); 110 float w = wd * wr; 111 S[0] = S[0] + b[0] * w; 112 S[1] = S[1] + b[1] * w; 113 S[2] = S[2] + b[2] * w; 114 W = W + w; 115 } 116 } 117 rgb[0] = Math.round(S[0] / W); 118 rgb[1] = Math.round(S[1] / W); 119 rgb[2] = Math.round(S[2] / W); 120 return rgb; 121 } 122 123 // ------------------------------------------------------ 124 125 private float[] makeDomainKernel1D(double sigma, int K) { 126 int size = K + 1 + K; 127 float[] domainKernel = new float[size]; 128 double sigma2 = sigma * sigma; 129 double scale = 1.0 / (Math.sqrt(2 * Math.PI) * sigma); 130 131 for (int i = 0; i < size; i++) { 132 double x = K - i; 133 domainKernel[i] = (float) (scale * Math.exp(-0.5 * (x*x) / sigma2)); 134 } 135 return domainKernel; 136 } 137 138}