This is an archive of the old MediaWiki-based ImageJ wiki. The current website can be found at imagej.net.

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.

Also you may want to have a look at Introduction_into_Macro_Programming as an easy way to automate repetitive tasks with existing tools and plugins.

Finding information

The search engines will point you to both https://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:

Environments used for testing this guide:

  • Debian Jessie | macOS 10.14.x
  • OpenJDK 1.8.x
  • NetBeans IDE version 8.0.2 | IntelliJ IDEA 2019.2.2 (Community Edition)
  • Git 2.x
  • Apache Maven 3.x

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

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

Both Git and Maven have build in support in current versions of the IDEs mentioned above.

Earlier versions of the ImageJ wiki mentioned another software tool, called Jenkins, which is according to Wikipedia "an open source continuous integration tool written in Java". This tool was later replaced by a similar working service called Travis CI. Tools like Maven and Travis CI make it more complicated for casual developers to understand the workflow of ImageJ Plugin development 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 "Travis CI" it is enough to know that "Continuous Integration" means merging all developer working copies to a shared mainline several times a day. Here the work of multiple developers is compiled and tested on a single machine to ensure that the combined code produces a working project at any time. For the development of a single plugin for our personal use we can ignore "Travis CI" for the moment. It will be important if you want to share your plugin in the ImageJ updater or even contribute to the ImageJ project.

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.

To make Maven work we need so called pom.xml files. These configuration file contains information about the project and various configuration details used by Maven to build the project(s). 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.


Build ImageJ with NetBeans, Git and 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.


Build the ImageJ Tutorial Plugin with IntelliJ IDEA, Git and Maven

Setup IntelliJ

  • Make sure you have a version of Java SDK 1.8.x installed
  • Make sure to activate Maven and Git Plugins when installing IntelliJ
  • However these plugins can be activated in the main settings at any time

Import and build the project:

  • Run IntelliJ IDEA
  • Choose File › New › Project from Version Control › Git from the main menu

ImageJ 1.x Plugin

ImageJ2 Plugin

Import

  • Click Clone and all sources will be downloaded and opened as a new Project
  • If IntelliJ asks to import all Maven changes you have to allow this
  • Expand the Maven window which can be found on one of the edges in the IntelliJ window and select your project
  • Here you can right-click and "Run Maven Build" or alternatively press the green arrow above it in the Maven window to build your project
  • The build process will generate two .jar file under [project_name]/targets/ that can be installed in your local ImageJ installation

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/tutorials

The imagej/tutorials are structured as individual projects. 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>

Finally add at least one of the following dependencies for ImageJ plugin support:

<dependencies>
	// support for ImageJ2 plugins
	<dependency>
	    <groupId>net.imagej</groupId>
	    <artifactId>imagej</artifactId>
	</dependency>
	// support for ImageJ 1.x plugins
	<dependency>
	    <groupId>net.imagej</groupId>
	    <artifactId>ij</artifactId>
	</dependency>
</dependencies>

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.

Also mvnrepository.com is a good resource to find repositories with code you can easily copy and paste in your pom.xml.

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 project to a Maven project?

In Netbeans

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.


In IntelliJ IDEA

  • Create a new Maven Project with File › New Project
  • Select Maven on the left and click Next
  • Choose your custom GroupID (eg. com.yourwebsite) and an ArtifactID as single identifier for this project (eg. project_name)
  • Note that for ImageJ 1.x Plugins a "_" in the project name/ identifier is required for ImageJ 1.x Plugins
  • The project structure required by Maven will be created for you
  • For Git support (recommended): VCS › Import into Version Control › Git
  • Copy all .java files into [project_name]/src/main/java
  • Copy your plugins.config file into [project_name]/src/main/resources
  • In the main project directory [project_name]/ you can find a pom.xml which has to be edited like the example shown in the previous chapter
  • If your IDE asks to import all Maven changes you have to allow this
  • Expand the Maven window which can be found on one of the edges in the IntelliJ window and select your project
  • Here you can right-click and Run Maven Build or alternatively press the green arrow above it in the Maven window to build your project
  • The build process will generate two .jar file under [project_name]/targets/

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.

Tests wit JUnit5

In IntelliJ IDEA you may want to make sure that the JUnit5 Plugin is activated. The next step would be to append the following lines to your pom.xml file:

<dependency> 
<groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.5.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-runner</artifactId>
    <version>1.5.1</version>
    <scope>test</scope>
</dependency>

<build>
    <plugins>
        <plugin>
            <!-- fix maven tests -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M3</version>
            <configuration>
                <excludes>
                    <exclude>some test to exclude here</exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

Note: if you want to test GUI tests with TravisCI you have to activate a virtual display as described in the Travis CI chapter.

Continuous Integration with Travis CI

If you want to share your plugin in the ImageJ updater automatically Automatic Update Site Uploads, contribute to the ImageJ project Fiji/Contribution requirements or work in a team with multiple developers, you may want to build, test and deploy your Plugin with Travis CI. If you are hosting your code in a public GitHub repository this service is free for you. After signing in with your GitHub account you can activate single repositories for Travis CI. Travis then automatically clones your repository with every change and runs a build according to the .travis.yml configuration file in your root directory.

	# specify compiler
	language: java
	sudo: false # faster builds
	jdk: openjdk8
	
	# maven build
	install: true
	script: mvn clean verify
	
	# cache maven dir for performance
	cache:
	  directories:
	    - $HOME/.m2

In case you are working with GUI tests, you may want to activate a virtual display as well:

	# virtual display variable for gui tests
		dist: xenial
		services:
		  - xvfb

JavaFX JAR not found

Add this to your pom.xml:

    <build>
        <plugins>
            <!-- Fix JavaFX support -->
            <plugin>
                <groupId>com.zenjava</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>8.8.3</version>
                <configuration>
                    <mainClass>your.package.with.Launcher</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

Log4j warning in IntelliJ IDEA

Some of the tutorials seem to be missing a configuration file for Log4. IntelliJ will warn you about this as soon as you try to try to build the project:

log4j:WARN No appenders could be found for logger (org.bushe.swing.event.EventService).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

The missing config file is called log4j.xml and has to be located in .../src/main/resources/:

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>

    <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="demoApplication.log"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
        </layout>
    </appender>

    <root>
        <priority value ="debug"></priority>
        <appender-ref ref="fileAppender"></appender-ref>
    </root>

</log4j:configuration>