[Fwd: diploma thesis; problems with developing plugins for imagej using eclipse]

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[Fwd: diploma thesis; problems with developing plugins for imagej using eclipse]

Marko Ulbrich
Dear imagej-user,

i'm trying to write a plugin (subject: pattern recognition) for my
diploma thesis and now have the following problem:

I downloaded several plugins for testing the matching of targets with
the software imagej, e.g. "otsu_thresholding", "template_matching" and
"watershed_algorithm".

I'm using *Eclipse (version 3.0.1)* as development environment and
tested several java-sdk's, because with some of them i could not compile
the whole imagej-project. After some attempts with java-sdk 1.4_06, i'm
now using *java-jdk 1.5_05*. This SDK brings the best results when
compiling imagej with my plugins.

I copied the *imagej-source* code *version 1.34s* to my eclipse project.
I tried to place my plugins in the following folders:
...\imagej\plugins\pluginname.java
...\imagej\plugins\subfolder\pluginname.java

...but these attempts didn't work.

So I changed the code in the file
*\imagej\ij\io\PluginClassLoader.java at lines 236-240.*

I inserted these lines:

String test = new String("plugins.Diplom.");
test += className;
result = defineClass(test, classBytes, 0, classBytes.length);
//result = defineClass(className, classBytes, 0, classBytes.length);
// this was the original (line above)


While debugging the project, i saw, that in the variable "className",
there is saved a wrong name without the whole path to the className.
With my change I can compile my project and my plugins without mistakes
and they work too, when I use the compiled imagej. But when I try to use
standard-plugins, e.g. the example \Demos\Plasma, my program crashes.

So, with my version and changes only the plugins in the folder
\plugins\Diplom work well.

I attached the files "PluginClassLoader.java" and my plugins. These
plugins are located in the folder \plugins\Diplom, as sayed above.

plugin "Otsu Thresholding" uses the classes: OtsuThresholding_.java,
GrayLevelClass.java
plugin "Watershed Algorithm" uses the classes: Watershed_Algorithm.java,
WatershedFIFO.java, WatershedPixel.java, WatershedStructure.java
plugin "Create A Template" uses the remaining classes

How can I change my code, so that all plugins work?
I really tried to find an answer by searching in the internet and many
forums but didn't find the right answer yet.

Thank you very much for your help
Yours sincerely, Marko Ulbrich (student at the University of Applied
Sciences, Mainz)












package plugins.Diplom;

import java.util.*;
import ij.*;
import ij.process.*;

// This class implements a class of pixels with basic statistical functions.

public class GrayLevelClass {

        // ***** Deklarationen
*****************************************************
       
        public static float[] probabilityHistogram;
        public static boolean probabilityHistogramDone;
        public static int N;
        private int index;
        private float omega, mu;
       
        // ***** Hauptklasse
*******************************************************

        public GrayLevelClass(ByteProcessor img, boolean first) {
                if (!probabilityHistogramDone) {
                        int[] histogram = img.getHistogram();
                        probabilityHistogram = new float[256];

                        // Berechnen des prozentualen Anteils jedes Grauwerts:
                        for (int i = 0; i < 256; i++) {
                                probabilityHistogram[i] = ((float) histogram[i]) / ((float) N);
                        }
                        probabilityHistogramDone = true;
                }

                index = 1;
                omega = 1;
                mu = 0;
               
                for (int i = index; i < 256; i++) {
                        mu += probabilityHistogram[i - 1] * i;
                }
        }
       
        // ***** toString
**********************************************************
       
        public String toString() {
                StringBuffer ret = new StringBuffer();

                ret.append("Index : " + index + "\n");
                ret.append("Mu : "    + mu    + "\n");
                ret.append("Omega : " + omega + "\n");

                return ret.toString();
        }
       
        // ***** get-Methoden
******************************************************

        public float getMu() {
                return mu;
        }
        public float getOmega() {
                return omega;
        }
        public int getThreshold() {
                return index;
        }
}





package plugins.Diplom;

// Otsu Thresholding algorithm

import ij.*;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import ij.gui.*;
import ij.plugin.frame.PlugInFrame;

import java.awt.*;
import java.util.*;
import java.lang.*;
import java.text.*;

public class OtsuThresholding_ implements PlugInFilter {
       
        private int threshold;
        private String kontrast = "unbekannt";
       
        // ***** setup (String, ImagePlus)
*****************************************

        public int setup(String arg, ImagePlus imp) {
                if (arg.equals("about")) {
                        showAbout();
                        return DONE;
                }
                return DOES_8G + DOES_STACKS + SUPPORTS_MASKING + NO_CHANGES;
        }
       
        // ***** run (ImageProcessor)
**********************************************
       
        public void run(ImageProcessor ip) {
                boolean debug = false;

                int width = ip.getWidth();
                int height = ip.getHeight();
               
                GrayLevelClass.N = width * height;
                GrayLevelClass.probabilityHistogramDone = false;
                GrayLevelClass C1 = new GrayLevelClass((ByteProcessor) ip, true);

                float fullMu = C1.getOmega() * C1.getMu();
               
                // ***** Start *********************************************************
               
                double sigma = 0;
                threshold = 0;
               
                // Restwert vom Erwartungswert bis zum "vollen Grauwert":
                // stellt sicher, dass beim Festlegen der Schranke 255 nicht
überschritten wird
                float diffMuGraumax = 255 - C1.getMu();
               
                // Format erstellen für eine formatierte Ausgabe:
                DecimalFormat df = new DecimalFormat( "###,##0.00" );
               
                for (int i = 0; i <= 255; i++) {
                        // Summe der Entfernungen vom Erwartungswert gewichtet mit der Anzahl
der Grauwertvorkommen:
                        sigma = sigma + (double)(ip.getHistogram()[i]  * (Math.pow(i -
C1.getMu(), 2)));
                }
               
                // Berechnen der Standardabweichung:
                sigma = Math.sqrt((1.0 / (width * height)) * sigma);
               
                // Ändern der Schranke je nach Kontrast:
                if (sigma >= (0.2 * C1.getMu())){
                        threshold = (int)(C1.getMu() + sigma + 0.35 * diffMuGraumax);
                        kontrast = "sehr hoch";
                }
                else {
                        threshold = (int)(C1.getMu() + sigma + 0.15 * diffMuGraumax);
                        kontrast = "gering";
                }
               
                ImagePlus imp = NewImage.createByteImage("Threshold", width, height, 1,
                                NewImage.FILL_WHITE);
                ImageProcessor nip = imp.getProcessor();

                byte[] pixels = (byte[]) ((ByteProcessor) ip).getPixels();

                int offset = 0;

                for (int y = 0; y < height; y++) {
                        offset = y * width;
                        for (int x = 0; x < width; x++) {
                                if ((pixels[offset + x] & 0xff) <= threshold)
                                        nip.putPixel(x, y, 0);
                        }
                }
                imp.show();
               
                // Ausgabe des Ergebnisses in einer Textbox:
                IJ.showMessage("Schwellwert : " + threshold + "\n" +
                                           "Erwartungswert: " + df.format(C1.getMu()) + "\n" +
                                           "Standardabweichung: " + df.format(sigma) + "\n" +
                                           "Der Kontrast im Bild ist " + kontrast + ".");
        }

