All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.mianalysis.mia.thirdparty.Stack_Focuser_ Maven / Gradle / Ivy

Go to download

ModularImageAnalysis (MIA) is an ImageJ plugin which provides a modular framework for assembling image and object analysis workflows. Detected objects can be transformed, filtered, measured and related. Analysis workflows are batch-enabled by default, allowing easy processing of high-content datasets.

There is a newer version: 1.6.12
Show newest version
package io.github.mianalysis.mia.thirdparty;

import com.drew.lang.annotations.Nullable;import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.plugin.filter.RankFilters;
import ij.process.*;

import java.awt.*;


/*
 * A modified copy of Stack_Focuser_.java plugin, created by Mikahil Umorin.
 * Downloaded from https://imagej.nih.gov/ij/plugins/download/Stack_Focuser_.java on 06-June-2018.
 *
 * @author Mikhail Umorin 
 *
 * 09.10.02 (i.e. October 9th, 2002) modified 10.07.03 -- added generation of
 * height map; 4.08.03 -- changed interface for k_size and yes/no height map;
 * 7.08.03 -- Plugin can be initialized with arg string to setup method and then
 * runs non-interactively; sometime in 2004 -- added color support;
 *
 * Inspired by the mentioning of the capability of "flattening" a set of images
 * of different focal planes in ImagePro 4.0. The author, however came up with
 * the algorithm idea (and implementation) totally on his own without any
 * reference to any similar algorithms, printed, digital, or otherwise. The
 * author is open to any suggestions regarding algorithm(s), implementation,
 * and/or features of the program. The program may be distributed and modified
 * freely under GPL with the reference of the original source. No implicit or
 * explicit warranty or suitabiluty for a particular purpose is given. See license.txt
 * for detailed conditions on use, modification, and distribution of the source and binary code
 * of the plugin.
 *
 * Contains a very simple algorythm of patching a *focused * image from a stack
 * of images corresponding to different focal planes. It is very important that
 * images in the stack are of the same brightness; otherwise pasting together
 * pieces of different images will create artificial edges.
 *
 * The principle: 1) find edges for each image in a stack by running a Sobel
 * filter (after noise-reducing 3x3 median filter); 2) create a "map" of how far
 * the influence of edges extends; i.e. how far from a focused edge we still
 * think that the image is focused by taking the maximum value in the
 * neighborhood of the specified size; 3) for every pixel (x, y) based on the
 * choice of the maximum "edge" value among different slices in the "map" stack
 * copy the pixel value from the appropriate original image to the new image.
 *
 * Program works on 8-bit and 16-bit grayscale stacks, and accepts rectangular
 * ROIs; if no ROI is given, plugin works on the whole image; For RGB stack the
 * plugin decomposes the stack into three 8-bit R, G, and B component stacks and
 * applies the above described algorithm to each one. The resulting *pasted" RGB
 * image is created from the 8-bit pasted images from each component stack. The
 * height map is an average of individual height maps for RGB case.
 *
 * If the option "R, G, and B come from same objects/structures" is set, the plugin
 * determines focussed areas for green colour component only and pastes R and B components
 * from the corresponding green-focussed areas.
 *
 * plugin converts stacks to 32-bit float to preserve precision before any
 * manipulation is performed. The size in pixels (odd integer > 1) of the
 * Maximum square filter is requested; trial and error would be the fastest way
 * to optimize the result. The final image is written to "Focused"+ window, but not saved on the disk (the user can do that him/her
 * self). Optionally, the plugin generates a "height map", i.e. an image of the
 * heights of focused parts of the image. The home-grown maximum filter
 * maxFilter has a square kernel and is MUCH faster then available in Process ->
 * Filters -> Maximum menu. The sacrifice in quality is believed negligible for
 * this kind of application even though the squareness makes it anisotropic (?)
 *
 * For a short but good reference on image analysis see
 * http://www.cee.hw.ac.uk/hipr/html/hipr_top.html
 */
public class Stack_Focuser_ implements PlugInFilter
{
    /*
     * ImageStacl object of the original image
     */
    private ImageStack i_stack;


    public static final int BYTE=0;
    public static final int SHORT=1;
    public static final int FLOAT=2;
    public static final int RGB=3;

    /*
     * Focusing kernel size, implies square kernel
     */
    protected int k_size;

    /*
     * Index of the image type, {@link #BYTE}, {@link #SHORT}, {@link #FLOAT}, {@link #RGB}
     */
    protected int type;

