2016-04-19 - Writing ImageJ2 Plugins: A Beginner's Perspective



Writing ImageJ2 Plugins: A Beginner's Perspective

Preamble

Before you consider to write your own plugin for ImageJ, please note that writing a script has a much lower barrier to entry than Java plugin development.

Finding information

The search engines will point you to both http://imagej.net/ and http://developer.imagej.net/. Avoid using http://developer.imagej.net/ for anything these days. It is a legacy site, in the process of being totally phased out. If you are looking for downloads, see the Downloads page.

Configure your environment

You will need:

Environment used for testing this guide:

  • Debian Jessie
  • OpenJDK 1.8.0
  • NetBeans IDE version 8.0.2
  • Git 2.1.4
  • Apache Maven 3.0.5

Git is a source-code-management system with revision control.

Maven is a build automation tool used primarily for Java projects.

The ImageJ wiki mentions another software tool, called Jenkins, which is according to Wikipedia "an open source continuous integration tool written in Java", whatever this means. Tools like Maven and Jenkins make it more complicated for casual developers to understand the workflow of ImageJ at the beginning. However, these tools are well maintained by the community so that you will not have to think about them too much initially, and they facilitate the programming process. For example, concerning "Jenkins" it is enough to know that "continuous integration" means merging all developer working copies to a shared mainline several times a day. For the development of a single plugin for our personal use we can ignore Jenkins for the moment. It works in the background and does its job. That's all we need to know.

All source code is on GitHub. As ImageJ nowadays is a rather complex project its development is split into several ImageJ subprojects. For a beginner it is hard to understand the interaction of the different available projects which all contribute under the label "SciJava" to ImageJ2. The nice thing is, that Maven will help to pull in the necessary code from all ImageJ subprojects automatically with the help of configurations files which are supplied by the ImageJ2 developer community. The ImageJ wiki provides a very first overview of the SciJava ecosystem of ImageJ2.

These configuration files contains information about the project and various configuration detail used by Maven to build the project(s). The configuration files are named pom.xml where pom stands for "Project Object Model", whatever this means. The pom.xml files help to organize everything needed to build ImageJ. You can use any Maven-based project you want with that approach, not just ImageJ. So e.g. you can import fiji/fiji that way, or an individual plugin such as fiji/AnalyzeSkeleton.

In general, there are two alternative strategies to develop your plugin:

  • Use your IDE without Maven
  • Use your IDE with Maven

Using NetBeans without Maven

When you build your own plugins with an IDE, the ImageJ project will link in all plugins as precompiled JAR dependencies (JAR files are archives for java projects).

Download the current JAR files, e.g. imagej-2.0.0-SNAPSHOT-all.jar, which you intend to use as library in your local project.

You could also use the JAR file to compile your own plugins, which are distributed in the Fiji.app/plugins directory.

If you download Fiji via Fiji/Downloads then take care to select the right version of Fiji. The most prominent download option on top of the page is compiled with JDK 1.8, while you can download so-called "life-line" versions at the bottom of the page which are compiled with JDK 1.6 to ensure compatibility with older plugins not supported by the ImageJ2 Team. For details look here.

In any case you need to open a new project, assign the project name, its directory location and add the JAR files as libraries.

  • File ▶ New Project: Java Application (Project Name, Project Location)
  • Right click on the Project in the tree view window ▶ Properties
  • Libraries ▶ Add JAR/Folder: point to the JAR files you downloaded for ImageJ/Fiji.

A somewhat outdated but rather detailed description how to work with an older version of NetBeans (version 6.7) including bit of customization of the build.xml file can be found here.


Using NetBeans with Maven

Using Maven to develop your plugins is a much better approach. You will not have to commit any JAR files to source control. You can pin your code to fixed, known versions of its dependencies that will provide reproducible builds for many years to come.

Getting the ImageJ sources in NetBeans should be as simple as importing the source from the Git repository.

The following was adapted from the Developing ImageJ in NetBeans page.