        void showAbout() {
                IJ.showMessage("OT calculates the OtsuThreshold. of a 8-bit image.\n");
        }
}





package ij.io;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import ij.IJ;

/** ImageJ uses this class loader to load plugins and resources from the
  * plugins directory and immediate subdirectories. This class loader will
  * also load classes and resources from JAR files.
  *
  * <p> The class loader searches for classes and resources in the
following order:
  * <ol>
  *  <li> Plugins directory</li>
  *  <li> Subdirectories of the Plugins directory</li>
  *  <li> JAR and ZIP files in the plugins directory and subdirectories</li>
  * </ol>
  * <p> The class loader does not recurse into subdirectories beyond the
first level.
*/
public class PluginClassLoader extends ClassLoader {
     protected String path;
     protected Hashtable cache = new Hashtable();
     protected Vector jarFiles;

     /**
      * Creates a new PluginClassLoader that searches in the directory path
      * passed as a parameter. The constructor automatically finds all
JAR and ZIP
      * files in the path and first level of subdirectories. The JAR and
ZIP files
      * are stored in a Vector for future searches.
      * @param path the path to the plugins directory.
      */
        public PluginClassLoader(String path) {
                this.path = path;
                jarFiles = new Vector();
                //find all JAR files on the path and subdirectories
                File f = new File(path);
                String[] list = f.list();
                if (list==null)
                        return;
                for (int i=0; i<list.length; i++) {
                        f=new File(path, list[i]);
                        if (f.isDirectory()) {
                                String[] innerlist = f.list();
                                if (innerlist==null) continue;
                                for (int j=0; j<innerlist.length; j++) {
                                        File g = new File(f,innerlist[j]);
                                        if (g.isFile()) addJAR(g);
                                }
                        } else
                                addJAR(f);
                }
        }

     private void addJAR(File f) {
         if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip"))
             jarFiles.addElement(f);
     }

     /**
      * Returns a resource from the path or JAR files as a URL
      * @param name a resource name.
      */
     public URL getResource(String name) {
         // try system loader first
         URL res = super.getSystemResource(name);
         if (res != null) return res;

         File resFile;

         //try plugins directory
         try {
             resFile = new File(path, name);
             if (resFile.exists()) {
               res = makeURL(resFile);
               return res;
             }
         }
         catch (Exception e) {}

         //try subfolders
         resFile = new File(path);
         String[] list = resFile.list();
         if (list!=null) {
             for (int i=0; i<list.length; i++) {
                 resFile = new File(path, list[i]);
                 if (resFile.isDirectory()) {
                     try {
                         File f = new File(path+list[i], name);
                         if (f.exists()) {
                             res = makeURL(f);
                             return res;
                         }
                     }
                     catch (Exception e) {}

                 }
             }
         }

         //otherwise look in JAR files
         byte [] resourceBytes;
         for (int i=0; i<jarFiles.size(); i++) {
             try {
                 File jf = (File)jarFiles.elementAt(i);
                 resourceBytes = loadFromJar(jf.getPath(), name);
                 if (resourceBytes != null) {
                     res = makeURL(name, jf);
                     return res;
                 }
             }
             catch (MalformedURLException e) {
                 IJ.error(e.toString());
             }
             catch (IOException e) {
                 IJ.error(e.toString());
             }
         }
         return null;
     }

     // make a URL from a file
     private URL makeURL (File fil) throws MalformedURLException {
         URL url = new URL("file","",fil.toString());
         return url;
     }

     // make a URL from a file within a JAR
     private URL makeURL (String name, File jar) throws
MalformedURLException {
         StringBuffer filename = new StringBuffer("file:///");
         filename.append(jar.toString());
         filename.append("!/");
         filename.append(name);
         //filename.insert(0,'/');
         String sf = filename.toString();
         String sfu = sf.replace('\\','/');
         URL url = new URL("jar","",sfu);
         return url;
     }

     /**
      * Returns a resource from the path or JAR files as an InputStream
      * @param name a resource name.
      */
     public InputStream getResourceAsStream(String name) {
         //try the system loader first
         InputStream is = super.getSystemResourceAsStream(name);
         if (is != null) return is;

         File resFile;

         //try plugins directory
         resFile = new File(path, name);
         try { // read the byte codes
             is = new FileInputStream(resFile);
         }
         catch (Exception e) {}
         if (is != null) return is;

         //try subdirectories
         resFile = new File(path);
         String[] list = resFile.list();
         if (list!=null) {
             for (int i=0; i<list.length; i++) {
                 resFile = new File(path, list[i]);
                 if (resFile.isDirectory()) {
                     try {
                         File f = new File(path+list[i], name);
                         is = new FileInputStream(f);
                     }
                     catch (Exception e) {}
                     if (is != null) return is;
                 }
             }
         }

         //look in JAR files
         byte [] resourceBytes;
         for (int i=0; i<jarFiles.size(); i++) {
             try {
                 File jf = (File)jarFiles.elementAt(i);
                 resourceBytes = loadFromJar(jf.getPath(), name);
                 if (resourceBytes != null){
                     is = new ByteArrayInputStream(resourceBytes);
                     return is;
                 }
             }
             catch (Exception e) {
                 IJ.error(e.toString());
             }
         }
         return null;
     }

     /**
      * Returns a Class from the path or JAR files. Classes are
automatically resolved.
      * @param className a class name without the .class extension.
      */
     public Class loadClass(String className) throws
ClassNotFoundException {
         return (loadClass(className, true));
     }

     /**
      * Returns a Class from the path or JAR files. Classes are resolved
if resolveIt is true.
      * @param className a String class name without the .class extension.
      *        resolveIt a boolean (should almost always be true)
      */
     public synchronized Class loadClass(String className, boolean
resolveIt) throws ClassNotFoundException {

         Class   result;
         byte[]  classBytes;

         // try the local cache of classes
         result = (Class)cache.get(className);
         if (result != null) {
             return result;
         }

         // try the system class loader
         try {
             result = super.findSystemClass(className);
             return result;
         }
         catch (Exception e) {}

         // Try to load it from plugins directory
         classBytes = loadClassBytes(className);
                //IJ.log("loadClass: "+ className + "  "+
(classBytes!=null?""+classBytes.length:"null"));
                if (classBytes==null) {
                        //IJ.log("ClassNotFoundException");
                        throw new ClassNotFoundException(className);
                }

                // ***** Correction of wrong className *********************************
               
