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.geometry.mappings;
011
012import ij.process.ImageProcessor;
013import imagingbook.lib.image.ImageAccessor;
014import imagingbook.lib.interpolation.InterpolationMethod;
015
016import java.awt.Point;
017import java.awt.geom.Point2D;
018
019/**
020 * 2013-02-02: changed to use the returned new point of applyTo(Point2D),
021 * not relying on side effect.
022 * 2014-04-19: modified to work with ImageAccessor.
023 * 2015-12-10: added isInverse() method.
024 */
025public abstract class Mapping implements Cloneable {
026        
027        protected boolean isInverseFlag = false;
028        
029        // all subclasses must implement this method
030        public abstract double[] applyTo(double[] pnt);
031
032        
033        public boolean isInverse() {
034                return isInverseFlag;
035        }
036
037        public Mapping getInverse() {
038                if (isInverseFlag)
039                        return this;
040                else {
041                        return this.invert(); // only linear mappings invert
042                }
043        }
044        
045        protected Mapping invert() {
046                throw new UnsupportedOperationException("mapping cannot be inverted");
047        }
048        
049        public Point2D applyTo(Point2D pnt) {
050                double[] xy = applyTo(new double[] {pnt.getX(), pnt.getY()});
051                return new Point2D.Double(xy[0], xy[1]);
052        }
053        
054        /**
055         * Applies this mapping to all points in the pnts array.
056         * @param pnts array of original points.
057         * @return an array of modified points.
058         */
059        public Point2D[] applyTo(Point2D[] pnts) {
060                Point2D[] outPnts = new Point2D[pnts.length];
061                for (int i = 0; i < pnts.length; i++) {
062                        outPnts[i] = applyTo(pnts[i]);
063                }
064                return outPnts;
065        }
066
067        /**
068         * Destructively transforms the image in "ip" using this geometric
069         * mapping and the specified pixel interpolation method.
070         * TODO: this should not be here (geometry only)?
071         * 
072         * @param ip target image to which THIS mapping is applied.
073         * @param im interpolation method.
074         */
075        public void applyTo(ImageProcessor ip, InterpolationMethod im) {
076                // make a temporary copy of the image:
077                ImageProcessor source = ip.duplicate();
078                ImageProcessor target = ip;
079                applyTo(source, target, im);
080                source = null;
081        }
082
083        /**
084         * Transforms the "source" image to the "target" image using this geometric
085         * mapping and the specified pixel interpolation method. Source and target
086         * must be different images!
087         * 
088         * @param source input image (not modified).
089         * @param target output image (modified).
090         * @param im interpolation method.
091         */
092        public void applyTo(ImageProcessor source, ImageProcessor target, InterpolationMethod im) {
093                if (target == source) {
094                        throw new IllegalArgumentException("source and target image must not be the same");
095                }
096                ImageAccessor sourceAcc = ImageAccessor.create(source, null, im);
097                ImageAccessor targetAcc = ImageAccessor.create(target);
098                applyTo(sourceAcc, targetAcc);
099        }
100
101
102        /**
103         * Transforms the source image (contained in "srcInterpol") to the "target"
104         * image using this geometric mapping and the specified pixel interpolator.
105         * 
106         * @param sourceAcc accessor to the source image
107         * @param targetAcc accessor to the target image
108         */
109        public void applyTo(ImageAccessor sourceAcc, ImageAccessor targetAcc) {
110                Mapping invMap = this.getInverse(); // get inverse mapping
111                ImageProcessor target = targetAcc.getProcessor();
112                final int w = target.getWidth();
113                final int h = target.getHeight();
114                for (int v = 0; v < h; v++) {
115                        for (int u = 0; u < w; u++) {
116                                Point2D sourcePt = invMap.applyTo(new Point(u, v));
117                                float[] val = sourceAcc.getPix(sourcePt.getX(),sourcePt.getY());
118                                targetAcc.setPix(u, v, val);
119                        }
120                }
121        }
122
123        public Mapping duplicate() { // duplicates any mapping, overwrite
124                return this.clone();
125        }
126        
127        protected Mapping clone() {
128                Mapping copy = null;
129                try {
130                        copy = (Mapping) super.clone();
131                } 
132                catch (CloneNotSupportedException e) { }
133                return copy;
134        }
135
136}