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-2025 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package More_;
010
011import ij.ImagePlus;
012import ij.gui.GenericDialog;
013import ij.gui.Overlay;
014import ij.gui.Roi;
015import ij.plugin.filter.PlugInFilter;
016import ij.process.ImageProcessor;
017import imagingbook.common.ij.DialogUtils;
018import imagingbook.common.image.OutOfBoundsStrategy;
019import imagingbook.common.image.access.ImageAccessor;
020import imagingbook.core.jdoc.JavaDocHelp;
021import imagingbook.core.resource.ImageResource;
022import imagingbook.sampleimages.GeneralSampleImage;
023
024import java.awt.Color;
025
026import static imagingbook.common.ij.IjUtils.noCurrentImage;
027
028/**
029 * This ImageJ plugin demonstrates the effect of different out-of-bounds strategies
030 * for handling pixels that are outside the image. The input image is placed centered inside
031 * a larger canvas and the missing pixels are inserted. The user can select one of the
032 * predefined out-of-bounds strategies and the size factor (min. 1), which specifies how
033 * much larger the output image is compared to the original image.
034 * In the resulting composite image, the original image is outlined by a colored rectangle.
035 * The use of {@link ImageAccessor} makes this plugin work with any type of input image.
036 * If no image is currently open, the user is asked to load a predefined sample image.
037 *
038 * @author WB
039 * @version 2023/04/02
040 * @see OutOfBoundsStrategy
041 * @see ImageAccessor
042 */
043public class OutOfBoundsStrategy_Demo implements PlugInFilter, JavaDocHelp {
044
045    private static ImageResource SampleImage = GeneralSampleImage.MortarSmall;
046    private static OutOfBoundsStrategy OBS = OutOfBoundsStrategy.NearestBorder;
047    private static double SizeFactor = 5.0;
048    private static Color OutlineColor = Color.green;
049
050    private ImagePlus im = null;
051
052    /**
053     * Constructor, asks to open a predefined sample image if no other image is currently open.
054     */
055    public OutOfBoundsStrategy_Demo() {
056        if (noCurrentImage()) {
057            DialogUtils.askForSampleImage(SampleImage);
058        }
059    }
060
061    @Override
062    public int setup(String arg, ImagePlus im) {
063        this.im = im;
064        return DOES_ALL + NO_CHANGES;
065    }
066
067    @Override
068    public void run(ImageProcessor ip1) {
069        if (!runDialog()) {
070            return;
071        }
072
073        int w1 = ip1.getWidth();
074        int h1 = ip1.getHeight();
075
076        int dw = (int) (0.5 * (SizeFactor - 1) * w1);
077        int dh = (int) (0.5 * (SizeFactor - 1) * h1);
078
079        dw = Math.max(dw, 0);
080        dh = Math.max(dh, 0);
081
082        int w2 = dw + w1 + dw;
083        int h2 = dh + h1 + dh;
084
085        ImageProcessor ip2 = ip1.createProcessor(w2, h2);
086
087        ImageAccessor ia1 = ImageAccessor.create(ip1, OBS, null);
088        ImageAccessor ia2 = ImageAccessor.create(ip2, null, null);
089
090        for (int u = 0; u < w2; u++) {
091            for (int v = 0; v < h2; v++) {
092                float[] val = ia1.getPix(u - dw, v - dh);
093                ia2.setPix(u, v, val);
094            }
095        }
096
097        // mark the embedded original in the composite image
098        Overlay oly = new Overlay();
099        Roi rect = new Roi(dw, dh, w1, h1);
100        rect.setStrokeWidth(1.0f);
101        rect.setStrokeColor(OutlineColor);
102        oly.add(rect);
103
104        // show the composite image
105        ImagePlus im2 = new ImagePlus("", ip2);
106        im2.setOverlay(oly);
107        ImagePlus im2f = im2.flatten();
108        im2f.setTitle(im.getShortTitle() + " (" + OBS + ")");
109        im2f.show();
110    }
111
112    private boolean runDialog() {
113        GenericDialog gd = new GenericDialog(this.getClass().getSimpleName());
114        gd.addHelp(getJavaDocUrl());
115
116        gd.addEnumChoice("Out-of-bounds strategy", OBS);
117        gd.addNumericField("Size factor (>1)", SizeFactor, 1);
118
119        gd.showDialog();
120        if(gd.wasCanceled())
121            return false;
122
123        OBS = gd.getNextEnumChoice(OutOfBoundsStrategy.class);
124        SizeFactor = gd.getNextNumber();
125        SizeFactor = Math.max(1, SizeFactor);
126
127        return true;
128    }
129}