                String test = new String("plugins.Diplom.");

                test += className;

                result = defineClass(test, classBytes, 0, classBytes.length);
               
                // *********************************************************************
               
                //Define it (parse the class file)
         //result = defineClass(className, classBytes, 0,
classBytes.length);
         if (result == null) {
             throw new ClassFormatError();
         }

         //Resolve if necessary
         if (resolveIt) resolveClass(result);

         cache.put(className, result);
         return result;
     }

     /**
      * This does the actual work of loading the bytes from the disk.
Returns an
      * array of bytes that will be defined as a Class. This should be
overloaded to have
      * the Class Loader look in more places.
      * @param name a class name without the .class extension.
      */

     protected byte[] loadClassBytes(String name) {
         byte [] classBytes = null;
         classBytes = loadIt(path, name);
         if (classBytes == null) {
             classBytes = loadFromSubdirectory(path, name);
             if (classBytes == null) {
                 // Attempt to get the class data from the JAR files.
                 for (int i=0; i<jarFiles.size(); i++) {
                     try {
                         File jf = (File)jarFiles.elementAt(i);
                         classBytes = loadClassFromJar(jf.getPath(), name);
                         if (classBytes != null)
                             return classBytes;
                     }
                     catch (Exception e) {
                         //no problem, try the next one
                     }
                 }
             }
         }
         return classBytes;
     }

     // Loads the bytes from file
     private byte [] loadIt(String path, String classname) {
         String filename = classname.replace('.','/');
         filename += ".class";
         File fullname = new File(path, filename);
         //ij.IJ.write("loadIt: " + fullname);
         try { // read the byte codes
             InputStream is = new FileInputStream(fullname);
             int bufsize = (int)fullname.length();
             byte buf[] = new byte[bufsize];
             is.read(buf, 0, bufsize);
             is.close();
             return buf;
         } catch (Exception e) {
             return null;
         }
     }

     private byte [] loadFromSubdirectory(String path, String name) {
         File f = new File(path);
         String[] list = f.list();
         if (list!=null) {
             for (int i=0; i<list.length; i++) {
                 //ij.IJ.write(path+"  "+list[i]);
                 f=new File(path, list[i]);
                 if (f.isDirectory()) {
                     byte [] buf = loadIt(path+list[i], name);
                     if (buf!=null)
                         return buf;
                 }
             }
         }
         return null;
     }

        // Load class from a JAR file
        byte[] loadClassFromJar(String jar, String className) {
                String name = className.replace('.','/');
                name += ".class";
                return loadFromJar(jar, name);
        }

        // Load class or resource from a JAR file
        byte[] loadFromJar(String jar, String name) {
                BufferedInputStream bis = null;
                try {
                        ZipFile jarFile = new ZipFile(jar);
                        Enumeration entries = jarFile.entries();
                        while (entries.hasMoreElements()) {
                                ZipEntry entry = (ZipEntry) entries.nextElement();
          if (entry.getName().equals(name)) {
                                        bis = new BufferedInputStream(jarFile.getInputStream(entry));
                                        int size = (int)entry.getSize();
                                        byte[] data = new byte[size];
                     int b=0, eofFlag=0;
                     while ((size - b) > 0) {
                         eofFlag = bis.read(data, b, size - b);
                         if (eofFlag==-1) break;
                         b += eofFlag;
                     }
                                        return data;
                                }
                        }
                }
      catch (Exception e) {}
      finally {
      try {if (bis!=null) bis.close();}
      catch (IOException e) {}
      }
      return null;
        }

}





package plugins.Diplom;

/*
  * Watershed algorithm
  *
  * Copyright (c) 2003 by Christopher Mei ([hidden email])
  *
  * This plugin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
  *
  * 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 plugin; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

import ij.IJ;
import ij.ImagePlus;
import ij.plugin.filter.PlugInFilter;
import ij.process.ByteProcessor;
import ij.process.ImageProcessor;

import java.util.Vector;

/**
  *  This algorithm is an implementation of the watershed immersion
algorithm
  *  written by Vincent and Soille (1991).
  *
  *  @Article{Vincent/Soille:1991,
  *   author =       "Lee Vincent and Pierre Soille",
  *   year =         "1991",
  *   keywords =     "IMAGE-PROC SKELETON SEGMENTATION GIS",
  *   institution =  "Harvard/Paris+Louvain",
  *   title =        "Watersheds in digital spaces: An efficient algorithm
  *                   based on immersion simulations",
  *   journal =      "IEEE PAMI, 1991",
  *   volume =       "13",
  *   number =       "6",
  *   pages =        "583--598",
  *   annote =       "Watershed lines (e.g. the continental divide) mark the
  *                  boundaries of catchment regions in a topographical map.
  *                  The height of a point on this map can have a direct
  *                  correlation to its pixel intensity. WIth this analogy,
  *                  the morphological operations of closing (or opening)
  *                  can be understood as smoothing the ridges (or filling
  *                  in the valleys). Develops a new algorithm for obtaining
  *                  the watershed lines in a graph, and then uses this in
  *                  developing a new segmentation approach based on the
  *                  {"}depth of immersion{"}.",
  *  }
  *
  *  A review of Watershed algorithms can be found at :
  *  http://www.cs.rug.nl/~roe/publications/parwshed.pdf
  *
  *  @Article{RoeMei00,
  *   author =       "Roerdink and Meijster",
  *   title =        "The Watershed Transform: Definitions, Algorithms and
  *                   Parallelization Strategies",
  *   journal =      "FUNDINF: Fundamenta Informatica",
  *   volume =       "41",
  *   publisher =    "IOS Press",
  *   year =         "2000",
  *  }
  **/

public class Watershed_Algorithm implements PlugInFilter {
     private int threshold;
     final static int HMIN = 0;
     final static int HMAX = 256;

     public int setup(String arg, ImagePlus imp) {
        if (arg.equals("about"))
            {showAbout(); return DONE;}
        return DOES_8G+DOES_STACKS+SUPPORTS_MASKING+NO_CHANGES;
     }

