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.IJ; 012import ij.ImagePlus; 013import ij.plugin.filter.PlugInFilter; 014import ij.process.ImageProcessor; 015import imagingbook.common.geometry.basic.Pnt2d; 016import imagingbook.common.geometry.mappings.linear.LinearMapping2D; 017import imagingbook.common.geometry.mappings.linear.ProjectiveMapping2D; 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.common.math.Matrix; 024import imagingbook.core.jdoc.JavaDocHelp; 025import imagingbook.sampleimages.GeneralSampleImage; 026 027/** 028 * <p> 029 * This plugin demonstrates the use of geometric mappings, as implemented in the imagingbook library. A 030 * {@link ProjectiveMapping2D} (transformation) is specified by 4 corresponding point pairs, given by point sequences P 031 * and Q. The inverse mapping is required for target-to-source mapping. See Sec. 21.1.4 of [1] for details. The actual 032 * pixel transformation is performed by an {@link ImageMapper} object. Try on a suitable test image and check if the 033 * image corners (P) are mapped to the points specified in Q. This plugin works for all image types. The transformed 034 * image is shown in a new window, the original image remains unchanged. Optionally opens a sample image if no image is 035 * currently open. 036 * </p> 037 * <p> 038 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 039 * (2022). 040 * </p> 041 * 042 * @author WB 043 * @version 2022/11/28 044 * @see LinearMapping2D 045 * @see ProjectiveMapping2D 046 * @see ImageMapper 047 */ 048public class Map_Projective implements PlugInFilter, JavaDocHelp { 049 050 private static Pnt2d[] P = { // source quad 051 Pnt2d.from(0, 0), 052 Pnt2d.from(400, 0), 053 Pnt2d.from(400, 400), 054 Pnt2d.from(0, 400)}; 055 056 private static Pnt2d[] Q = { // target quad 057 Pnt2d.from(0, 60), 058 Pnt2d.from(400, 20), 059 Pnt2d.from(300, 400), 060 Pnt2d.from(30, 200)}; 061 062 private ImagePlus im; 063 064 /** 065 * Constructor, asks to open a predefined sample image if no other image 066 * is currently open. 067 */ 068 public Map_Projective() { 069 if (IjUtils.noCurrentImage()) { 070 DialogUtils.askForSampleImage(GeneralSampleImage.Kepler); 071 } 072 } 073 074 @Override 075 public int setup(String arg, ImagePlus im) { 076 this.im = im; 077 return DOES_ALL + NO_CHANGES; 078 } 079 080 @Override 081 public void run(ImageProcessor source) { 082 int W = source.getWidth(); 083 int H = source.getHeight(); 084 085 // create the target image: 086 ImageProcessor target = source.createProcessor(W, H); 087 088 // create the target-to source mapping, i.e. Q -> P. 089 // there are 2 alternatives: 090 LinearMapping2D m = ProjectiveMapping2D.fromPoints(P, Q); // P -> Q, then invert 091 LinearMapping2D mi = m.getInverse(); 092 //Mapping2D mi = ProjectiveMapping2D.fromPoints(Q, P); // Q -> P = inverse mapping 093 094 // create a mapper instance: 095 ImageMapper mapper = 096 new ImageMapper(mi, OutOfBoundsStrategy.ZeroValues, InterpolationMethod.Bicubic); 097 098 // apply the mapper: 099 mapper.map(source, target); 100 101 // display the target image: 102 new ImagePlus(im.getShortTitle() + "-transformed", target).show(); 103 104 IJ.log("A = \n" + Matrix.toString(m.getTransformationMatrix())); 105 } 106}