    /*
     * Width of the original image
     */
    protected int o_width;

    /*
     * Height of the original image
     */
    protected int o_height;


    /*
     * Num of slices in the original stack
     */
    protected int n_slices;

    /*
     * Total number of pixels in the original image,  = {@link #o_height} x {@link #o_width}
     */
    protected int o_dim;

    /*
     * Rectangle object for image's ROI where to perform focusing. If ROI is not set,
     * focusing is performed on the whole image.
     */
    private Rectangle r;

    /*
     * Width of ROI
     */
    protected int n_width;

    /*
     * Height of ROI
     */
    protected int n_height;

    /*
     * Total number of pixels in ROI, = {@link #n_height } x {@link #n_width}
     */
    protected int n_dim;

    /*
     * Filename of the original image
     */
    private String o_title;


    private boolean create_map = false;
    private boolean onefocus = false;
    private boolean smooth = false;
    private GenericDialog input_dialog;
    private boolean interact = true;
    private ImageProcessor focused_ip = null, height_ip = null, existing_map = null;
    private ImageStack focused_stack, height_stack;
    private static final int redMask = 0xff0000, greenMask = 0x00ff00, blueMask = 0x0000ff;
    private static final int redShift = 16, greenShift = 8, blueShift = 0;

    public int setup(String arg, ImagePlus imp) {
        k_size = 11;
        if (arg.equalsIgnoreCase("about")) {
            showAbout();
            return DONE;
        }
        // check if the arg string has parameters to set
        if (arg.indexOf("ksize=") >= 0) {
            interact = false;
            int pos = arg.indexOf("ksize=") + 6;
            if (pos != arg.length()) {
                if (arg.charAt(pos) != ' ') {
                    String kss;
                    int posn = arg.indexOf(' ', pos + 1);
                    if (posn > 0) {
                        kss = arg.substring(pos, posn);
                    } else {
                        kss = arg.substring(pos);
                    }
                    k_size = Integer.parseInt(kss);
                }
            }
        }
        if (arg.indexOf("hmap=") >= 0) {
            interact = false;
            int pos = arg.indexOf("hmap=") + 5;
            if (pos != arg.length()) {
                if (arg.charAt(pos) != ' ') {
                    String hms;
                    int posn = arg.indexOf(' ', pos + 1);
                    if (posn > 0) {
                        hms = arg.substring(pos, posn);
                    } else {
                        hms = arg.substring(pos);
                    }
                    create_map = hms.equalsIgnoreCase("true");
                }
            }
        }
        if (arg.indexOf("rgbone=") >= 0) {
            interact = false;
            int pos = arg.indexOf("rgbone=") + 7;
            if (pos != arg.length()) {
                if (arg.charAt(pos) != ' ') {
                    String hms;
                    int posn = arg.indexOf(' ', pos + 1);
                    if (posn > 0) {
                        hms = arg.substring(pos, posn);
                    } else {
                        hms = arg.substring(pos);
                    }
                    onefocus = hms.equalsIgnoreCase("true");
                }
            }
        }
        if (arg.indexOf("smooth=") >= 0) {
            interact = false;
            int pos = arg.indexOf("smooth=") + 7;
            if (pos != arg.length()) {
                if (arg.charAt(pos) != ' ') {
                    String hms;
                    int posn = arg.indexOf(' ', pos + 1);
                    if (posn > 0) {
                        hms = arg.substring(pos, posn);
                    } else {
                        hms = arg.substring(pos);
                    }
                    smooth = hms.equalsIgnoreCase("true");
                }
            }
        }
        //
        if (imp == null) {
            IJ.noImage();
            return DONE;
        }
        //
        ImageProcessor ip_p = imp.getProcessor();
        o_title = imp.getTitle();
        int dot_i = o_title.indexOf(".");
        if (dot_i > 0)
            o_title = o_title.substring(0, dot_i);
        // determine the type of the image; getType() does not work for stacks
        if (ip_p instanceof ByteProcessor)
            type = BYTE;
        else if (ip_p instanceof ShortProcessor)
            type = SHORT;
        else if (ip_p instanceof FloatProcessor)
            type = FLOAT;
        else
            type = RGB;
        i_stack = imp.getStack();
        o_width = imp.getWidth();
        o_height = imp.getHeight();
        o_dim = o_width * o_height;
        n_slices = imp.getStackSize();
        // obtain ROI and if ROI not set set ROI to the whole image
        r = i_stack.getRoi();
        if ((r == null) || (r.width < 2) || (r.height < 2)) {
            r = new Rectangle(0, 0, o_width, o_height);
            i_stack.setRoi(r);
        }
        n_width = r.width;
        n_height = r.height;
        n_dim = n_width * n_height;
        return DOES_8G + DOES_16 + DOES_RGB + STACK_REQUIRED + NO_CHANGES
                + NO_UNDO;
    }