     public void run(ImageProcessor ip) {
        boolean debug = false;

        IJ.showStatus("Sorting pixels...");
        IJ.showProgress(0.1);

        /** First step : the pixels are sorted according to increasing grey
values **/
        WatershedStructure watershedStructure = new WatershedStructure(ip);
        if(debug)
            IJ.write(""+watershedStructure.toString());

        IJ.showProgress(0.8);
        IJ.showStatus("Start flooding...");

        if(debug)
            IJ.write("Starting algorithm...\n");

        /** Start flooding **/
        WatershedFIFO queue = new WatershedFIFO();
        int curlab = 0;

        int heightIndex1 = 0;
        int heightIndex2 = 0;
       
        for(int h=HMIN; h<HMAX; h++) /*Geodesic SKIZ of level h-1 inside level
h */ {
       
            for(int pixelIndex = heightIndex1 ;
pixelIndex<watershedStructure.size() ; pixelIndex++) /*mask all pixels
at level h*/ {
                WatershedPixel p = watershedStructure.get(pixelIndex);

                if(p.getIntHeight() != h) {
                    /** This pixel is at level h+1 **/
                    heightIndex1 = pixelIndex;
                    break;
                }

                p.setLabelToMASK();

                Vector neighbours = p.getNeighbours();
                for(int i=0 ; i<neighbours.size() ; i++) {
                    WatershedPixel q = (WatershedPixel) neighbours.get(i);
               
                    if(q.getLabel()>=0) {/*Initialise queue with neighbours at level h
of current basins or watersheds*/
                        p.setDistance(1);
                        queue.fifo_add(p);
                        break;
                    } // end if
                } // end for
            } // end for

       
            int curdist = 1;
            queue.fifo_add_FICTITIOUS();

            while(true) /** extend basins **/{
                WatershedPixel p = queue.fifo_remove();

                if(p.isFICTITIOUS())
                    if(queue.fifo_empty())
                        break;
                    else {
                        queue.fifo_add_FICTITIOUS();
                        curdist++;
                        p = queue.fifo_remove();
                    }
                if(debug) {
                    IJ.write("\nWorking on :");
                    IJ.write(""+p);
                }
               
                Vector neighbours = p.getNeighbours();
                for(int i=0 ; i<neighbours.size() ; i++) /* Labelling p by inspecting
neighbours */{
                    WatershedPixel q = (WatershedPixel) neighbours.get(i);
               
                    if(debug)
                        IJ.write("Neighbour : "+q);

                    /* Original algorithm :
                       if( (q.getDistance() < curdist) &&
                       (q.getLabel()>0 || q.isLabelWSHED()) ) {*/
                    if( (q.getDistance() <= curdist) &&
                        (q.getLabel()>=0) ) {
                        /* q belongs to an existing basin or to a watershed */
                       
                        if(q.getLabel() > 0) {
                            if( p.isLabelMASK() )
                                // Removed from original algorithm || p.isLabelWSHED() )
                                p.setLabel(q.getLabel());
                            else
                                if(p.getLabel() != q.getLabel())
                                    p.setLabelToWSHED();
                        } // end if lab>0
                        else
                            if(p.isLabelMASK())
                                p.setLabelToWSHED();
                    }
                    else
                        if( q.isLabelMASK() &&
                            (q.getDistance() == 0) ) {
                       
                            if(debug)
                                IJ.write("Adding value");
                            q.setDistance( curdist+1 );
                            queue.fifo_add( q );
                        }
                } // end for, end processing neighbours

                if(debug) {
                    IJ.write("End processing neighbours");
                    IJ.write("New val :\n"+p);
                    IJ.write("Queue :\n"+queue);
                }
            } // end while (loop)

            /* Detect and process new minima at level h */
            for(int pixelIndex = heightIndex2 ;
pixelIndex<watershedStructure.size() ; pixelIndex++) {
                WatershedPixel p = watershedStructure.get(pixelIndex);

                if(p.getIntHeight() != h) {
                    /** This pixel is at level h+1 **/
                    heightIndex2 = pixelIndex;
                    break;
                }

                p.setDistance(0); /* Reset distance to zero */
               
                if(p.isLabelMASK()) { /* the pixel is inside a new minimum */
                    curlab++;
                    p.setLabel(curlab);
                    queue.fifo_add(p);
               
               
                    while(!queue.fifo_empty()) {
                        WatershedPixel q = queue.fifo_remove();

                        Vector neighbours = q.getNeighbours();

                        for(int i=0 ; i<neighbours.size() ; i++) /* inspect neighbours of p2*/{
                            WatershedPixel r = (WatershedPixel) neighbours.get(i);
               
                            if( r.isLabelMASK() ) {
                                r.setLabel(curlab);
                                queue.fifo_add(r);
                            }
                        }
                    } // end while
                } // end if
            } // end for
        } /** End of flooding **/

        IJ.showProgress(0.9);
        IJ.showStatus("Putting result in a new image...");

        /** Put the result in a new image **/
        int width = ip.getWidth();

        ImageProcessor outputImage = new ByteProcessor(width, ip.getHeight());
        byte[] newPixels = (byte[]) outputImage.getPixels();

        for(int pixelIndex = 0 ; pixelIndex<watershedStructure.size() ;
pixelIndex++) {
            WatershedPixel p = watershedStructure.get(pixelIndex);
       
            if(p.isLabelWSHED() && !p.allNeighboursAreWSHED())
                newPixels[p.getX()+p.getY()*width] = (byte)255;
        }

        IJ.showProgress(1);
        IJ.showStatus("Displaying result...");

        new ImagePlus("Watershed", outputImage).show();
     }

     void showAbout() {
        IJ.showMessage("About Watershed_Algorithm...",
                       "This plug-in filter calculates the watershed of a 8-bit
images.\n" +
                       "It uses the immersion algorithm written by Vincent and Soille
(1991)\n"
                       );
     }
}






package plugins.Diplom;

/*
  * Watershed plugin
  *
  * Copyright (c) 2003 by Christopher Mei ([hidden email])
  *
  * This plugin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
  *
  * 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 plugin; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

import java.util.*;
import ij.*;

/** This class implements a FIFO queue that
  *  uses the same formalism as the Vincent
  *  and Soille algorithm (1991)
  **/

public class WatershedFIFO {
     private LinkedList watershedFIFO;

     public WatershedFIFO() {
        watershedFIFO = new LinkedList();
     }

     public void fifo_add(WatershedPixel p) {
        watershedFIFO.addFirst(p);
     }

     public WatershedPixel fifo_remove() {
        return (WatershedPixel) watershedFIFO.removeLast();
     }

     public boolean fifo_empty() {
        return watershedFIFO.isEmpty();
     }

     public void fifo_add_FICTITIOUS() {
        watershedFIFO.addFirst(new WatershedPixel());
     }

     public String toString() {
        StringBuffer ret = new StringBuffer();
        for(int i=0; i<watershedFIFO.size(); i++) {
            ret.append( ((WatershedPixel)watershedFIFO.get(i)).toString() );
            ret.append( "\n" );
        }
       
        return ret.toString();
     }
}





package plugins.Diplom;

/*
  * Watershed algorithm
  *
  * Copyright (c) 2003 by Christopher Mei ([hidden email])
  *
  * This plugin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
  *
  * 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 plugin; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

import java.lang.*;
import java.util.*;
import ij.*;

/**
  *  The aim of WatershedPixel is to enable
  *  sorting the pixels of an Image according
  *  to their grayscale value.
  *
  *  This is the first step of the Vincent
  *  and Soille Watershed algorithm (1991)
  *
  **/

