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-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009 010package Ch25_SIFT; 011 012import ij.IJ; 013import ij.ImagePlus; 014import ij.gui.GenericDialog; 015import ij.plugin.filter.PlugInFilter; 016import ij.process.FloatProcessor; 017import ij.process.ImageProcessor; 018import imagingbook.common.ij.DialogUtils; 019import imagingbook.common.ij.IjUtils; 020import imagingbook.common.ij.overlay.ColoredStroke; 021import imagingbook.common.ij.overlay.ShapeOverlayAdapter; 022import imagingbook.common.sift.SiftColors; 023import imagingbook.common.sift.SiftDescriptor; 024import imagingbook.common.sift.SiftDetector; 025import imagingbook.common.sift.SiftParameters; 026import imagingbook.core.jdoc.JavaDocHelp; 027import imagingbook.core.resource.ImageResource; 028import imagingbook.sampleimages.GeneralSampleImage; 029 030import java.awt.Color; 031import java.util.List; 032 033import static imagingbook.common.color.sets.ColorEnumeration.getColors; 034 035/** 036 * <p> 037 * This plugin extracts multi-scale SIFT features [1] from the current image and displays them as M-shaped markers. The 038 * list of keypoints (if shown) is sorted by descending magnitude. The input image is always converted to grayscale 039 * before SIFT feature detection is performed. See Ch. 25 of [2] for details. If no image is currently open, the user is 040 * asked to load a predefined sample image. 041 * </p> 042 * <p> 043 * [1] D. G. Lowe. Distinctive image features from scale-invariant keypoints. International Journal of Computer Vision 044 * 60, 91–110 (2004). <br> [2] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic 045 * Introduction</em>, 3rd ed, Springer (2022). 046 * </p> 047 * 048 * @author WB 049 * @version 2022/04/01 050 * @see SiftDetector 051 * @see SiftDescriptor 052 */ 053 054public class SIFT_Detection_Demo implements PlugInFilter, JavaDocHelp { 055 056 private static ImageResource SampleImage = GeneralSampleImage.Castle; 057 private static SiftParameters params = new SiftParameters(); 058 private static int MaxFeaturesToShow = 200; 059 private static double FeatureScale = 1.0; // 1.5; 060 private static double FeatureStrokewidth = 0.5; 061 private static boolean ListSiftFeatures = false; 062 private static Color[] ScaleLevelColors = getColors(SiftColors.class); 063 064 private ImagePlus im; 065 066 /** 067 * Constructor, asks to open a predefined sample image if no other image is currently open. 068 */ 069 public SIFT_Detection_Demo() { 070 if (IjUtils.noCurrentImage()) { 071 DialogUtils.askForSampleImage(SampleImage); 072 } 073 } 074 075 // --------------------------------------------------- 076 077 @Override 078 public int setup(String arg0, ImagePlus im) { 079 this.im = im; 080 return DOES_ALL; 081 } 082 083 @Override 084 public void run(ImageProcessor ip) { 085 if (!runDialog()) { 086 return; 087 } 088 089 FloatProcessor fp = ip.convertToFloatProcessor(); 090 SiftDetector detector = new SiftDetector(fp, params); 091 List<SiftDescriptor> features = detector.getSiftFeatures(); 092 093 ColoredStroke[] fStrokes = new ColoredStroke[ScaleLevelColors.length]; 094 for (int i = 0; i < ScaleLevelColors.length; i++) { 095 fStrokes[i] = new ColoredStroke(FeatureStrokewidth, ScaleLevelColors[i]); 096 } 097 098 ShapeOverlayAdapter ola = new ShapeOverlayAdapter(); 099 100 int cnt = 1; 101 for (SiftDescriptor sf : features) { 102 ColoredStroke stroke = fStrokes[sf.getScaleLevel() % fStrokes.length]; 103 ola.addShape(sf.getShape(FeatureScale), stroke); 104 if(++cnt > MaxFeaturesToShow) break; 105 } 106 107 im.setOverlay(ola.getOverlay()); 108 109 if (ListSiftFeatures) { 110 int n = 1; 111 for (SiftDescriptor sf : features) { 112 IJ.log(n + ": " + sf.toString()); 113 if(++n > MaxFeaturesToShow) break; 114 } 115 } 116 } 117 118 // --------------------------------------------------- 119 120 private boolean runDialog() { 121 GenericDialog gd = new GenericDialog(this.getClass().getSimpleName()); 122 gd.addHelp(getJavaDocUrl()); 123 DialogUtils.addToDialog(params, gd); 124 gd.addNumericField("Max. number of features to show", MaxFeaturesToShow); 125 gd.addCheckbox("List SIFT features)", ListSiftFeatures); 126 127 gd.showDialog(); 128 if (gd.wasCanceled()) { 129 return false; 130 } 131 132 DialogUtils.getFromDialog(params, gd); 133 MaxFeaturesToShow = (int) gd.getNextNumber(); 134 ListSiftFeatures = gd.getNextBoolean(); 135 return true; 136 } 137 138}