Import and build the project:

  • Run NetBeans
  • Choose Team ▶ Git ▶ Clone... from the NetBeans menu
  • For the Repository URL, enter: https://github.com/fiji/fiji or alternatively enter https://github.com/imagej/imagej
  • Click Next, check the master* branch, then Next again, then Finish
  • When prompted, click Open Project... in case of Error Messages click on "Resolve".

Launch the program:

  • Expand the "ImageJ Projects" project, then "Modules"
  • Expand the "ImageJ POM: User Interface" module
  • Double-click the "ImageJ Application" project to open it
  • Right-click the "ImageJ Application" project and choose "Run"
  • On the Main Class dialog, choose "net.imagej.Main"
  • To expand the projects you can also right click on the top-level "ImageJ Projects" and choose "Open Required Projects" (and "Close Required Projects" to close). During development you must select "Open Required Projects" before you can successfully do "Find Usages" in the "Open Projects" scope.

Do not expect to find the ImageJ sources after cloning imagej/imagej or fiji/fiji.

If you want to look at the source code to study how to program image analysis algorithms then you will need to clone other GitHub projects.

The repository imagej/imagej1, for example, contains the source code of ImageJ 1.x, but it does not use Maven. The ImageJA project at imagej/ImageJA is a Mavenized version of ImageJ 1.x with a clean Git history. For curious people like me: the "A" in ImageJA was originally used for "Applet" and to differentiate the project from ImageJ itself.


Details of Writing Plugins

Tutorials

Getting Started

Start from an existing plugin as a template:

Import it as a sample project into your IDE and modify this project according to your needs:

  • NetBeans: File ▶ Open Project

The following lines are copied/cited from the README.md file of the minimal-ij1-plugin:

  • Edit the pom.xml file and change
    • the artifactId
      • Note: for ImageJ 1.x plugins the artifactId should contain a _ character. If you write an ImageJ2 command (like the ones linked above in simple-commands tutorial) then the underscore is unnecessary.
    • the groupId
      • You should put a groupId. It is misleading to leave it off, because then net.imagej (or sc.fiji if you used pom-fiji as parent) will be inherited. And your project is probably not a core ImageJ project.
    • the version (note that you typically want to use a version number ending in -SNAPSHOT to mark it as a work in progress rather than a final version)
    • the dependencies (read how to specify the correct groupId/artifactId/version triplet here)
    • the developer information
    • the scm information
  • Remove the Process_Pixels.java file and add your own .java files to src/main/java/<package>/ (if you need supporting files—like icons—in the resulting .jar file, put them into src/main/resources/)
  • Edit src/main/resources/plugins.config
    • This is only needed for ImageJ 1.x plugins. For ImageJ2 commands, the information is provided by the @Plugin annotation at the top of the Java class.
  • Replace the contents of README.md with information about your project.

Additional sample plugins

imagej/imagej-tutorials

The imagej-tutorials are an individual project. The files can live in a directory on its own outside the ImageJ or Fiji project. The pom.xml files of the imagej-tutorials pull in all the necessary dependencies for compiling via Maven.

"One file to bind them all": parent pom.xml files

As the projects get more complex, read about the Maven component structure of ImageJ/SciJava and something which is called "Bill of Materials" or just BOM. A "BOM" is a list of dependencies at particular versions which are believed to be mutually compatible. The complexity of ImageJ/SciJava's dependencies is a tribute to the different organizations which are contributing with their independent projects to ImageJ/SciJava. There are several "parent" pom.xml files which are independently maintained for example by the ImageJ, ImgLib2 or SCIFIO organizations. Each of these organizations has developed source code components which depend on components within the other two organizations. This complicated network of dependencies is managed with the help of the parent pom.xml files, i.e. pom-imagej, pom-fiji, pom-imglib2 etc. (see a list of all on the ImageJ Architecture page).

Initially I could not figure out where to put one of these pom-xxx files to use it as parent POM. I erroneously thought it should be downloaded from GitHub and copied somewhere in my ImageJ projects folders. However, one does not have to take care of the parent POM file at all! You just have to refer to it in the local pom.xml file of your intended plugin project in the section <parent>.