public class WatershedPixel implements Comparable {
     /** Value used to initialise the image */
     final static int INIT = -1;
     /** Value used to indicate the new pixels that
      *  are going to be processed (intial value
      *  at each level)
      **/
     final static int MASK = -2;
     /** Value indicating that the pixel belongs
      *  to a watershed.
      **/
     final static int WSHED = 0;
     /** Fictitious pixel **/
     final static int FICTITIOUS = -3;

     /** x coordinate of the pixel **/
     private int x;
     /** y coordinate of the pixel **/
     private int y;
     /** grayscale value of the pixel **/
     private byte height;
     /** Label used in the Watershed immersion algorithm **/
     private int label;
     /** Distance used for working on pixels */
     private int dist;

     /** Neighbours **/
     private Vector neighbours;

     public WatershedPixel(int x, int y, byte height) {
        this.x = x;
        this.y = y;
        this.height = height;
        label = INIT;
        dist = 0;
        neighbours = new Vector(8);
     }

     public WatershedPixel() {
        label = FICTITIOUS;
     }

     public void addNeighbour(WatershedPixel neighbour) {
        /*IJ.write("In Pixel, adding :");
          IJ.write(""+neighbour);
          IJ.write("Add done");
        */
        neighbours.add(neighbour);
     }

     public Vector getNeighbours() {
        return neighbours;
     }



     public String toString() {
        return new String("("+x+","+y+"), height : "+getIntHeight()+", label :
"+label+", distance : "+dist);
     }



     public final byte getHeight() {
        return height;
     }

     public final int getIntHeight() {
        return (int) height&0xff;
     }

     public final int getX() {
        return x;
     }

     public final int getY() {
        return y;
     }


     /** Method to be able to use the Collections.sort static method. **/
     public int compareTo(Object o) {
        if(!(o instanceof WatershedPixel))
            throw new ClassCastException();

        WatershedPixel obj =  (WatershedPixel) o;

        if( obj.getIntHeight() < getIntHeight() )
            return 1;

        if( obj.getIntHeight() > getIntHeight() )
            return -1;

        return 0;
     }

     public void setLabel(int label) {
        this.label = label;
     }

     public void setLabelToINIT() {
        label = INIT;
     }

     public void setLabelToMASK() {
        label = MASK;
     }

     public void setLabelToWSHED() {
        label = WSHED;
     }


     public boolean isLabelINIT() {
        return label == INIT;
     }
     public boolean isLabelMASK() {
        return label == MASK;
     }
     public boolean isLabelWSHED() {
        return label == WSHED;
     }

     public int getLabel() {
        return label;
     }

     public void setDistance(int distance) {
        dist = distance;
     }

     public int getDistance() {
        return dist;
     }

     public boolean isFICTITIOUS() {
        return label == FICTITIOUS;
     }

     public boolean allNeighboursAreWSHED() {
        for(int i=0 ; i<neighbours.size() ; i++) {
            WatershedPixel r = (WatershedPixel) neighbours.get(i);
               
            if( !r.isLabelWSHED() )
                return false;
        }
        return true;
     }
}





package plugins.Diplom;

/*
  * Watershed algorithm
  *
  * Copyright (c) 2003 by Christopher Mei ([hidden email])
  *
  * This plugin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
  *
  * 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 plugin; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

import java.lang.*;
import java.util.*;
import ij.process.*;
import ij.*;
import java.awt.*;

/**
  *  WatershedStructure contains the pixels
  *  of the image ordered according to their
  *  grayscale value with a direct access to their
  *  neighbours.
  *
  **/

public class WatershedStructure {
     private Vector watershedStructure;

     public WatershedStructure(ImageProcessor ip) {
        byte[] pixels = (byte[])ip.getPixels();
        Rectangle r = ip.getRoi();
        int width = ip.getWidth();
        int offset, topOffset, bottomOffset, i;

        watershedStructure = new Vector(r.width*r.height);

        /** The structure is filled with the pixels of the image. **/
        for (int y=r.y; y<(r.y+r.height); y++) {
            offset = y*width;
       
            IJ.showProgress(0.1+0.3*(y-r.y)/(r.height));

            for (int x=r.x; x<(r.x+r.width); x++) {
                i = offset + x;

                int indiceY = y-r.y;
                int indiceX = x-r.x;
               
                watershedStructure.add(new WatershedPixel(indiceX, indiceY, pixels[i]));
            }
        }

        /** The WatershedPixels are then filled with the reference to their
neighbours. **/
        for (int y=0; y<r.height; y++) {

            offset = y*width;
            topOffset = offset+width;
            bottomOffset = offset-width;
       
            IJ.showProgress(0.4+0.3*(y-r.y)/(r.height));

            for (int x=0; x<r.width; x++) {
                WatershedPixel currentPixel =
(WatershedPixel)watershedStructure.get(x+offset);

                if(x+1<r.width) {
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x+1+offset));

                    if(y-1>=0)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x+1+bottomOffset));
               
                    if(y+1<r.height)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x+1+topOffset));
                }

                if(x-1>=0) {
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x-1+offset));
               
                    if(y-1>=0)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x-1+bottomOffset));
               
                    if(y+1<r.height)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x-1+topOffset));
                }
               
                if(y-1>=0)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x+bottomOffset));
               
                if(y+1<r.height)
                 
currentPixel.addNeighbour((WatershedPixel)watershedStructure.get(x+topOffset));
            }
        }

        Collections.sort(watershedStructure);
        //IJ.showProgress(0.8);
     }

     public String toString() {
        StringBuffer ret = new StringBuffer();

        for(int i=0; i<watershedStructure.size() ; i++) {
            ret.append( ((WatershedPixel) watershedStructure.get(i)).toString() );
            ret.append( "\n" );
            ret.append( "Neighbours :\n" );

            Vector neighbours = ((WatershedPixel)
watershedStructure.get(i)).getNeighbours();

            for(int j=0 ; j<neighbours.size() ; j++) {
                ret.append( ((WatershedPixel) neighbours.get(j)).toString() );
                ret.append( "\n" );
            }
            ret.append( "\n" );
        }
        return ret.toString();
     }

     public int size() {
        return watershedStructure.size();
     }

     public WatershedPixel get(int i) {
        return (WatershedPixel) watershedStructure.get(i);
     }
}





package plugins.Diplom;

//  this file contains the classes:
//  - public class ArrayDisplay
//  - class ScreenSelectorClass
//  - class ScreenSelectorCanvas extends ImageCanvas implements KeyListener

import java.awt.*;
import java.io.*;   // for PrintWriter
import java.util.*; // for Properties class
import java.awt.image.*;
import java.awt.event.*; // needed for KeyListener
import ij.*;
import ij.gui.*;
import ij.io.*;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;