    public void run(ImageProcessor ip)
    {
        // read options
        // TODO allow for different x and y kern_size later
        if (interact) {
            input_dialog = new GenericDialog("Options");
            input_dialog.addNumericField("Enter the n (>2) for n x n kernel:", k_size, 0);
            input_dialog.addCheckbox("Generate height map", create_map);
            input_dialog.addCheckbox("R, G, and B come from same objects/structures", onefocus);
            input_dialog.addCheckbox("Smooth height map",smooth);
            input_dialog.showDialog();
            if (input_dialog.wasCanceled()) return;
            k_size = (int)input_dialog.getNextNumber();
            create_map = input_dialog.getNextBoolean();
            onefocus = input_dialog.getNextBoolean();
            smooth = input_dialog.getNextBoolean();
            if ( input_dialog.invalidNumber() || k_size<3 ) {
                IJ.error("Invalid number or " +k_size+" is incorrect! ");
                return;
            }
        }
        switch(type)
        {
            case BYTE: focused_ip = focusGreyStack(i_stack, BYTE); break;
            case SHORT: focused_ip = focusGreyStack(i_stack, SHORT); break;
            case FLOAT: focused_ip = focusGreyStack(i_stack, FLOAT); break;
            case RGB:
                if (onefocus) {
                    focusRGBStackOne(i_stack);
                } else {
                    focusRGBStack(i_stack);
                }
                break;
            default: break;
        }
        // construct the title of the new window
        ImagePlus dispFocusedIpl = null;
        ImagePlus dispHeightIpl = null;
        String n_title = "Focused_"+o_title;
        dispFocusedIpl = new ImagePlus(n_title, focused_ip);
        dispFocusedIpl.setPosition(1,1,1);
        dispFocusedIpl.updateChannelAndDraw();
        dispFocusedIpl.show();

        if (create_map) {
            String nm_title = "HeightMap_" + o_title;
            dispHeightIpl = new ImagePlus(nm_title, height_ip);
            dispHeightIpl.setPosition(1,1,1);
            dispHeightIpl.updateChannelAndDraw();
            dispHeightIpl.show();

        }
    }

