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 ******************************************************************************/ 009package More_; 010 011import ij.IJ; 012import ij.ImagePlus; 013import ij.gui.GenericDialog; 014import ij.gui.Overlay; 015import ij.plugin.filter.PlugInFilter; 016import ij.process.ImageProcessor; 017import imagingbook.common.corners.Corner; 018import imagingbook.common.corners.GradientCornerDetector.Parameters; 019import imagingbook.common.corners.HarrisCornerDetector; 020import imagingbook.common.geometry.basic.Pnt2d; 021import imagingbook.common.geometry.delaunay.DelaunayTriangulation; 022import imagingbook.common.geometry.delaunay.Triangle; 023import imagingbook.common.ij.DialogUtils; 024import imagingbook.common.ij.overlay.ColoredStroke; 025import imagingbook.common.ij.overlay.ShapeOverlayAdapter; 026import imagingbook.core.jdoc.JavaDocHelp; 027import imagingbook.core.resource.ImageResource; 028import imagingbook.sampleimages.GeneralSampleImage; 029 030import java.awt.BasicStroke; 031import java.awt.Color; 032import java.util.List; 033 034import static imagingbook.common.ij.IjUtils.noCurrentImage; 035 036 037/** 038 * This ImageJ plugin performs corner detection on the active image, applies Delaunay triangulation to the N 039 * strongest corners and displays the result as a vector overlay on top of the same image. If no image is currently 040 * open, the user is asked to load a predefined sample image. 041 * 042 * @author WB 043 * @version 2022/06/25 044 * @see HarrisCornerDetector 045 * @see DelaunayTriangulation 046 */ 047public class Delaunay_Demo implements PlugInFilter, JavaDocHelp { 048 049 private static ImageResource SampleImage = GeneralSampleImage.MortarSmall; 050 051 private static int CornerCount = 0; // number of corners to show (0 = show all) 052 private static boolean ShuffleCorners = true; 053 private static Color DelaunayColor = Color.green; // color for graph edges 054 private static Color PointColor = Color.red; // color for point markers 055 private static double StrokeWidth = 0.25; 056 private static double PointRadius = 1.0; 057 058 private static Parameters params = new Parameters(); 059 060 private ImagePlus im = null; 061 062 /** 063 * Constructor, asks to open a predefined sample image if no other image is currently open. 064 */ 065 public Delaunay_Demo() { 066 if (noCurrentImage()) { 067 DialogUtils.askForSampleImage(SampleImage); 068 } 069 } 070 071 @Override 072 public int setup(String arg, ImagePlus im) { 073 this.im = im; 074 return DOES_ALL; 075 } 076 077 @Override 078 public void run(ImageProcessor ip) { 079 080 if (!runDialog()) { 081 return; 082 } 083 084 HarrisCornerDetector cd = new HarrisCornerDetector(ip, params); 085 List<Corner> corners = cd.getCorners(); 086 087 DelaunayTriangulation dt = DelaunayTriangulation.from(corners, ShuffleCorners); 088 im.setOverlay(makeOverlay(dt)); 089 } 090 091 // --------------------------------------------------------------------------- 092 093 private Overlay makeOverlay(DelaunayTriangulation dt) { 094 List<Triangle> triangles = dt.getTriangles(); 095 List<Pnt2d> allPoints = dt.getPoints(); 096 097 ShapeOverlayAdapter ola = new ShapeOverlayAdapter(); 098 099 // draw the triangles: 100 ColoredStroke triangleStroke = new ColoredStroke(StrokeWidth, DelaunayColor); 101 triangleStroke.setEndCap(BasicStroke.CAP_ROUND); 102 triangleStroke.setLineJoin(BasicStroke.JOIN_ROUND); 103 104 ola.setStroke(triangleStroke); 105 for (Triangle trgl : triangles) { 106 ola.addShape(trgl.getShape()); 107 } 108 109 // draw the vertices of the triangulation: 110 ColoredStroke pointStroke = new ColoredStroke(StrokeWidth, PointColor); 111 ola.setStroke(pointStroke); 112 for (Pnt2d p : allPoints) { 113 // get original Pnt2d shape (not Corner shape) 114 ola.addShape(Pnt2d.from(p).getShape(PointRadius)); 115 } 116 117 return ola.getOverlay(); 118 } 119 120 // --------------------------------------------------------------------------- 121 122 private boolean runDialog() { 123 GenericDialog gd = new GenericDialog(this.getClass().getSimpleName()); 124 gd.addHelp(getJavaDocUrl()); 125 gd.addNumericField("Corner score threshold", params.scoreThreshold, 0); 126 gd.addNumericField("Border width", params.border, 0); 127 gd.addCheckbox("Clean up corners", params.doCleanUp); 128 gd.addNumericField("Corners to show (0 = show all)", CornerCount, 0); 129 gd.addCheckbox("Shuffle corners", ShuffleCorners); 130 131 gd.showDialog(); 132 if(gd.wasCanceled()) 133 return false; 134 135 params.scoreThreshold = (int) gd.getNextNumber(); 136 params.border = (int) gd.getNextNumber(); 137 params.doCleanUp = gd.getNextBoolean(); 138 CornerCount = (int) gd.getNextNumber(); 139 ShuffleCorners = gd.getNextBoolean(); 140 141 if(gd.invalidNumber() || !params.validate()) { 142 IJ.error("Input Error", "Invalid input"); 143 return false; 144 } 145 return true; 146 } 147}