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 ImageJ_Demos;
010
011import ij.IJ;
012import ij.ImagePlus;
013import ij.ImageStack;
014import ij.WindowManager;
015import ij.gui.GenericDialog;
016import ij.gui.NewImage;
017import ij.plugin.filter.PlugInFilter;
018import ij.process.Blitter;
019import ij.process.ByteBlitter;
020import ij.process.ByteProcessor;
021import ij.process.ImageProcessor;
022import imagingbook.core.jdoc.JavaDocHelp;
023
024/**
025 * This imageJ plugin demonstrates blending of two images and generating a stack of intermediate images. At least two
026 * images must be open. The active image is taken as the first image, the second image may be selected interactively.
027 *
028 * @author WB
029 */
030public class Alpha_Blending_Stack implements PlugInFilter, JavaDocHelp {
031        static int nFrames = 10;
032
033        ImagePlus fgIm = null; // foreground image (chosen interactively)
034        
035        public int setup(String arg, ImagePlus imp) {
036                return DOES_8G;
037        }
038
039        public void run(ImageProcessor bgIp) {
040                if (!runDialog() || fgIm == null) {
041                        return;
042                }
043                
044                int w = bgIp.getWidth();
045                int h = bgIp.getHeight();
046
047                // prepare foreground image
048                ImageProcessor fgIp = fgIm.getProcessor().convertToByte(false);
049                ImageProcessor fgTmpIp = bgIp.duplicate();
050
051                // create image stack
052                ImagePlus movie = NewImage.createByteImage("Movie", w, h, nFrames, 0); 
053                ImageStack stack = movie.getStack();
054
055                // loop over stack frames
056                for (int i = 0; i < nFrames; i++) {
057                        // transparency of foreground image
058                        double iAlpha = 1.0 - (double) i / (nFrames - 1);
059                        ImageProcessor iFrame = stack.getProcessor(i + 1); 
060                        // copy background image to frame i
061                        iFrame.insert(bgIp, 0, 0);
062                        iFrame.multiply(iAlpha);
063
064                        // copy foreground image and make transparent
065                        fgTmpIp.insert(fgIp, 0, 0);
066                        fgTmpIp.multiply(1 - iAlpha);
067
068                        // add foreground image frame \Code{i}
069                        ByteBlitter blitter = new ByteBlitter((ByteProcessor) iFrame);
070                        blitter.copyBits(fgTmpIp, 0, 0, Blitter.ADD);
071                }
072
073                // display movie (image stack)
074                movie.show();
075        }
076        
077        private boolean runDialog() {
078                // get list of open images
079                // TODO: simplify foreground image selection (see matching plugins)
080                int[] windowList = WindowManager.getIDList(); 
081                if (windowList == null) {
082                        IJ.noImage();
083                        return false;
084                }
085                String[] windowTitles = new String[windowList.length];
086                for (int i = 0; i < windowList.length; i++) {
087                        ImagePlus imp = WindowManager.getImage(windowList[i]); 
088                        if (imp != null)
089                                windowTitles[i] = imp.getShortTitle(); 
090                        else
091                                windowTitles[i] = "untitled";
092                }
093                
094                GenericDialog gd = new GenericDialog("Alpha Blending");
095                gd.addHelp(getJavaDocUrl());
096                gd.addChoice("Foreground image:", 
097                windowTitles, windowTitles[0]);
098                gd.addNumericField("Frames:", nFrames, 0); 
099                
100                gd.showDialog(); 
101                if (gd.wasCanceled()) 
102                        return false;
103                
104                int img2Index = gd.getNextChoiceIndex(); 
105                fgIm = WindowManager.getImage(windowList[img2Index]);
106                nFrames = (int) gd.getNextNumber(); 
107                if (nFrames < 2)
108                        nFrames = 2;
109                return true;
110        }
111
112}