001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.demos.utils;
031
032import java.awt.event.MouseEvent;
033import java.awt.event.MouseListener;
034import java.util.ArrayList;
035import java.util.List;
036
037import javax.swing.JFrame;
038
039import org.openimaj.feature.local.list.LocalFeatureList;
040import org.openimaj.image.DisplayUtilities;
041import org.openimaj.image.MBFImage;
042import org.openimaj.image.colour.RGBColour;
043import org.openimaj.image.feature.local.engine.ipd.InterestPointImageExtractorProperties;
044import org.openimaj.image.feature.local.interest.InterestPointData;
045import org.openimaj.image.feature.local.interest.InterestPointVisualiser;
046import org.openimaj.image.feature.local.keypoints.InterestPointKeypoint;
047import org.openimaj.image.processing.convolution.FGaussianConvolve;
048import org.openimaj.math.geometry.point.Point2dImpl;
049import org.openimaj.math.geometry.shape.Ellipse;
050import org.openimaj.math.geometry.shape.Rectangle;
051
052/**
053 * {@link MouseListener} for the IPD demos
054 * 
055 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
056 */
057public class FeatureClickListener implements MouseListener {
058
059        private List<InterestPointData> points = null;
060        private MBFImage image;
061        private boolean areaSelected = false;
062        private Point2dImpl pressClick;
063        private Point2dImpl releaseClick;
064        private JFrame displayFrame;
065        private Rectangle selectedArea;
066
067        /**
068         * Construct the listener
069         */
070        public FeatureClickListener() {
071                DisplayUtilities.createNamedWindow("blurwarp", "Warped Blurred patch", true);
072                DisplayUtilities.createNamedWindow("blurnorm", "Normal Blurred patch", true);
073                DisplayUtilities.createNamedWindow("warp", "Warpped patch", true);
074                DisplayUtilities.createNamedWindow("norm", "Normal patch", true);
075        }
076
077        @Override
078        public synchronized void mouseClicked(MouseEvent e) {
079                if (this.points == null)
080                        return;
081                if (this.areaSelected && e.isControlDown()) {
082                        this.areaSelected = false;
083                        this.selectArea(this.image.getBounds());
084                }
085                // double smallestScale = Double.MAX_VALUE;
086                double smallestDistance = Double.MAX_VALUE;
087                Ellipse foundShape = null;
088                InterestPointData foundPoint = null;
089                Point2dImpl clickPoint = new Point2dImpl(e.getPoint().x, e.getPoint().y);
090                for (InterestPointData point : points) {
091                        if (this.selectedArea.isInside(point)) {
092                                Ellipse ellipse = point.getEllipse();
093                                if (ellipse.isInside(clickPoint)) {
094                                        // double currentScale = point.scale;
095                                        // if(currentScale < smallestScale){
096                                        // foundShape = ellipse;
097                                        // foundPoint = point;
098                                        // smallestScale = currentScale;
099                                        // }
100                                        double distance = Math.sqrt(Math.pow(
101                                                        clickPoint.x - point.x, 2)
102                                                        + Math.pow(clickPoint.y - point.y, 2));
103                                        if (distance < smallestDistance) {
104                                                foundShape = ellipse;
105                                                foundPoint = point;
106                                                smallestDistance = distance;
107                                        }
108                                }
109                        }
110                }
111                if (foundShape != null) {
112                        // PolygonExtractionProcessor<S, T> ext = new
113                        // PolygonExtractionProcessor<S,T>(foundShape, image.newInstance(1,
114                        // 1).getPixel(0,0));
115                        FGaussianConvolve blur = new FGaussianConvolve(foundPoint.scale);
116                        InterestPointImageExtractorProperties<Float[], MBFImage> extractWarp = new InterestPointImageExtractorProperties<Float[], MBFImage>(
117                                        image, foundPoint, true);
118                        InterestPointImageExtractorProperties<Float[], MBFImage> extractNorm = new InterestPointImageExtractorProperties<Float[], MBFImage>(
119                                        image, foundPoint, false);
120
121                        int centerX = extractNorm.halfWindowSize;
122                        int centerY = extractNorm.halfWindowSize;
123                        // Clone ellipses
124                        Ellipse warppedEllipse = new Ellipse(centerX, centerY,
125                                        foundPoint.scale, foundPoint.scale, 0);
126                        Ellipse normalEllipse = new Ellipse(centerX, centerY,
127                                        foundShape.getMajor(), foundShape.getMinor(),
128                                        foundShape.getRotation());
129
130                        MBFImage extractedWarpBlurred = extractWarp.image.process(blur);
131                        MBFImage extractedNormBlurred = extractNorm.image.process(blur);
132                        MBFImage extractedWarp = extractWarp.image.clone();
133                        MBFImage extractedNorm = extractNorm.image.clone();
134                        extractedWarpBlurred.drawShape(warppedEllipse, RGBColour.RED);
135                        extractedNormBlurred.drawShape(normalEllipse, RGBColour.RED);
136
137                        extractedWarp.drawShape(warppedEllipse, RGBColour.RED);
138                        extractedNorm.drawShape(normalEllipse, RGBColour.RED);
139
140                        DisplayUtilities.displayName(extractedWarpBlurred, "blurwarp");
141                        DisplayUtilities.displayName(extractedNormBlurred, "blurnorm");
142                        DisplayUtilities.displayName(extractedWarp, "warp");
143                        DisplayUtilities.displayName(extractedNorm, "norm");
144
145                        DisplayUtilities.positionNamed("blurwarp", image.getWidth(), 0);
146                        DisplayUtilities.positionNamed(
147                                        "blurnorm",
148                                        image.getWidth()
149                                                        + DisplayUtilities.createNamedWindow("blurwarp")
150                                                                        .getWidth(), 0);
151                        DisplayUtilities.positionNamed("warp", image.getWidth(),
152                                        DisplayUtilities.createNamedWindow("blurwarp").getHeight());
153                        DisplayUtilities.positionNamed(
154                                        "norm",
155                                        image.getWidth()
156                                                        + DisplayUtilities.createNamedWindow("blurwarp")
157                                                                        .getWidth(), DisplayUtilities
158                                                        .createNamedWindow("blurwarp").getHeight());
159                }
160        }
161
162        @Override
163        public void mousePressed(MouseEvent e) {
164                if (e.isAltDown()) {
165                        pressClick = new Point2dImpl(e.getX(), e.getY());
166                }
167
168        }
169
170        @Override
171        public void mouseReleased(MouseEvent e) {
172                if (e.isAltDown()) {
173                        releaseClick = new Point2dImpl(e.getX(), e.getY());
174                        this.areaSelected = true;
175                        float px = pressClick.getX();
176                        float py = pressClick.getY();
177                        float rx = releaseClick.getX();
178                        float ry = releaseClick.getY();
179                        selectArea(new Rectangle(Math.min(px, rx), Math.min(py, ry),
180                                        Math.abs(px - rx), Math.abs(py - ry)));
181                }
182
183        }
184
185        private void selectArea(Rectangle rectangle) {
186                this.selectedArea = rectangle;
187                List<InterestPointData> pointsInsideRect = new ArrayList<InterestPointData>();
188                for (InterestPointData point : points) {
189                        if (rectangle.isInside(point)) {
190                                pointsInsideRect.add(point);
191                        }
192                }
193                InterestPointVisualiser<Float[], MBFImage> visualiser = InterestPointVisualiser
194                                .visualiseInterestPoints((MBFImage) image, pointsInsideRect);
195                MBFImage out = visualiser.drawPatches(RGBColour.RED, RGBColour.GREEN);
196                DisplayUtilities.display(out, this.displayFrame);
197        }
198
199        @Override
200        public void mouseEntered(MouseEvent e) {
201                // TODO Auto-generated method stub
202
203        }
204
205        @Override
206        public void mouseExited(MouseEvent e) {
207                // TODO Auto-generated method stub
208
209        }
210
211        /**
212         * @return the points
213         */
214        public List<? extends InterestPointData> getPoints() {
215                return points;
216        }
217
218        /**
219         * Set the image
220         * @param points
221         * @param image
222         */
223        public synchronized void setImage(List<? extends InterestPointData> points, MBFImage image) {
224                this.image = image;
225                this.selectedArea = image.getBounds();
226                this.points = new ArrayList<InterestPointData>();
227                for (InterestPointData x : points) {
228                        this.points.add(x);
229                }
230        }
231
232        /**
233         * @return the image
234         */
235        public MBFImage getImage() {
236                return image;
237        }
238
239        /**
240         * Set the image
241         * @param kps
242         * @param image
243         */
244        public void setImage(LocalFeatureList<? extends InterestPointKeypoint<?>> kps, MBFImage image) {
245                this.image = image;
246                this.selectedArea = image.getBounds();
247                this.points = new ArrayList<InterestPointData>();
248                
249                for (InterestPointKeypoint<?> x : kps) {
250                        this.points.add(x.location);
251                }
252        }
253
254        /**
255         * Set the display frame
256         * @param f
257         */
258        public void setDisplayFrame(JFrame f) {
259                this.displayFrame = f;
260        }
261}