Tips for developers
An unsorted list of hints that you might find useful:
Contents
- 1 Compile & Execute a Class
- 2 Rapidly Prototype a Plugin
- 3 Find the .jar File Containing a Certain Class
- 4 Using ImageJ Effectively
- 4.1 How to read a file into an ImagePlus
- 4.2 How to get the current image
- 4.3 Making a new image stack -- quickly!
- 4.4 How to display an exception in a window
- 4.5 How to show a plot
- 4.6 Duplicate, or convert between, ImageProcessor types
- 4.7 How to store settings persistently
- 4.8 How to turn a number into a string, using a given number of decimal places
- 4.9 How to abort a plugin completely
- 4.10 Interact with the ROI manager
- 4.11 Show a results table
- 4.12 How to find equivalent API commands between ImageJ1 and ImageJ2?
- 5 Tips for Graphical User Interface (GUI) programming
Compile & Execute a Class
You do not need to call javac
yourself with a long classpath:
$ ./fiji --javac YourClass.java
and you can call its main()
method just as easy:
$ ./fiji YourClass.class argument1 argument2
Rapidly Prototype a Plugin
It is often easier to start out with a Jython, JRuby or BeanShell script, as you do not have to care about strict typing, exceptions or recompiling. Just place your script (with the correct extension -- .py, .rb or .bsh) into the plugins/ folder and execute the script. Fiji will always execute the current version of the script, so you can edit and run the script without restarting Fiji.
Of course, it is even more convenient to use the Script Editor...
Once you have working code, you can turn it into a proper plugin (this is easiest with BeanShell, as its syntax is closest to Java already), adding strict typing and exception handling as needed.
Find the .jar File Containing a Certain Class
Sometimes, the compiler complains about a class not having a certain method or interface, but you know it must contain it. More often than not, that class exists in different versions in your classpath. Find out with
$ ./fiji bin/find-jar-for-class.py the.class.youre.looking.For
If you want to do that with an installed Fiji (i.e. when bin/ is missing), you can start the Script Editor and execute a BeanShell like this:
import ij.IJ; print(IJ.getClassLoader() .loadClass("the.class.youre.looking.For") .getResource("For.class").toString());
This will output the URL to the .class file, including the path to the enclosing .jar file.
Using ImageJ Effectively
ImageJ has a simple API, but it is also big, so here are a few pointers to some useful parts.
How to read a file into an ImagePlus
ImagePlus image = IJ.openImage(path);
How to get the current image
ImagePlus img = WindowManager.getCurrentImage(); // current image ImageProcessor ip = img.getProcessor(); // current slice
Making a new image stack -- quickly!
ImagePlus image = IJ.createImage("my image", "RGB", 640, 480, 20);
How to display an exception in a window
This is especially useful on Windows, where you usually do not see the console:
IJ.handleException(exception);
This is available since ImageJ 1.43g, as well as the option to set a different exception handler using
IJ.setExceptionHandler(new IJ.ExceptionHandler() { public void handle(Throwable exception) { // do something } });
How to show a plot
ImageJ offers the ij.gui.Plot
class to make a window showing a plot. Use it like this:
Plot plot = new Plot("The window title", "labels on the x-axis", "labels on the y-axis", float_array_of_x_values, float_array_of_y_values); plot.show();
Instead of float arrays, you can also use double arrays.
If you need to update the plot at some stage, you need to save the return value of show():
Plot plot = new Plot("The window title", "labels on the x-axis", "labels on the y-axis", float_array_of_x_values, float_array_of_y_values); PlotWindow window = plot.show(); // ... Plot update = new Plot("Dummy", "x labels", "y labels", x_values, y_values); window.drawPlot(update);
To add another plot to the same window, use the addPoints()
method:
plot.addPoints(float_array_of_x_values, float_array_of_y_values, Plot.LINE);
The plots can be drawn in different colors like this:
Plot plot = new Plot("The window title", "labels on the x-axis", "labels on the y-axis", float_array_of_x_values, float_array_of_y_values); plot.setColor(Color.RED); plot.draw(); plot.addPoints(float_array_of_x_values, another_float_array_of_y_values, Plot.LINE); plot.setColor(Color.BLUE); plot.draw();
You might need to adjust the bounding box if the second plot does not match the bounding box of the first one by using the setLimits()
method before the call to plot.draw();
Duplicate, or convert between, ImageProcessor types
The ImageProcessor
class has several useful methods: duplicate(), convertToByte(), convertToFloat(),
convertToRGB(), and convertToShort().
This class also has some other goodies, such as methods for convolution.
How to store settings persistently
ImageJ (and therefore Fiji, too) has a way to store key/value pairs persistently, i.e. they are available even after a restart. The settings are stored in a file called IJ_Prefs.txt in the subdirectory called .imagej/ in your home directory (on Windows, directly in your home directory; on Mac, in ~/Library/Preferences).
Note: the settings are only saved upon regular exit of Fiji; If you kill the process, or if the Java Runtime crashes, they are not saved. You can ask for the settings to be saved explicitly, though.
Example:
import ij.Prefs; ... // save key/value pairs Prefs.set("my.persistent.name", "Grizzly Adams"); Prefs.set("my.persistent.number", 10); .... // retrieve the values (with default values) int myNumber = Prefs.get("my.persistent.number", 0); // explicitly save the preferences _now_ Prefs.savePreferences();
Note: do not use the getString()
or getInt()
; These methods do not have any setter methods, and they do not access the same values as the get()
method (get()
actually prefixes the keys with a dot)!
How to turn a number into a string, using a given number of decimal places
Use the d2s()
method of the ij.IJ
class:
String message = "The current temperature is " + IJ.d2s(degrees, 1) + "° Celsius";
How to abort a plugin completely
Sometimes, you want to abort a plugin without catching an exception at the highest level(s), without having ImageJ show an exception window. You can do that:
throw new RuntimeException(Macro.MACRO_CANCELED);
The special message Macro.MACRO_CANCELED will tell ImageJ not to show the exception message and stack trace in a text window.
Interact with the ROI manager
To add ROIs to the ROI manager, do something like this:
RoiManager manager = RoiManager.getInstance(); if (manager == null) manager = new RoiManager(); manager.addRoi(roi);
If you want to add the ROI with a slice label, you have to jump through a hoop:
int currentSlice = image.getCurrentSlice(); RoiManager manager = RoiManager.getInstance(); if (manager == null) manager = new RoiManager(); // the first slice is 1 (not 0) image.setSliceWithoutUpdate(slice); manager.add(image, rois[i], slice); image.setSlice(currentSlice);
To retrieve all ROIs from the ROI manager, do this:
RoiManager manager = RoiManager.getInstance(); if (manager != null) { Hashtable<String, Roi> table = (Hashtable<String, Roi>)manager.getROIs(); for (String label : table.keySet()) { int slice = manager.getSliceNumber(label); Roi roi = table.get(label); ... <do something with the ROI and the slice> ... } }
If you need to preserve the order of the ROIs in the ROI manager, unfortunately you'll have to access the AWT listbox:
import java.awt.List; ... RoiManager manager = RoiManager.getInstance(); if (manager != null) { List labels = manager.getList(); Hashtable<String, Roi> table = (Hashtable<String, Roi>)manager.getROIs(); for (int i = 0; i < labels.getItemCount(); i++) { String label = labels.getItem(i); int slice = manager.getSliceNumber(label); Roi roi = table.get(label); ... <do something with the ROI and the slice> ... } }
To get just the selected ROIs, use code similar to this:
import java.awt.List; ... RoiManager manager = RoiManager.getInstance(); if (manager != null) { String[] labels = manager.getList().getSelectedItems(); Hashtable<String, Roi> table = (Hashtable<String, Roi>)manager.getROIs(); for (String label : labels) { int slice = manager.getSliceNumber(label); Roi roi = table.get(label); ... <do something with the ROI and the slice> ... } }
Show a results table
First you need to get the results table instance (or create one):
ResultsTable rt = Analyzer.getResultsTable(); if (rt == null) { rt = new ResultsTable(); Analyzer.setResultsTable(rt); }
Then you can add a new row:
rt.incrementCounter();
... and add entries by column label:
rt.addValue("slice", slice); rt.addValue("bratwurst", -1);
Finally make sure that the result table is displayed/updated:
rt.show("Results");
How to find equivalent API commands between ImageJ1 and ImageJ2?
ImageJ1-ImageJ2 cheat sheet is available.
Tips for Graphical User Interface (GUI) programming
Programming with Swing components
When programming with Swing, beware that Swing is not thread safe. Swing's golden rule states that:
- Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
When has a Swing component been realized? When it is visible inside Window or JFrame that got a call to setVisible(true)
. This implies that its paint(Graphics)
method has been called or will be called soon.
These are the methods that can realize a component, or rather, methods called on a Window or Frame or JFrame that will realize all its children components:
setVisible(true) show() pack() // this might surprise you
There is a class which helps you with all this: SwingUtilities. Example: to call pack()
from within the constructor (which might or might not be called from the Event Dispatch Thread):
if (SwingUtilities.isEventDispatchThread()) pack(); else try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { pack(); }}); } catch (Exception e) { /* ignore */ }
Information extracted from the Swing guide (now only available from via web.archive.org as Oracle removed the original docs), additional information can be found in the Concurrency in Swing lesson of The Java Tutorials.