public class ArrayDisplay {
        protected ImagePlus working_display;
        protected ImageProcessor[] working_ip_stack;
        // can be either an ImageWindow or a StackWindow:
        protected Object working_window;
        // false then using stackwindow:
        protected boolean onlyOneImage = false;
        protected ImageStack working_stack;
        protected ImageCanvas working_canvas;
        protected Graphics working_graphics;
        public static int DEPTH = 0, HEIGHT = 1, WIDTH = 2;
        public static int X=0, Y=1;
        protected String Label;
        protected String[] SliceLabels;
        protected int SliderDim, Depth, Height, Width;
        protected float alpha = 0.5f;

        /********** ArrayDisplay(byte[][] vals)
*************************************/

        // 3/21/02 assume vals array is indexed as [d][w][h]
        // in base ImageJ stacks do not have properties[]
        public ArrayDisplay(byte[][] vals) {
                this(vals, (String)null, (Properties)null);
        }

        /********** ArrayDisplay(byte[][] vals, String label)
***********************/

        public ArrayDisplay(byte[][] vals, String label) {
                this(vals, label, (Properties)null);
        }

        /********** ArrayDisplay(byte[][] vals, String label, Properties props)
*****/

        public ArrayDisplay(byte[][] vals, String label, Properties props) {
                Label = new String(label);
                SliceLabels = new String[1];
                SliceLabels[0] = new String(Label);
                SliderDim = DEPTH;
                Width = vals.length;
                Height = vals[0].length;
                Depth = 1;
                onlyOneImage = true;
                byte[][][] vals3d = new byte[1][Width][Height];
                vals3d[0] = vals;
                if (working_stack == null)
                        initAsByte((Properties)null);
                setPixels(vals3d);
                if (props != null)
                        working_display.setProperty("Info",props.get("Info"));
                working_display.updateAndDraw();
        }

        /********** ArrayDisplay(short[][] vals)
************************************/

        public ArrayDisplay(short[][] vals) {
                this(vals, (String)null, (Properties)null);
        }

        /********** ArrayDisplay(short[][] vals, String label)
**********************/
       
        public ArrayDisplay(short[][] vals, String label) {
                this(vals, label, (Properties)null);
        }

        /********** ArrayDisplay(short[][] vals, String label, Properties
props) ****/

        public ArrayDisplay(short[][] vals, String label, Properties props) {
                Label = new String(label);
                SliceLabels = new String[1];
                SliceLabels[0] = new String(Label);
                SliderDim = DEPTH;
                Width = vals.length;
                Height = vals[0].length;
                Depth = 1;
                onlyOneImage = true;
                short[][][] vals3d = new short[1][Width][Height];
                vals3d[0] = vals;
                if (working_stack == null)
                        initAsShort((Properties)null);
                setPixels(vals3d);
                if (props != null)
                        working_display.setProperty("Info",props.get("Info"));
                working_display.updateAndDraw();
        }

        /********** ArrayDisplay(float[][] vals)
************************************/

        public ArrayDisplay(float[][] vals) {
                this(vals, (String)null, (Properties)null);
        }

        /********** ArrayDisplay(float[][] vals, String label)
**********************/

        public ArrayDisplay(float[][] vals, String label) {
                this(vals, label, (Properties)null);
        }

        /********** ArrayDisplay(float[][] vals, String label, Properties
props) ****/

        public ArrayDisplay(float[][] vals, String label, Properties props) {
                Label = new String(label);
                SliceLabels = new String[1];
                SliceLabels[0] = new String(Label);
                SliderDim = DEPTH;
                Width = vals.length;
                Height = vals[0].length;
                Depth = 1;
                onlyOneImage = true;
                float[][][]vals3d = new float[1][Width][Height];
                for (int w=0; w<Width; w++)
                        for (int h=0; h<Height; h++)
                                vals3d[0][w][h] = vals[w][h];
                if (working_stack == null)
                        initAsFloat((Properties)null);
                setPixels(vals3d);
                if (props != null)
                        working_display.setProperty("Info",props.get("Info"));
                working_display.updateAndDraw();
        }

        /********** toString()
******************************************************/

        public String toString() {
                return("Array Display: '"+Label+"'   dims "+Width+":"+Height+":"+Depth);
        }

        /********** Copy(ArrayDisplay A)
********************************************/

        public void Copy(ArrayDisplay A) {
                this.working_display = A.working_display;
                this.working_ip_stack = A.working_ip_stack;
                this.working_window = A.working_window;
                this.onlyOneImage = A.onlyOneImage;
                this.working_stack = A.working_stack;
                this.working_canvas = A.working_canvas;
                this.working_graphics = A.working_graphics;
                this.Label = A.Label;
                this.SliceLabels = A.SliceLabels;
                this.SliderDim = A.SliderDim;
                this.Depth = A.Depth;
                this.Height = A.Height;
                this.Width = A.Width;
        }

        /********** getPixels - Methoden
********************************************/

        public float[][][] getPixels() {
                return(getPixels(new float[1][1][1]));
        }

        public float[][][] getPixels(float[][][] typeFlag) {
                float[][][] vals = new float[Depth][Width][Height];
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                    for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                            vals[d][w-1][h] = working_ip_stack[w].getPixelValue(h,d);
                // put with shown image width x depth, height as slider:
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                vals[d][w][h-1] = working_ip_stack[h].getPixelValue(w,d);
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                vals[d-1][w][h] = working_ip_stack[d].getPixelValue(w,h);
                return vals;
        }

        public short[][][] getPixels(short[][][] typeFlag) {
                short[][][] vals = new short[Depth][Width][Height];
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                        for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                                vals[d][w-1][h] = (short)(working_ip_stack[w].getPixel(h,d));
                // put with shown image width x depth, height as slider
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                vals[d][w][h-1] = (short)(working_ip_stack[h].getPixel(w,d));
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                vals[d-1][w][h] = (short)(working_ip_stack[d].getPixel(w,h));
                return vals;
        }

        public byte[][][] getPixels(byte[][][] typeFlag) {
                byte[][][] vals = new byte[Depth][Width][Height];
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                        for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                                vals[d][w-1][h] = (byte)(working_ip_stack[w].getPixel(h,d));
                // put with shown image width x depth, height as slider:
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                vals[d][w][h-1] = (byte)(working_ip_stack[h].getPixel(w,d));
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                vals[d-1][w][h] = (byte)(working_ip_stack[d].getPixel(w,h));
                return vals;
        }

        /********** setPixels - Methoden
********************************************/

        public void setPixels(byte[][][] vals) {
                if (Depth!=vals.length || vals[0].length!=Width ||
                        vals[0][0].length!=Height) {
                        // resize window
                        Depth=vals.length; Width=vals[0].length;  Height=vals[0][0].length;
                        initAsByte(working_display.getProperties());
                }
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                        for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[w].putPixel(h,d, vals[d][w-1][h]);
                // put with shown image width x depth, height as slider:
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[h].putPixel(w,d, vals[d][w][h-1]);
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                working_ip_stack[d].putPixel(w,h, vals[d-1][w][h]);
                working_display.updateAndDraw();
        }

