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}