<parent>
  <groupId>net.imagej</groupId>
  <artifactId>pom-imagej</artifactId>
  <version>15.1.0</version>
  <relativePath />
</parent>

If pom-imagej is the parent POM file, then the local pom.xml could override the following configuration sections:

<name>
<description>
<url>
<inceptionYear>
<organization>
<licenses>
<developers>
<contributors>
<scm>
<issueManagement>
<ciManagement>

In the local pom.xml at least the sections:

<groupId>
<artifactId>
<version>

should be changed. Optionally also:

<name>
<description>
<url>

Further readings

Other References

and all other links cited in the text!

Appendix

How do I find dependencies?

You can search by class for Maven artifacts. For example, search for ij.plugin.PlugIn. There is also a "Find Jar For Class" helper script in Fiji which does a similar thing for JAR files currently on ImageJ's classpath.

If you are comfortable with command-line tools, you can also use the Maven Dependency Plugin which enables you to do things like download local copies of the dependency jars for inspection.

Manage Java versions

On Linux several java version can be installed. Select the preferred version in a terminal window (e.g. bash):

update-alternatives --config java

Note: It might be necessary to use sudo.

If necessary, tell NetBeans to use JDK 1.8 as the default JRE for new projects (i. e. on Debian Linux: Project Properties ▶ Build ▶ Compile... /usr/lib/jvm/java-1.8.0-openjdk-amd64) or alternatively set the netbeans_jdkhome property in your NetBeans config file. It should be in the local NetBeans directory, for example ./netbeans-8.0/netbeans.conf.

Where can I find example plugins?

What is the directory structure for a plugin?

This text was adapted from the Maven page.

The directory structure of a very simple demo project looks like:

DemoPlugin
|-- pom.xml
|-- src
|   !-- main
|       |-- java
|       |   !-- MyPlugin.java
|       !-- resources
|           !-- lena.tif

After compiling your java files, Maven automatically generates the content of the target folder. Therefore: never commit any files from target to Git! You can tell Git to ignore these files by using a .gitignore file (usually you start by copying an existing one from another project)

!-- target
    |-- classes
    |   |-- lena.tif
    |   |-- META-INF
    |   |   !-- json
    |   |       !-- org.scijava.plugin.Plugin
    |   !-- MyPlugin.class
    |-- generated-sources
    |   !-- annotations
    |-- maven-status
    |   !-- maven-compiler-plugin
    |       !-- compile
    |           !-- default-compile
    |               |-- createdFiles.lst
    |               !-- inputFiles.lst
    !-- test-classes

In general:

Put your .java files under src/main/java/ and the other files you need to be included into src/main/resources/.

Should you want to apply the best practices called "regression tests" or even "test-driven development" put your tests' .java files to src/test/java/ and the non-.java files you might require go into src/test/resources/.

More information about Maven's standard directory layout can be found on the Maven website.

What does a minimal pom.xml look like?

This text was adapted from the Maven page.

This is a very simple example:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.mywebsite</groupId>
  <artifactId>my-uber-library</artifactId>
  <version>2.0.0-SNAPSHOT</version>
</project>

The first 6 lines are of course just a way to say "Hi, Maven? How are you today? This is what I would like you to do...".

The only relevant parts are the groupId, which by convention is something like the inverted domain name (similar to the Java package convention), the name of the artifact to build (it will be put into target/, under the name <artifactId>-<version>.jar). And of course the version.

While the example pom.xml above shows the general idea, for ImageJ more details have to be considered. Therefore it is better to start with an existing pom.xml file, for example the one from imagej/minimal-ij1-plugin. Copy it to your project and modify it as needed.

Are the underscores still needed for plugins to be visible in IJ or Fiji?

From ImageJ Forum Thread 1020:

Underscores are needed if your plugin is an ImageJ 1.x style plugin. I.e.: does it implement ij.plugin.PlugIn or ij.plugin.filter.PlugInFilter? Then put an underscore in your JAR file and/or in your class name.

If you write an ImageJ2 command plugin (i.e.: implement the org.scijava.command.Command interface, with an @Plugin annotation) then the underscore is no longer necessary.

Make the plugins appear in the menus

Example, which sets the plugins.dir property so that the plugin appears in the menus when launched from an IDE:

Setting plugins.dir is only necessary for ImageJ 1.x style plugins. If you write an ImageJ2 command, it should appear in the menus regardless.

NetBeans: what is the difference between Ant and Maven? Or: build.xml vs. pom.xml

From StackOverflow #15121928:

Ant is a build tool primarily, this means it knows how to compile and package source code and run tests, but has no ability to manage project dependencies. Ant uses build.xml files to define where to find the source code and which steps to take to build your project.

Maven is more than just a build tool, it is a project management tool. It allows you to define dependencies in the pom.xml proect definition, as well build, test and distribute the application. It also allows sub projects, parent projects and there exist many plugins for many other features. Maven will automatically download the dependencies and manages these dependencies between projects.

Maven is declarative, whereas Ant is procedural. In Ant, you say "do X, then do Y, then do Z." Whereas in Maven, you say "my code is here, my resources are there, and please use these plugins." One advantage of the latter is that Maven provides a standardized build sequence (called the "build lifecycle") making it compatible with all the major IDEs.

How to migrate an existing Netbeans project to a Maven project?

Adapted from StackOverflow #7548008:

  • Backup your project.
  • Create a new project with name NewMavenProject.
  • Close your original project.
  • Copy the pom.xml from imagej/minimal-ij1-plugin or other appropriate template.
  • Modify the pom.xml's project specific settings (e.g. project name, dependencies).
  • Delete the build.xml and the whole nbproject folder.
  • Move and rename the folder to src/main/newproject (newproject is the new name).
  • Move src/java to src/main/java.
  • Open your project again in NetBeans. It should be a Maven project now.
  • Delete the unnecessary NewMavenProject project.

Enable the ImageJ 1.x UI, instead of the ImageJ2 Swing UI

From ImageJ Forum Thread 1364:

Add the following dependency to your POM:

<dependency>
  <groupId>net.imagej</groupId>
  <artifactId>imagej-legacy</artifactId>
  <scope>runtime</scope>
</dependency>

That will enable the ImageJ 1.x UI, instead of the ImageJ2 Swing UI which is otherwise the default.

What is it all about with this Java 6 and Java 8 stuff?

From ImageJ Forum Thread 1151:

The current situation with respect to Java 6 vs. Java 8, as well as the ramifications there regarding Java 3D, is basically:

  • If you download "vanilla" ImageJ2 (author's note: in the context of software "vanilla" means software used as originally distributed without any customizations or updates applied to them) from the Downloads page, you get a "Java 8" version from February 2016.
  • If you download the latest Fiji you get the newest "Java 8" version—i.e., with Java-8 update site. This includes the Java 3D 1.6 (SciJava fork) along with all Fiji plugins (except for TrakEM2) updated to work with it.
  • If you download a Life-Line version of Fiji and fully update it, you'll have the newest (probably the final) "Java 6" version including the latest Java-6-compatible plugin versions. No Java 3D until you run the 3D Viewer for the first time and it gets auto-installed. Those plugin versions are frozen: the ImageJ/Fiji developers are in the process of migrating everything to Java 8, and are only uploading new versions of everything to the Java-8 update site now, to avoid breaking the stable Java-6 versions of everything.

Ultimately, the ImageJ/Fiji developers will push all the Java-8 stuff back to the core ImageJ and Fiji sites. But not until the ImageJ/Fiji developers add a launch check that verifies your version of Java is new enough—and if not, tells you how to upgrade it. Ihe ImageJ/Fiji developers will definitely archive the final Java-6-compatible versions of ImageJ and Fiji when they complete that transition.

Note: You can check the Java version as described here.

More information can be read here: 2015-12-22 - The road to Java 8

Make a redistributable package from a locally customized Fiji

Turn your local customized Fiji into a redistributable package that can then be installed on other machines e.g. in your lab: use the Make Fiji Package command.