    /*
     * Focuses an RGB stack. All colors are focused independently.
     */
    public void focusRGBStack (ImageStack rgb_stack) {
//        IJ.showStatus("Processing RGB stack");
        focused_stack = new ImageStack(n_width, n_height);
        height_stack = new ImageStack(n_width, n_height);
        // split RGB stack into R, G, and B components
        // and then generateModuleList FocusGreyStack() on each independently


        // Red
        ImageStack colored_stack = extractColor(rgb_stack, redMask, redShift);
//        IJ.showStatus("Extracted red color");
        focused_ip = focusGreyStack(colored_stack, BYTE);
//        IJ.showStatus("Focused red color stack");
        focused_stack.addSlice("Red", focused_ip);
        height_stack.addSlice("Red", height_ip);
        colored_stack = null;
        // Green
        colored_stack = extractColor(rgb_stack, greenMask, greenShift);
//        IJ.showStatus("Extracted green color");
        focused_ip = focusGreyStack(colored_stack, BYTE);
//        IJ.showStatus("Focused green color stack");
        focused_stack.addSlice("Green", focused_ip);
        height_stack.addSlice("Green", height_ip);
        colored_stack = null;
        // Blue
        colored_stack = extractColor(rgb_stack, blueMask, blueShift);
//        IJ.showStatus("Extracted blue color");
        focused_ip = focusGreyStack(colored_stack, BYTE);
//        IJ.showStatus("Focused blue color stack");
        focused_stack.addSlice("Blue", focused_ip);
        height_stack.addSlice("Blue", height_ip);
        colored_stack = null;
        //
        ImagePlus fs_image = new ImagePlus("Focused stack", focused_stack);
        fs_image.show();
        fs_image.updateAndDraw();
        // ImageWindow win = fs_image.getWindow();
        IJ.run("RGB Color");
        focused_ip = new ColorProcessor(n_width, n_height);
        focused_ip.copyBits(WindowManager.getCurrentWindow().getImagePlus().getProcessor(),
                0, 0, Blitter.COPY);
        IJ.run("Close");
        // ImagePlus hs_image = new ImagePlus("Focus height stack", height_stack);
        // hs_image.show();
        // hs_image.updateAndDraw();
        long[] sum = new long[n_dim];
        // int hs_size = height_stack.getSize();
        for (int i=1; i<=height_stack.getSize(); i++)
        {
            byte[] pixels = (byte[]) height_stack.getPixels(i);
            // addRef the value of each pixel an the corresponding position of the sum array
            for (int j=0; jmax_e)
                    {
                        max_e = curr_pixels[i];
                        max_slice = z;
                    }
                }

                height_ip.putPixel(x, y, max_slice);

            }
        }

        return height_ip;

    }

    /*
     * Apply median filter to the provided height map image
     */
    private void smoothHeightMap(ImageProcessor height_ip, int r) {
        new RankFilters().rank(height_ip,r,RankFilters.MEDIAN);
    }

    /*
     * By comparing max values for the same point in different slices we decide which
     * original slice to use to paste into the new image at that location.
     */
    private ImageProcessor pasteGreyImage(ImageStack g_stack, ImageStack m_stack, int stackType) {
        ImageProcessor f_ip = null;
        byte[] orig_pixels8 = null;
        short[] orig_pixels16 = null;
        float[] orig_pixels32 = null;
        byte[] dest_pixels8 = null;
        short[] dest_pixels16 = null;
        float[] dest_pixels32 = null;

        // If an existing map hasn't been provided, generateModuleList the following
        if (existing_map == null) {
            height_ip = createHeightMap(m_stack);

            // If enabled, applying the median filter to smooth the height profile
            if (smooth) smoothHeightMap(height_ip,k_size);
        } else {
            height_ip = existing_map;
        }

        switch (stackType)
        {
            case BYTE:
                dest_pixels8 = new byte[n_dim];
                break;
            case SHORT:
                dest_pixels16 = new short[n_dim];
                break;
            case FLOAT:
                dest_pixels32 = new float[n_dim];
                break;
            default:
                break;
        }

        int offset, i;
        int copy_i, copy_x, copy_y;

        for (int y=0; y1; odd and even do not matter, i.e. size=2 is same as size=3
     */
    private float findMaxInNeigh(ImageProcessor ip_, int center_x, int center_y, int size_x, int size_y)
    {
        float maxVal = 0.0f;
        int width_ = ip_.getWidth();
        int height_ = ip_.getHeight();
        float[] pixels_ = (float[]) ip_.getPixels();
        int half_x= size_x / 2;
        int half_y= size_y / 2;
        int start_x = center_x-half_x;
        int start_y = center_y-half_y;
        if (start_x<0) {start_x = 0;}
        if (start_y<0) {start_y = 0;}
        int end_x = center_x+half_x;
        int end_y = center_y+half_y;
        if (end_x>width_) {end_x = width_;}
        if (end_y>height_) {end_y = height_;}
        int offset_, i_;
        for (int y=start_y; ymaxVal) {maxVal = pixels_[i_];}
            }
        }
        return maxVal;
    }

    private ImageStack extractColor(ImageStack rgbStack, int mask, int shift)
    {
        ImageProcessor sliceProcessor;
        ImageStack g_stack = new ImageStack(n_width, n_height);
        int offset, pos;
        int w = rgbStack.getWidth();
        int h = rgbStack.getHeight();
        // gretStack = new ImageStack(w, h);
        // match input stack and the new one slice by slice
        for (int i=1; i<=rgbStack.getSize(); i++)
        {
            sliceProcessor = rgbStack.getProcessor(i);
            int[] colorPixels = (int[]) sliceProcessor.getPixels();
            byte[] greyPixels = new byte[w*h];
            for (int y=0; y>shift);
                }
            }
            g_stack.addSlice("", greyPixels);
        }
        return g_stack;
    }

    public void showAbout()
    {
        IJ.showMessage("About Stack Focuser...",
                "Patches a *focused* image\n"+
                        " from a stack of images \n"+
                        "corresponding to different focal planes\n"+
                        "\n Mikhail Umorin ");
    }


    // NEW METHODS


    public ImageProcessor getHeightImage() {
        return height_ip;

    }

    public void setExistingHeightMap(@Nullable ImageProcessor existing_map) {
        this.existing_map = existing_map;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy