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 *******************************************************************************/
009package imagingbook.lib.image;
010
011import ij.process.ImageProcessor;
012import imagingbook.pub.geometry.mappings.linear.AffineMapping;
013import imagingbook.pub.geometry.mappings.linear.LinearMapping;
014import imagingbook.pub.geometry.mappings.linear.ProjectiveMapping;
015
016import java.awt.Point;
017import java.awt.geom.Point2D;
018
019/**
020 * Use to exctract warped images for testing the Lucas-Kanade matcher.
021 * @author wilbur
022 *
023 */
024public class ImageExtractor {
025        
026        
027//      public static ImageProcessor extract(ImageProcessor source, int width, int height, LinearMapping T) {
028//              ImageExtractor ie = new ImageExtractor(source);
029//              return ie.extractImage(width, height, T);
030//      }
031        
032                
033        private int interpolationMethod = ImageProcessor.BILINEAR;
034        private final ImageProcessor I;
035        
036        /**
037         * Creates a new instance of {@link ImageExtractor} for the image {@code I}.
038         * @param I the target image.
039         */
040        public ImageExtractor(ImageProcessor I) {
041                this.I = I;
042        }
043        
044        public void setInterpolationMethod(int interpolationMethod) {
045                this.interpolationMethod = interpolationMethod;
046        }
047        
048        /**
049         * Extracts an image {@code R} of size {@code width} x {@code height} from the source image 
050         * {@code I} (referenced by {@code this} object).
051         * The image {@code R} is extracted from a quadrilateral patch of the source image,
052         * defined by the transformation of the boundary of {@code R} by {@code T(x)}.
053         * @param width the width of the target image {@code R}.
054         * @param height the height of the target image {@code R}.
055         * @param T a {@link LinearMapping} object.
056         * @return the extracted image {@code R}, which is of the same type as the source image.
057         */     
058
059        public ImageProcessor extractImage(int width, int height, LinearMapping T) {
060                ImageProcessor R = I.createProcessor(width, height);
061                extractImage(R, T);
062                return R;
063        }
064        
065        public ImageProcessor extractImage(int width, int height, Point2D[] sourcePnts) {
066                ImageProcessor R = I.createProcessor(width, height);
067                ProjectiveMapping T = getMapping(width, height, sourcePnts);
068                extractImage(R, T);
069                return R;
070        }
071        
072        /**
073         * Fills the image {@code R} from the source image 
074         * {@code I} (referenced by {@code this} object).
075         * The image {@code R} is extracted from a quadrilateral patch of the source image,
076         * defined by the transformation of the boundary of {@code R} by {@code T(x)}.
077         * Grayscale and color images my not be mixed (i.e., {@code R} must be of the same type as {@code I}).
078         * @param R the image to be filled.
079         * @param T a {@link LinearMapping} object.
080         */     
081        public void extractImage(ImageProcessor R, LinearMapping T) {
082                int prevInterpolationMethod = I.getInterpolationMethod();
083                // save current interpolation method
084                I.setInterpolationMethod(interpolationMethod);
085                
086                ImageAccessor iaI = ImageAccessor.create(I);
087                ImageAccessor iaR = ImageAccessor.create(R);
088        
089                int wT = R.getWidth();
090                int hT = R.getHeight();
091                for (int u = 0; u < wT; u++) {
092                        for (int v = 0; v < hT; v++) {
093                                Point2D uv = new Point(u, v);
094                                Point2D xy = T.applyTo(uv);
095                                float[] val = iaI.getPix(xy.getX(), xy.getY());
096                                iaR.setPix(u, v, val);
097                        }
098                }
099                // restore interpolation method
100                I.setInterpolationMethod(prevInterpolationMethod);
101        }
102        
103        /**
104         * Extracts a warped sub-image of the associated target image I,
105         * defined by a sequence of 3 or 4 points. In the case of 3 
106         * points in sourcePnts, an {@link AffineMapping} is used; with 4 points,
107         * a {@link ProjectiveMapping} is used. The 3 or 4 points map clockwise to
108         * the corner points of the target image R, starting with the top-left corner.
109         * @param R the target image;
110         * @param sourcePnts an array of 3 or 4 {@link Point2D} objects.
111         */
112        public void extractImage(ImageProcessor R, Point2D[] sourcePnts) {
113                ProjectiveMapping T = getMapping(R.getWidth(), R.getHeight(), sourcePnts);
114                extractImage(R, T);
115        }
116        
117        private ProjectiveMapping getMapping(int w, int h, Point2D[] sourcePnts) {
118                Point2D[] targetPnts = {
119                                new Point(0, 0), new Point(w - 1, 0),
120                                new Point(w - 1, h - 1), new Point(0, h - 1)
121                        };
122                ProjectiveMapping T = null;
123                switch (sourcePnts.length) {
124                case (3) : T = new AffineMapping(targetPnts, sourcePnts); break;
125                case (4) : T = new ProjectiveMapping(targetPnts, sourcePnts); break;
126                default : throw new IllegalArgumentException("wrong number of source points");
127                }
128                return T;
129        }
130        
131}