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 Ch08_Binary_Regions; 011 012import ij.IJ; 013import ij.ImagePlus; 014import ij.gui.GenericDialog; 015import ij.plugin.filter.PlugInFilter; 016import ij.process.ByteProcessor; 017import ij.process.ImageProcessor; 018import imagingbook.common.color.sets.BasicAwtColor; 019import imagingbook.common.geometry.hulls.AxisAlignedBoundingBox; 020import imagingbook.common.ij.DialogUtils; 021import imagingbook.common.ij.IjUtils; 022import imagingbook.common.ij.overlay.ColoredStroke; 023import imagingbook.common.ij.overlay.ShapeOverlayAdapter; 024import imagingbook.common.regions.BinaryRegion; 025import imagingbook.common.regions.RegionContourSegmentation; 026import imagingbook.core.plugin.IjPluginName; 027import imagingbook.core.jdoc.JavaDocHelp; 028import imagingbook.sampleimages.GeneralSampleImage; 029 030import java.util.List; 031 032import static imagingbook.common.ij.IjUtils.noCurrentImage; 033 034/** 035 * <p> 036 * This ImageJ plugin creates a binary region segmentation, calculates the center and major axis and subsequently the 037 * major axis-aligned bounding box for each binary region (connected component). See Sec. 8.6.4 of [1] for additional 038 * details. Requires a binary image. Zero-value pixels are considered background, all other pixels are foreground. 039 * Display lookup tables (LUTs) are not considered. The resulting bounding box is shown as a vector overlay on top of a 040 * new image, the original image is not modified. If no image is currently open, the plugin optionally loads a suitable 041 * sample image. 042 * </p> 043 * <p> 044 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 045 * (2022). 046 * </p> 047 * 048 * @author WB 049 * @version 2022/06/23 050 */ 051@IjPluginName("Axis-Aligned Bounding Box") 052public class Axis_Aligned_Bounding_Box implements PlugInFilter, JavaDocHelp { 053 054 /** Color of the bounding-box outline. */ 055 public static BasicAwtColor DrawingColor = BasicAwtColor.Blue; 056 public static double StrokeWidth = 0.5; 057 058 private ImagePlus im; 059 060 /** 061 * Constructor, asks to open a predefined sample image if no other image is currently open. 062 */ 063 public Axis_Aligned_Bounding_Box() { 064 if (noCurrentImage()) { 065 DialogUtils.askForSampleImage(GeneralSampleImage.ToolsSmall); 066 } 067 } 068 069 // ---------------------------------------------------------------- 070 071 @Override 072 public int setup(String arg, ImagePlus im) { 073 this.im = im; 074 return DOES_8G; 075 } 076 077 @Override 078 public void run(ImageProcessor ip) { 079 if (!IjUtils.isBinary(ip)) { 080 IJ.showMessage("Plugin requires a binary image!"); 081 return; 082 } 083 084 if (!runDialog()) 085 return; 086 087 RegionContourSegmentation segmenter = new RegionContourSegmentation((ByteProcessor) ip); 088 List<BinaryRegion> regions = segmenter.getRegions(); 089 if (regions.isEmpty()) { 090 IJ.error("No regions detected!"); 091 return; 092 } 093 094 ImageProcessor ip2 = ip.duplicate(); 095 ip2.add(128); // brighten 096 097 // draw bounding boxes as vector overlay 098 099 ShapeOverlayAdapter ola = new ShapeOverlayAdapter(); 100 ola.setStroke(new ColoredStroke(StrokeWidth, DrawingColor.getColor())); 101 102 for (BinaryRegion r : regions) { 103 if (r.getSize() > 5) { 104 AxisAlignedBoundingBox box = new AxisAlignedBoundingBox(r); 105 ola.addShapes(box.getShapes()); 106 } 107 } 108 109 im.setOverlay(ola.getOverlay()); 110 } 111 112 // -------------------------------------------------------------------------- 113 114 private boolean runDialog() { 115 GenericDialog gd = new GenericDialog(Region_Contours_Demo.class.getSimpleName()); 116 gd.addHelp(getJavaDocUrl()); 117 gd.addEnumChoice("Drawing color", DrawingColor); 118 gd.addNumericField("Stroke width", StrokeWidth, 1); 119 120 gd.showDialog(); 121 if (gd.wasCanceled()) { 122 return false; 123 } 124 125 DrawingColor = gd.getNextEnumChoice(BasicAwtColor.class); 126 StrokeWidth = gd.getNextNumber(); 127 return true; 128 } 129 130}