import ij.*; import ij.plugin.filter.PlugInFilter; import ij.process.*; import ij.gui.GenericDialog; import ij.util.StringSorter; import java.awt.*; import java.util.Vector; /** This sample ImageJ plugin filter blends an image with another one, * i.e., it adds another image with user-specified weight. */ /* A few things to note: 1) Filter plugins must implement the PlugInFilter interface. 2) User plugins do not use the package statement; 3) Plugins residing in the "plugins" folder, and with at least one underscore in their name, are automatically installed in the PlugIns menu. 4) Plugins can be installed in other menus by packaging them as JAR files. 5) The class name ("Blend_Images") and file name ("Blend_Images.java") must be the same. 6) An ImagePlus is, roughly speaking, an image or stack that has a name and usually its own window. An ImageProcessor (ip) carries image data for a single image (grayscale or color), e.g. the image displayed in the window of an ImagePlus, or a single slice of a stack. Depending on the image type, an ImageProcesor can be a ByteProcessor (8 bit), ShortProcessor (16 bit), FloatProcessor (32 bit) or ColorProcessor (RGB). 7) This filter works with selections, including non-rectangular selections. It is the programmer's responsibility to modify only the pixels within the ip.getRoi() rectangle; with the flag SUPPORTS_MASKING ImageJ takes care of out-of-roi pixels in that rectangle (for non-rectangular rois). 8) The run method of a PlugInFilter will be called repeatedly to process all the slices in a stack if requested by the user. 9) It supports Undo for single images. ImageJ takes care of this. 10) This filter uses the following methods of the ImageProcessor class: ip.toFloat(i, fp), ip.setPixels(i, fp) and ip.getNChannels(), simplifying code that does float operations on grayscale images of any type or on the channels of color images. See the run(ip) method. Requires ImageJ 1.38m or later. */ public class Blend_Images implements PlugInFilter { /* Parameters from the dialog. These are declared static, so they are * remembered until ImageJ quits. */ /** The name of the other image that will be added ("Image 2") */ static String image2name = ""; /** The weight of the image that the filter is applied to ("Image 1") */ static float weight1; /** The weight of the other image, that will be added ("Image 2") */ static float weight2 = 0.5f; /** Whether to set weight1 = 1. Otherwise weight1 = 1 - weight2 */ static boolean fixWeight1 = false; /* Further class variables */ /** The ImagePlus of the image (or ImageStack) to be filtered */ private ImagePlus imp; /** The name of this PlugInFIlter (declaring it once reduces copy&paste errors) */ private final static String plugInFilterName = "Blend Images"; /** * This method is called by ImageJ for initialization. * @param arg Unused here; for plugins in a .jar file arguments can be * specified in the plugins.config file of the .jar archive. * @param imp The ImagePlus containing the image (or stack) to be processed * @return The method returns flags (i.e., a bit mask) specifying the * capabilities and needs of the filter. See PlugInFilter.java * in the ImageJ sources for details. */ public int setup(String arg, ImagePlus imp) { if (IJ.versionLessThan("1.38m")) { // also generates an error message for older versions return DONE; } else { int flags = DOES_ALL|SUPPORTS_MASKING; // capabilities and needs of the filter. if (imp==null) { IJ.noImage(); // error message if no image is open return DONE; } this.imp = imp; // we need the ImagePlus as class variable to determine the image size&type if (!showDialog()) return DONE; // if dialog cancelled IJ.register(this.getClass()); // protect static class variables (parameters) from garbage collection return IJ.setupDialog(imp, flags); // for stacks ask "process all slices?" } } /** * This method is called by ImageJ once for a single image or repeatedly * for all images in a stack. * It creates a weighted sum (blend) of image ip and an image ip2 determined * previously in the dialog. * @param ip The image that should be processed */ public void run(ImageProcessor ip) { ImagePlus imp2 = WindowManager.getImage(image2name); ImageProcessor ip2 = null; // the image that we will blend ip with if (imp2 != null) ip2 = imp2.getProcessor(); if (ip2 == null) return; // should never happen, we have imp2 from a list of suitable images FloatProcessor fp1 = null, fp2 = null; // non-float images will be converted to these for (int i=0; i