        public void setPixels(short[][][] vals) {
                if (Depth!=vals.length || vals[0].length!=Width ||
                        vals[0][0].length!=Height) {
                        // resize window:
                        Depth=vals.length; Width=vals[0].length;  Height=vals[0][0].length;
                        initAsShort(working_display.getProperties());
                }
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                        for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[w].putPixel(h,d, vals[d][w-1][h]);
                // put with shown image width x depth, height as slider:
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[h].putPixel(w,d, vals[d][w][h-1]);
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                working_ip_stack[d].putPixel(w,h, vals[d-1][w][h]);
                working_display.updateAndDraw();
        }

        public void setPixels(float[][][] vals) {
                if (Depth!=vals.length || vals[0].length!=Width ||
                        vals[0][0].length!=Height) {
                        // resize window
                        Depth=vals.length; Width=vals[0].length;  Height=vals[0][0].length;
                        initAsFloat(working_display.getProperties());
                }
                // put with 2D shown image height x depth, width as slider:
                if (SliderDim==WIDTH)
                  for (int w=1; w<=Width; w++)
                        for (int h=0; h<Height; h++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[w].putPixel(h,d,
                                Float.floatToIntBits(vals[d][w-1][h]));
                // put with shown image width x depth, height as slider:
                else if (SliderDim==HEIGHT)
                  for (int h=1; h<=Height; h++)
                        for (int w=0; w<Width; w++)
                          for (int d=0; d<Depth; d++)
                                working_ip_stack[h].putPixel(w,d,
                                Float.floatToIntBits(vals[d][w][h-1]));
                // put with shown image width x height, depth as slider:
                else if (SliderDim==DEPTH)
                  for (int d=1; d<=Depth; d++)
                        for (int w=0; w<Width; w++)
                          for (int h=0; h<Height; h++)
                                working_ip_stack[d].putPixel(w,h,
                                Float.floatToIntBits(vals[d-1][w][h]));
                working_display.updateAndDraw();
        }

        public void setPixels(byte[][] vals) {
                byte[][][]vals3d = new byte[1][vals.length][vals[0].length];
                vals3d[0] = vals;
                setPixels(vals3d);
        }

        public void setPixels(short[][] vals) {
                short[][][]vals3d = new short[1][vals.length][vals[0].length];
                vals3d[0] = vals;
                setPixels(vals3d);
        }

        public void setPixels(float[][] vals) {
                float[][][]vals3d = new float[1][vals.length][vals[0].length];
                vals3d[0] = vals;
                setPixels(vals3d);
        }

        /********** initAsByte(Properties props)
************************************/

        private void initAsByte(Properties props) {
                IJ.showStatus("Initializing 3D byte array display: "+Label);
                // clear previous display:
                if (working_display != null) {
                  ((ImageWindow)working_window).dispose();
                  working_graphics.dispose();
                  System.gc();
                }
                // if (props==null) props = new Properties[MathI.max(Depth,
                // Width, Height)];
                if (SliderDim == WIDTH) {
                  working_stack = new ImageStack(Height, Depth);
                  working_ip_stack = new ByteProcessor[Width + 1];
                   for (int i=1; i<=Width; i++) {
                        working_ip_stack[i] = new ByteProcessor(Height, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == HEIGHT) {
                  working_stack = new ImageStack(Width, Depth);
                  working_ip_stack = new ByteProcessor[Height + 1];
                  for (int i=1; i<=Height; i++) {
                        working_ip_stack[i] = new ByteProcessor(Width, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == DEPTH) {
                  working_stack = new ImageStack(Width, Height);
                  working_ip_stack = new ByteProcessor[Depth + 1];
                  for (int i=1; i<=Depth; i++) {
                        working_ip_stack[i] = new ByteProcessor(Width, Height);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                if (onlyOneImage == true)
                  working_display = new ImagePlus(Label, working_ip_stack[1]);
                else working_display = new ImagePlus(Label, working_stack);
                if (props != null)
                  working_display.setProperty("Info",props.get("Info"));
                working_canvas = new ImageCanvas(working_display);
                if (onlyOneImage == true)
                  working_window = new ImageWindow(working_display, working_canvas);
                else working_window = new StackWindow(working_display, working_canvas);
                working_graphics = working_canvas.getGraphics();
                // working_canvas.zoomIn(0,0);
                // make images larger
                IJ.showStatus("");
        }

        /********** initAsShort(Properties props)
***********************************/

        private void initAsShort(Properties props) {
                IJ.showStatus("Initializing 3D short array display: "+Label);
                // clear previous display:
                if (working_display != null) {
                  ((ImageWindow)working_window).dispose();
                  working_graphics.dispose();
                  System.gc();
                }
                // if (props==null) props = new Properties[MathI.max(Depth,
                // Width, Height)];
                if (SliderDim == WIDTH) {
                  working_stack = new ImageStack(Height, Depth);
                  working_ip_stack = new ShortProcessor[Width + 1];
                   for (int i=1; i<=Width; i++) {
                        working_ip_stack[i] = new ShortProcessor(Height, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == HEIGHT) {
                  working_stack = new ImageStack(Width, Depth);
                  working_ip_stack = new ShortProcessor[Height + 1];
                  for (int i=1; i<=Height; i++) {
                        working_ip_stack[i] = new ShortProcessor(Width, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == DEPTH) {
                  working_stack = new ImageStack(Width, Height);
                  working_ip_stack = new ShortProcessor[Depth + 1];
                  for (int i=1; i<=Depth; i++) {
                        working_ip_stack[i] = new ShortProcessor(Width, Height);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                if (onlyOneImage == true)
                  working_display = new ImagePlus(Label, working_ip_stack[1]);
                else working_display = new ImagePlus(Label, working_stack);
                if (props != null)
                  working_display.setProperty("Info",props.get("Info"));
                working_canvas = new ImageCanvas(working_display);
                if (onlyOneImage == true)
                  working_window = new ImageWindow(working_display, working_canvas);
                else working_window = new StackWindow(working_display, working_canvas);
                working_graphics = working_canvas.getGraphics();
                // working_canvas.zoomIn(0,0); // make images larger
                 IJ.showStatus("");
        }

        /********** initAsFloat(Properties props)
***********************************/

        private void initAsFloat(Properties props) {
                IJ.showStatus("Initializing 3D array display: "+Label);
                // clear previous display:
                if (working_display != null) {
                  ((ImageWindow)working_window).dispose();
                  working_graphics.dispose();
                  System.gc();
                }
                // if (props==null) props = new Properties[MathI.max(Depth,
                // Width, Height)];
                if (SliderDim == WIDTH) {
                  working_stack = new ImageStack(Height, Depth);
                  working_ip_stack = new FloatProcessor[Width + 1];
                  for (int i=1; i<=Width; i++) {
                        working_ip_stack[i] = new FloatProcessor(Height, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == HEIGHT) {
                  working_stack = new ImageStack(Width, Depth);
                  working_ip_stack = new FloatProcessor[Height + 1];
                  for (int i=1; i<=Height; i++) {
                        working_ip_stack[i] = new FloatProcessor(Width, Depth);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                else if (SliderDim == DEPTH) {
                  working_stack = new ImageStack(Width, Height);
                  working_ip_stack = new FloatProcessor[Depth + 1];
                  for (int i=1; i<=Depth; i++) {
                        working_ip_stack[i] = new FloatProcessor(Width, Height);
                        working_stack.addSlice(SliceLabels[i-1], working_ip_stack[i]);
                        //, props[i-1]);
                  }
                }
                if (onlyOneImage == true)
                  working_display = new ImagePlus(Label, working_ip_stack[1]);
                else working_display = new ImagePlus(Label, working_stack);
                if (props != null)
                  working_display.setProperty("Info",props.get("Info"));
                working_canvas = new ImageCanvas(working_display);
                if (onlyOneImage == true)
                  working_window = new ImageWindow(working_display, working_canvas);
                else working_window = new StackWindow(working_display, working_canvas);
                working_graphics = working_canvas.getGraphics();
                // working_canvas.zoomIn(0,0);
                // make images larger
                IJ.showStatus("");
        }

        /********** getImageProcessor()
*********************************************/

        public ImageProcessor getImageProcessor() {
                return working_ip_stack[1];
        }

        /********** ImagePlus getImagePlus()
****************************************/

        public ImagePlus getImagePlus() {
                return working_display;
        }

        /********** getImageStack()
*************************************************/

        public ImageStack getImageStack() {
                return working_stack;
        }

        /********** getScreenSize()
*************************************************/

        public Dimension getScreenSize() {
                // usage: Dimension.width, .height
                return ((ImageWindow)working_window).getSize();
        }

        /********** getScreenLocation() und setScreenLocation() - Methoden
**********/

        public Point getScreenLocation() {
                // usage: Point.x, .y
                return ((ImageWindow)working_window).getLocation();
        }

        public void setScreenLocation(int topleftx, int toplefty) {
                ((ImageWindow)working_window).setLocation(topleftx, toplefty);
        }

        /********** getStackWindow()
************************************************/

        public StackWindow getStackWindow() {
                return ((StackWindow)working_window);
        }
       
        /********** getIPStack()
****************************************************/
       
        public ImageProcessor[] getIPStack() {
                return working_ip_stack;
        }
       
        /********** getGraphics()
***************************************************/
       
        public Graphics getGraphics() {
                return working_graphics;
        }
       
        /********** getImageCanvas()
************************************************/
       
        public ImageCanvas getImageCanvas() {
                return working_canvas;
        }

        /********** invertLUT()
*****************************************************/

        public void invertLUT() {
                for(int i=1; i<working_ip_stack.length; i++)
                        working_ip_stack[i].invert();
        }
       
        /********** getCurrentSlice() und setCurrentSlice - Methoden
****************/

        public int getCurrentSlice() {  // indexed 1...N
                return working_display.getCurrentSlice();
        }
       
        public void setCurrentSlice(int curSlice) {  // indexed 1...N
                working_display.setSlice(curSlice);
                ((StackWindow)working_window).showSlice(curSlice);
        }
       
        /********** getRoi(), setRoi und killRoi - Methoden
*************************/

        public Roi getRoi() {  // indexed 1...N
        return (working_display.getRoi());
        }
       
        public void setRoi(Roi roi) {
                working_display.setRoi(roi);
        }
       
        public void killRoi() {
                working_display.killRoi();
        }
       
        /********** setAlpha und getAlpha - Methoden
*********************************/
       
        public void setAlpha(float newAlpha) {
                alpha = newAlpha;
        }
       
        public float getAlpha() {
                return alpha;
        }
       
        /********** clearOverlays()
*************************************************/

        public void clearOverlays() {
                working_display.updateAndDraw();
                IJ.wait(100); // give system time to draw
        }
       
        /********** overlayPoint und overlayPoints - Methoden
***********************/

        public void overlayPoints(Point2D[] points, int marksize) {
                for (int i=0; i<points.length; i++)
                        overlayPoint(points[i].x, points[i].y, marksize, "green");
        }
       
        public void overlayPoints(Point2D[] points, int marksize, String color) {
                for (int i=0; i<points.length; i++) {
                        overlayPoint(points[i].x, points[i].y, marksize, color);
                        if (i>0)
                                overlayLine(points[i-1], points[i], color);
                        else
                                overlayLine(points[0], points[points.length-1], color);
                }
        }
       
        public void overlayPoint(Point2D point, int marksize) {
                overlayPoint(point.x, point.y, marksize, "green");
        }
       
        public void overlayPoint(Point2D point, int marksize, String color) {
                overlayPoint(point.x, point.y, marksize, color);
        }

        public void overlayPoints(float[] point, int marksize) {
                overlayPoint(point[X], point[Y], marksize, "green");
        }
       
        public void overlayPoints(float[] point, int marksize, String color) {
                overlayPoint(point[X], point[Y], marksize, color);
        }
       
        public void overlayPoints(float[][] points, int marksize) {
                for (int i=0; i<points.length; i++)
                        overlayPoint(points[i][X], points[i][Y], marksize, "green");
        }
       
        public void overlayPoints(float[][] points, int marksize, String color) {
                for (int i=0; i<points.length; i++)
                        overlayPoint(points[i][X], points[i][Y], marksize, color);
        }
       
        public void overlayPoint(float x, float y, int marksize) {
                overlayPoint(x,y, marksize, "green");
        }
       
        public void overlayPoint(float x, float y, int marksize, String color) {
                int scrTLx = ImageTools.Int(working_canvas.screenX(ImageTools.Int(x +
0.5f))
                -(float)marksize/2f); // shift by half pixel
                int scrTLy = ImageTools.Int(working_canvas.screenY(ImageTools.Int(y +
0.5f))
                -(float)marksize/2f);
                working_graphics.setColor(getColor(color));
               
                // from the java man pages: "If you draw a figure that covers a given
                //  rectangle, that figure occupies one extra row of pixels on the right
                //  and bottom edges as compared to filling a figure that is bounded by
                //  that same rectangle."
                // working_graphics.fillOval(scrTLx, scrTLy, marksize, marksize);
               
                // force it to draw (system does not):
                working_graphics.drawRect(scrTLx-1, scrTLy-1, marksize, marksize);
        }
       
        /********** overlayLine und overlayLines - Methoden
*************************/

        public void overlayLine(Point2D point1, Point2D point2