ImageJ: Tipps und Tricks / Fallen, die man vermeiden sollte

Wolfgang Konen,
Institut für Informatik, TH Köln
2021

Benutzung ImageJ

o   AnalyzeAnalyze Particle: Als Objekt wird gezählt, was im aktuellen Bild schwarz dargestellt ist (also Grauwert 0 oder Wert 255 bei "inverting LUT"). Wenn man ein Bild mit weißen Objekten hat, wird der (in der Regel zusammenhängende) schwarze Hintergrund als ein großes Objekt gezählt!

o   Bei Edit – SelectionCreate Mask wird die aktuelle ROI als Maske dargestellt: im Bild schwarz, hat aber wg. "inverting LUT" den Wert 255.

o   ProcessImage Calculator: Beim Subtrahieren zweier Byte- oder Short-Bilder (8-bit oder 16-bit) werden negative Werte (z.B. -2) abgeschnitten (geclippt), also auf 0 (!!) gesetzt. Da das meist nicht das gewünschte Resultat ist: Besser Resultat als 32-bit-Float-Bild ablegen (Checkbox 32-bit Result) und danach dieses Resultat wieder auf 8-bit (bzw. 16-bit) konvertieren (bildet [Min,Max] für 8-bit auf [0,255] ab).

o   Image – Stacks – Convert Images to Stack: Wenn man viele gleichartige Bilder hat, die man miteinander vergleichen will, braucht man nicht unbedingt mühselig die Windows übereinanderzuschieben, sondern man kann mit dem Befehl "Convert Images to Stack" alle Bilder in einen Stack packen, in dem sie dann genau übereinanderliegen und man kann mit "<" und ">" bequem hin- und herblättern. (Geht aber nur, wenn alle offenen Bilder dieselbe Größe UND denselben Typ haben.)

o   Platziert man den Cursor an einer bestimmten Stelle im Bild und drückt dann mehrmals +, dann zoomt ImageJ an genau der Stelle des Cursors ein. Mit   zoomt man wieder heraus.

 

Programmierung ImageJ-Plugins

o   Man kann ImageJ’s Plugin-Directory „umbiegen“, indem man die Konfigurationsdatei ImageJ.cfg in <ImageJ> umeditiert:

.
jre\bin\javaw.exe
-Xmx640m -cp ij.jar -Dplugins.dir="E:\ImageJ_Plugins" ij.ImageJ

Dann bekommt man die Plugins, die in E:\ImageJ_Plugins\ gespeichert sind (ImageJ zeigt die Class-Files mit Underscore an, die es in E:\ImageJ_Plugins\ findet und führt nach Edit… - Ctrl-R und Compilation auch das neu erzeugte Class-File von dort aus.).

o   Man unterscheide zwischen Rectangle ImageProcessor.getRoi() (liefert immer ein Rectangle zurück)
und Roi ImagePlus.getRoi() (kann auch null zurückliefern)

o   getPixel(i,j) liefert 0 zurück, KEINE Exception, wenn ein Pixel (i,j) ausserhalb des gültigen Bildbereiches angefordert wird (z.B. i=-1, j=-2)

o   Eine Falle bei Process – Filters – Convolve  ist: Wenn im Laplace negative Werte auftauchen, man dies auf ein Grauwertbild anwendet (8-Bit) so wird alles kleiner Null abgeschnitten (!!! kaum das gewünschte Ergebnis). Reparatur: vorher auf 32bit konvertieren.

o   Pixelzugriff für FloatProcessor: Methoden float getPixelValue(int,int) und putPixelValue(int,int,double), NICHT die Methode int getPixel(int,int).
(Die Methode int getPixel(int,int) liefert zwar auch etwas zurück, aber das muss man mit Float.intBitsToFloat() nach float konvertieren!!)

o   Die Methode ColorProcessorcp; cp.putPixel(int i,int j,int val) funktioniert zwar, liefert aber i.d.R. nicht das gewünschte Resultat. Will man z.B. das Pixel auf Weiss setzen, so liefert cp.putPixel(i,j,255) ein blaues (!) Pixel (klar, denn von RGB wird nur das untere 'B' gesetzt. Was funktioniert: cp.putPixel(i,j,0xFFFFFF) oder cp.putPixel(i,j,{255,255,255}) (letzteres deshalb, weil es eine putPixel(int,int,int[])–Methode gibt).
Analoges gilt für cp.getPixel(...)

Befüllen eines ImageStack

o   Befüllen eines ImageStack: Wenn man einen ImageStack in einer Schleife mittels addSlice() befüllt, kann man sich leicht einen Fehler einhandeln, wenn man einen sukzessive veränderten ImageProcessor im Stack abspeichern will:

ImageStack istack = new ImageStack(64,64);
ByteProcessor ip = new ByteProcessor(64,64);
for (int i=0; i<10; i++)
     ip = ... // neuen Inhalt zuweisen
     istack.addSlice(“i: “+i, ip);
}

Im Ergebnis werden alle Bilder im Stack den gleichen (!!) Inhalt haben, was in der Regel unerwünscht ist. Das liegt daran, dass addSlice, wie jede andere Java-Methode nur eine Referenz auf das Objekt ip erhält und dass auch nur die Referenz auf ip, nicht sein aktueller Inhalt, im Stack gespeichert wird. (Grund: Die Befüllung eines Stacks soll schnell und ohne Speicher-Overhead gehen.) Wenn man aber den zeitlich veränderlichen Zustand von ip speichern will, dann muss man bei jedem addSlice ein ip.duplicate() bemühen:

for (int i=0; i<10; i++)
     ip = ... // neuen Inhalt zuweisen
     istack.addSlice(“i: “+i, ip.duplicate());
}

Wie visualisiert man eine Kurve (x vs y, PlotWindow)?

o   Indem man die x- und y-Werte an ein PlotWindow übergibt. Wir zeigen dies am Beispiel eines kumulativen Histogramms:

           public void run(ImageProcessor ip) {

                double[] cumhist = new double[256];

double[] xval = new double[256];

int[] hist = ip.getHistogram();

                cumhist[0] = hist[0];

                for (int i=1; i<256; ++i) {

                     cumhist[i] = cumhist[i-1]+hist[i];

                     xval[i] = i;

     }

     PlotWindow p1 = new PlotWindow("Cum.Histo","x","y",xval,cumhist);

     p1.setColor(Color.blue);

     p1.draw();

}

o   Man beachte, dass der Plot erst nach p1.draw() im PlotWindow erscheint (denn vorher kann man noch Befehle absetzen, wie auch setLimits, die das Aussehen des Plots beeinflussen).