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 Ch21_Geometric_Operations; 010 011import ij.ImagePlus; 012import ij.gui.GenericDialog; 013import ij.plugin.filter.PlugInFilter; 014import ij.process.ImageProcessor; 015import imagingbook.common.geometry.mappings.linear.AffineMapping2D; 016import imagingbook.common.geometry.mappings.linear.Rotation2D; 017import imagingbook.common.geometry.mappings.linear.Translation2D; 018import imagingbook.common.ij.DialogUtils; 019import imagingbook.common.ij.IjUtils; 020import imagingbook.common.image.ImageMapper; 021import imagingbook.common.image.OutOfBoundsStrategy; 022import imagingbook.common.image.interpolation.InterpolationMethod; 023import imagingbook.core.jdoc.JavaDocHelp; 024import imagingbook.sampleimages.GeneralSampleImage; 025 026/** 027 * <p> 028 * ImageJ plugin, rotates the current image by a specified angle around its center. See Sec. 21.1.1 of [1] for details. 029 * Optionally opens a sample image if no image is currently open. 030 * </p> 031 * <p> 032 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 033 * (2022). 034 * </p> 035 * 036 * @author WB 037 * @version 2022/11/28 038 * @see ImageMapper 039 * @see Rotation2D 040 */ 041public class Map_Rotate_Center implements PlugInFilter, JavaDocHelp { 042 043 private static double alphaDeg = 15.0; // rotation angle (in degrees) 044 045 private static OutOfBoundsStrategy OBS = OutOfBoundsStrategy.ZeroValues; 046 private static InterpolationMethod IPM = InterpolationMethod.Bicubic; 047 048 /** 049 * Constructor, asks to open a predefined sample image if no other image 050 * is currently open. 051 */ 052 public Map_Rotate_Center() { 053 if (IjUtils.noCurrentImage()) { 054 DialogUtils.askForSampleImage(GeneralSampleImage.Clown); 055 } 056 } 057 058 @Override 059 public int setup(String arg, ImagePlus im) { 060 return DOES_ALL; 061 } 062 063 @Override 064 public void run(ImageProcessor ip) { 065 066 if (!runDialog()) { 067 return; 068 } 069 070 double alpha = Math.toRadians(alphaDeg); 071 double xc = 0.5 * ip.getWidth(); 072 double yc = 0.5 * ip.getHeight(); 073 074 AffineMapping2D T1 = new Translation2D(-xc, -yc); 075 AffineMapping2D R = new Rotation2D(alpha); 076 AffineMapping2D T2 = new Translation2D(xc, yc); 077 AffineMapping2D A = T1.concat(R).concat(T2); 078 079 AffineMapping2D iA = A.getInverse(); // inverse mapping (target to source) 080 new ImageMapper(iA, OBS, IPM).map(ip); 081 } 082 083 // -------------------------------------------- 084 085 private boolean runDialog() { 086 GenericDialog gd = new GenericDialog(this.getClass().getSimpleName()); 087 gd.addHelp(getJavaDocUrl()); 088 gd.addNumericField("Rotation angle (deg)", alphaDeg, 1); 089 gd.addEnumChoice("Image out-of-bounds strategy", OBS); 090 gd.addEnumChoice("Pixel interpolation method", IPM); 091 092 gd.showDialog(); 093 if (gd.wasCanceled()) 094 return false; 095 096 alphaDeg = gd.getNextNumber(); 097 OBS = gd.getNextEnumChoice(OutOfBoundsStrategy.class); 098 IPM = gd.getNextEnumChoice(InterpolationMethod.class); 099 100 return true; 101 } 102}