Eye_Candy.java |
package vnt; /** * Eye_Candy.java - v1.0 * Began: July 27, 2005 * Last Updated: July 28, 2005 * * Copyright (C) 2005 - Michael D. Miller - mdm162@truman.edu * Truman State University * 100 E. Normal * Kirksville, MO - 63501 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ import ij.*; import ij.gui.*; import java.awt.*; import java.awt.Color.*; import ij.plugin.filter.PlugInFilter; import ij.process.*; import java.io.*; import ij.io.*; import java.lang.String.*; import java.lang.Math.*; import ij.plugin.filter.*; /** * <p>A simple class that opens the original color image and overlays * graphics generated by the Vascular Network Toolkit.</p> * * @author Michael Miller - Truman State University * @version 1.0 * @since 1.0 */ public class Eye_Candy extends VascularNetworkToolkit implements PlugInFilter { /** * Specifies the preconditions for the plug-in. * If this method succeeds then run() is called. * * <p>Pre: ImageJ is running and an 8-bit grayscale image is open. The plug-in was just activated. * <br />Post: Either an argument was processed, the image was not saved to a local folder, or the plug-in is cleared to run on the image. * @param arg Required by the interface. The argument list passed to the plug-in. * @param imp Required by the interface. The ImagePlus datastructure holding (among other things) information to grab path and filename. * @return If DONE is returned, ImageJ quits without run()'ing the plug-in. Otherwise, the plug-in signals to ImageJ that this plugin only handles 8-bit (256 grayscale) and will not change the original image. * @see #run */ public int setup(String arg, ImagePlus ip) { if (arg.equals("about")) { showAbout("Eye Candy"," * Generates a variety of visualizations overlayed on the original image. Used for image processing qualitative comparison and evaluation. (Copyright 2005. Michael Miller mdm162@truman.edu)"); return DONE; // exit without run() } boolean processImage = getFileInformation(ip); if (!processImage) { IJ.error("Must have a file path. Please save this image to a local folder."); return DONE; // exit without run() } return DOES_ALL+NO_CHANGES; // success, run() } /** * Receives a binary image (specifically a thinned, pruned distance map * skeletonization) and computes information regarding the topological * properties of the image. * 1) Parses the image for possible node locations. * 2) Accesses the distance map to calculate node radii information. * 3) Simplifies the structures by testing for node overlap. * 4) Performs graph theoretic analysis based on the information generated. * * <p>Pre: The image was cleared to run by the setup() method. * <br />Post: Quantitative data is finally extracted from the image. * @param bp Required by the interface. The access information to the original image. * @see #setup * @see #RED * @see #YELLOW * @see #GREEN * @see #drawOutlineOverlay * @see #drawNodeOverlay * @see #drawSkeletonOverlay * @see #drawPrunedSkeletonOverlay * @see #drawBlendedSegmentationOverlay */ public void run(ImageProcessor bp){ // get the width and height information getDimensions(bp); // load the original image if (!loadOriginalImage()) { // could not load original, quit return; } int totalStacks = 9; if (generateEyeCandyWithOriginalImage) totalStacks ++; if (generateLightingCorrectionSurfaceEyeCandy) totalStacks ++; // create a stack image for multiple overlays String title = "Eye Candy"; ImagePlus imp = NewImage.createRGBImage (title, width, height, totalStacks, NewImage.FILL_WHITE); ImageProcessor nip; int stackIndex = 1; // draw original image if (generateEyeCandyWithOriginalImage) { imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawOriginalImage(nip); } // draw light correction surface if (generateLightingCorrectionSurfaceEyeCandy) { imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawOriginalImage(nip); } // display blended segmented overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawBlendedSegmentationOverlay(nip, RED); // display segmented overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawSegmentedOverlay(nip, RED); // display skeleton overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawSkeletonOverlay(nip, GREEN); // display pruned skeleton overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawPrunedSkeletonOverlay(nip, GREEN); // display pruned skeleton's outline overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawOutlineOverlay(nip, RED); // display pruned's outline and skeleton imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawOutlineSkeletonOverlay(nip, RED, GREEN); // display node-skeleton overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawSkeletonNodeOverlay(nip, GREEN, YELLOW); // display node graph overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawGraphOverlay(nip, YELLOW); // display merged node graph overlay imp.setSlice(stackIndex++); nip = imp.getProcessor(); drawMergedGraphOverlay(nip, YELLOW); // all done, set to first slice and display imp.setSlice(1); imp.show(); IJ.selectWindow(title); // save the visualizations saveFile("eyecandy"); return; } /** * Opens the original image and loads it into memory for access * by all the display methods in this class. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. Ie: File information exists. * <br />Post: The original image will be loaded into local memory. * @return Returns true on successful load. False otherwise. */ private boolean loadOriginalImage() { int x=0, y=0; // load the original image IJ.open(directory+originalName); ImagePlus originalImage = IJ.getImage(); ImageProcessor originalIP = originalImage.getProcessor(); originalImage.show(); IJ.selectWindow(originalName); IJ.run("RGB Color"); // required if final is also RGB (makes things blue if it's only 8-bit) pixel = VascularNetworkToolkit.LoadImage(originalIP); // close the original after loading it's pixels into memory IJ.run("Close"); // something bad happened, quit with error if (pixel == null) { return false; } return true; } /** * Draws the original image into the given ImageProcessor. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new stack should be created of the original color image. * @param nip The ImageProcessor that will be drawn on. * @return Returns true on successful draw. False otherwise. */ public boolean drawOriginalImage(ImageProcessor nip) { int x=0, y=0; // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } return true; } /** * Draws the lighting correction surface into the given ImageProcessor. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new stack should be created of the original color image. * @param nip The ImageProcessor that will be drawn on. * @return Returns true on successful draw. False otherwise. */ public boolean drawLightingCorrectionSurface(ImageProcessor nip) { int x=0, y=0; // load the outline IJ.open(directory+"surface_"+originalName); ImagePlus surfaceImage = IJ.getImage(); ImageProcessor surfaceIP = surfaceImage.getProcessor(); // draw the surface in the appropriate color for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,surfaceIP.getPixel(x,y)); } } IJ.run("Close"); return true; } /** * Opens the EDM outline, and generates a new image with the * EDM generated outline overlayed in a particular color over the * original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawOutlineOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the outline IJ.open(directory+"outline_"+originalName); ImagePlus outlineImage = IJ.getImage(); ImageProcessor outlineIP = outlineImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the outline in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (outlineIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the node and skeleton overlays, and generates a new image with the * nodes and skeleton overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param skeletonColor The 32-bit color to draw the skeleton overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @param nodeColor The 32-bit color to draw the node overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawSkeletonNodeOverlay(ImageProcessor nip, int skeletonColor, int nodeColor) { int x=0, y=0; // load the node overlay IJ.open(directory+"nodes_"+originalName); ImagePlus nodesImage = IJ.getImage(); ImageProcessor nodesIP = nodesImage.getProcessor(); // load the pruned skeleton IJ.open(directory+"pruned_"+originalName); ImagePlus prunedImage = IJ.getImage(); ImageProcessor prunedIP = prunedImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the nodes in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (nodesIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,nodeColor); } } } // draw the pruned skeleton in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (prunedIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,skeletonColor); } } } IJ.run("Close"); IJ.run("Close"); return true; } /** * Opens the node overlay, and generates a new image with the * nodes overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawNodeOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the node overlay IJ.open(directory+"nodes_"+originalName); ImagePlus nodesImage = IJ.getImage(); ImageProcessor nodesIP = nodesImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the nodes in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (nodesIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the simplified graph representation, and generates a * new image with the graph overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawGraphOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the graph overlay IJ.open(directory+"graph_"+originalName); ImagePlus graphImage = IJ.getImage(); ImageProcessor graphIP = graphImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the graph in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (graphIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the merged graph representation, and generates a * new image with the graph overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawMergedGraphOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the graph overlay IJ.open(directory+"merged_"+originalName); ImagePlus graphImage = IJ.getImage(); ImageProcessor graphIP = graphImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the graph in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (graphIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the EDM skeleton, and generates a new image with the * skeleton overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawSkeletonOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the skeleton IJ.open(directory+"skeleton_"+originalName); ImagePlus skeletonImage = IJ.getImage(); ImageProcessor skeletonIP = skeletonImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the skeleton in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (skeletonIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the segmentation, and generates a new image with the * segmented drawn solid overlayed in a particular color over the * original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawSegmentedOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the segmentation IJ.open(directory+"segmented_"+originalName); ImagePlus segmentedImage = IJ.getImage(); ImageProcessor segmentedIP = segmentedImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the segmented overlay in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (segmentedIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the EDM pruned skeleton, and generates a new * image with the skeleton overlayed in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawPrunedSkeletonOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the pruned skeleton IJ.open(directory+"pruned_"+originalName); ImagePlus prunedImage = IJ.getImage(); ImageProcessor prunedIP = prunedImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the pruned skeleton in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (prunedIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,color); } } } IJ.run("Close"); return true; } /** * Opens the original image and it's segmented image, and generates a new * image with the segmentation blended in a particular color over the original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the blue channel. * @return Returns true on successful draw. False otherwise. * @see #red */ public boolean drawBlendedSegmentationOverlay(ImageProcessor nip, int color) { int x=0, y=0; // load the blended segmented IJ.open(directory+"segmented_"+originalName); ImagePlus segmentedImage = IJ.getImage(); ImageProcessor segmentedIP = segmentedImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the blended segment in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (segmentedIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,VascularNetworkToolkit.averageARGBcolor(color,getColor(x,y))); } } } IJ.run("Close"); return true; } /** * Opens the EDM outline, and generates a new image with the * EDM generated outline overlayed in a particular color over the * original. * * <p>Pre: This class was setup appropriately with an image from the processed set of images. * <br />Post: A new image should be created the original color image with a graphical overlay. * @param color The 32-bit color to draw the overlay. 0xAABBCCDD where AA is the alpha, BB is the red channel, CC is the green channel, and DD is the green channel. * @return Returns true on successful draw. False otherwise. */ public boolean drawOutlineSkeletonOverlay(ImageProcessor nip, int colorOutline, int colorSkeleton) { int x=0, y=0; // load the outline IJ.open(directory+"outline_"+originalName); ImagePlus outlineImage = IJ.getImage(); ImageProcessor outlineIP = outlineImage.getProcessor(); // load the pruned skeleton IJ.open(directory+"pruned_"+originalName); ImagePlus prunedImage = IJ.getImage(); ImageProcessor prunedIP = prunedImage.getProcessor(); // draw the original image for(y=0; y<height; y++) { for(x=0; x<width; x++) { nip.putPixel(x,y,getColor(x,y)); } } // draw the outline in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (outlineIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,colorOutline); } } } // draw the pruned skeleton in the given color for(y=0; y<height; y++) { for(x=0; x<width; x++) { if (prunedIP.getPixel(x,y) != WHITE) { nip.putPixel(x,y,colorSkeleton); } } } IJ.run("Close"); IJ.run("Close"); return true; } }