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.lib.filters;
011
012import ij.IJ;
013import ij.process.ColorProcessor;
014import ij.process.ImageProcessor;
015import imagingbook.lib.image.ImageAccessor;
016import imagingbook.lib.image.OutOfBoundsStrategy;
017
018/**
019 * This abstract class represents a generic filter that, when applied to 
020 * an {@code ImageProcessor} object performs all pixel-level
021 * iterations automatically.
022 * Concrete implementations of this class need to define only two methods:<br>
023 * {@code float filterPixel(ImageAccessor.Scalar, int, int)} and<br>
024 * {@code float[] filterPixel(ImageAccessor.Rgb, int, int)}.<br>
025 * See {@link LinearFilter} for a sample implementation.
026 * 
027 * <br>
028 * Note that this is experimental code!
029 * 
030 * @author wilbur
031 * @version 2016/11/01
032 * 
033 */
034public abstract class GenericFilter {
035        
036        /* TODO: PASS THE IMAGE PROCESSOR of the original image and
037         * set up width/height, accessors etc., then use apply without processor argument??
038         * Allow source/target to be of different types?
039         * Implement using interfaces (for gray/color)?
040         * 
041         */
042        
043        private OutOfBoundsStrategy obs = OutOfBoundsStrategy.NearestBorder;
044        
045        /**
046         * Set the out-of-bounds strategy of this {@link GenericFilter}. See {@link OutOfBoundsStrategy}.
047         * @param obs the out-of-bounds strategy
048         */
049        public void setOutOfBoundsStrategy(OutOfBoundsStrategy obs) {
050                this.obs = obs;
051        }
052        
053        protected GenericFilter() {
054        }
055        
056        /**
057         * Calculates and returns the filter result for a single pixel
058         * at the given position.
059         * 
060         * @param source the {@link ImageAccessor.Scalar} representing the source (scalar-valued) image
061         * @param u the horizontal pixel position
062         * @param v the vertical pixel position
063         * @return the resulting (scalar) filter value for the specified pixel position
064         */
065        public abstract float filterPixel(ImageAccessor.Scalar source, int u, int v);
066        
067        /**
068         * Calculates and returns the filter result for a single pixel
069         * at the given position.
070         * 
071         * @param source the {@link ImageAccessor.Rgb} representing the source (RGB) image
072         * @param u the horizontal pixel position
073         * @param v the vertical pixel position
074         * @return the resulting (RGB) filter value for the specified pixel position
075         */
076        public abstract float[] filterPixel(ImageAccessor.Rgb source, int u, int v);
077        
078
079        /**
080         * Dispatch work depending on actual (runtime) type of processor.
081         * This is ugly but we want to avoid generic types (which would
082         * not be of much help in this case anyway).
083         * 
084         * @param ip the image this filter is applied to (destructively)
085         */
086        public void applyTo(ImageProcessor ip) {        // TODO: check for target == null?
087                final int w = ip.getWidth();
088                final int h = ip.getHeight();
089                ImageProcessor ipCopy = ip.duplicate();
090 
091                if (ip instanceof ColorProcessor) {
092                        ImageAccessor.Rgb iaOrig = ImageAccessor.Rgb.create(ip, obs, null);
093                        ImageAccessor.Rgb iaCopy = ImageAccessor.Rgb.create(ipCopy, obs, null);
094                        for (int v = 0; v < h; v++) {
095                                for (int u = 0; u < w; u++) {
096                        //int p = (int) filterPixel(iaCopy, u, v);
097                        float[] rgb = filterPixel(iaCopy, u, v);
098                        iaOrig.setPix(u, v, rgb);
099                    }
100                    IJ.showProgress(v, h);
101                }
102                }
103                else {
104                        ImageAccessor.Scalar iaOrig = ImageAccessor.Scalar.create(ip, obs, null);
105                        ImageAccessor.Scalar iaCopy = ImageAccessor.Scalar.create(ipCopy, obs, null);
106                        for (int v = 0; v < h; v++) {
107                                for (int u = 0; u < w; u++) {
108                                        float p = filterPixel(iaCopy, u, v);
109                                        iaOrig.setVal(u, v, p);
110                                }
111                                IJ.showProgress(v, h);
112                        }
113                }
114        }
115
116}