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}