How we've made our first Fiji plugin
As most people who do any image analysis (which, let's be honest, should be anyone who does any microscopy), at CAMDU we are avid Fiji users. A major reason to use Fiji is the amount of work that has already been done by the community to extend its functionalities by writing plugins. For someone who does image analysis for a living (as myself), the size of your plugins menu is the kind of thing you're proud of and ashamed of at the same time.
Plugins are written in Java (as the rest of Fiji/ImageJ). They allow you to do things you wouldn't be able to do on a simple ImageJ macro, since you, as a programmer, have access to the full power of a general-purpose programming language rather than the subset of routines that can run inside Fiji. Of course, the downside is that the complexity added to your code is significant. More power, more responsibility, etc.
The problem there is that Java has scared me for about 10 years. Ever since I was an undergraduate working on a research project that used Java, I have dreaded the day I'd need to use it again. It was a mainly irrational fear, I'll admit, but it was real and I'm sure I'm not the only person intimidated by Java. In this post, I'll talk about how we've finally gone the extra mile and moved from macro-ing to plugin-ing!
Claire spends a lot of time doing quality control on our microscopes. Our multi-user microscopes have very nice plots for some quality control parameters. Her workflow consists in taking images of beads and fluorescent slides, and then using MetroloJ and TrackMate for calculating actual QC parameters. In addition to taking images and using the plugins, however, there was a whole lot of manual work: cropping beads, copying results to spreadsheets and so on and so forth. Also, the plugins generated a whole lot of results and information that we did not need: we were interested in a few numbers, and we were getting multiple spreadsheets, images and PDFs that we had no use for.
So one day we sat down, she explained to me every step of what she was doing in Fiji and I wrote a set of macros that would do those things for her. This set of Jython scripts saved her a lot of work! They were also super specific to our workflow (images need to have specific strings on their filenames, for example) and were still generating a whole lot of information and files that we did not need.
Facing scary Java
That would probably have been the end of it: we had something that worked well enough for us, and the downsides were very manageable. I'm very comfortable with Python, so updating, fixing and maintaining that code was not a problem. Then, NEUBIAS TS11 came around (sidenote: I cannot stress enough how great this was; great lectures, great people attending). One of the lectures was by Robert Haase introducing us to imglib2. In Java! My nemesis!
It went surprisingly well and it was definitely less scary than I was expecting. We used IntelliJ Idea as an IDE and it did a lot of the heavy lifting for us. By the end of the hands-on session, we had a very simple plugin template that ran in Fiji, which was incredibly exciting.
When in doubt, press Alt+Enter
So when the time came to deconstruct a workflow in small groups, I presented to Cesar. Christopher, Jan and Paul an idea: what if we made one of my Jython scripts into a proper Fiji plugin? I'm very lucky that they were on board with that idea, so we went ahead and spent many (7-ish?) hours banging our heads against it. With the help from Robert and Jean-Yves Tinevez, we managed to have a functional prototype by the end of the training school!
That was probably the end of it for the rest of Group 4, but for me it was only the beginning. There were lots of things I wanted to do better (coding on a short deadline makes for pretty ugly workarounds and shortcuts...), and I wanted to have all my scripts running as plugins. Instead of the 200 different outputs from MetroloJ and TrackMate, I wanted all the outputs from each individual routine to be a simple CSV file. I wanted all of them to be stable, robust and ready for the whole world to use.
So I spent a few weeks on it, and it now sits on a Github repo. It works! It wasn't simple, but it wasn't overwhelming.
A few quick things
It can be very confusing at first. An Img is a RandomAccessibleInterval, but it's also an IterableInterval, and sometimes you need to be an ImgPlus or even an ImagePlus because of someone else's code you're using. Converting to/from these objects is not always straightforward. Running a maximum projection means you need a UnaryComputerOp that will be passed to ij.op().transform().project(). The documentation level can be less-than-optimal sometimes.
However, things start making sense when you internalise the design ideas behind it. They go a bit against my personal intuition for how to do things the simple way, and imglib2 certainly doesn't fail gracefully all the time, but it's not an impossible challenge. What probably helped me the most was looking through other people's plugins and seeing how they did things. There's plenty of imglib2-based plugins our there. Grab some source code and get into it!
Fiji plugins have the super useful annotation @Parameter for variables that will be user-defined parameters. Simply by using this annotation (with a "label" parameter, such as @Parameter(label = "number of beads:")), you can make any class variable into an input. Fiji will automatically generate a dialog window when you run the plugin, with the appropriate input fields given the type of your variable. Defining a variable as File was of particular interest to me, and luckily enough Fiji does the right thing and allows the user to input a list of files!
Two words of warning: First, if you want to run your project directly from Idea to test things, this approach is not enough. Idea has no, well, idea how to do Fiji things. I eventually settled on having a helper method CreateUI() that I could call in case I wanted to run the project inside Idea: this will generate a window dialog with the fields I need and allow me to input parameters that I can set in my class. When I want to build it to run in Fiji, I can just comment the call to this method out to avoid generating two dialog windows.
Second, avoid any IJ.run() calls at all costs. IJ.run() calls cause your script to return a command early and without the input parameters, which is not a problem for running the plugin itself, but it makes anything you do impossible to record in a macro. If you want your plugin macro-recordable, you will need to get rid of all of those calls.
I'm sure everyone that uses Fiji knows the concept of update sites: these are a web space provided by ImageJ for people to share their plugins, macros and so on. As a user, the list of update sites was like gospel to me: the idea that anyone can have an update site sounded preposterous.
I couldn't be more wrong! Setting up a CAMDU update site took a few minutes, and adding it to the list of "official" Fiji update sites was also a few minutes' work. Now every Fiji user in the world can use our plugin by going to the Fiji updater and checking the box next to "CAMDU", and that's it!
DOI and Zenodo
As soon as I started working on this, one of the very first things I did was generating a DOI for my repository. Tools like Zenodo make the whole process painless. There's no reason not to. If anyone wants to cite your software in the absence of a publication, it makes their life much easier.
This was the first time ever I tried out Travis. The setup is very easy if your code is on Github, and it will generate a build of your code every time you push a commit. In the case of this repository it's not particularly useful, mostly due to the shameful lack of tests, but it can do the whole thing for you and then you have a live badge on your repository that indicates whether your build is passing or failing. I'll definitely use this again in the future.
So there you go. I hope this sheds some light on how the process for writing a plugin works, and encourages more people to try their hand at it! The more people writing and sharing code, the stronger the whole community is.
We have been using the autoQC for a few weeks now, and it's been (mostly) a successful journey! Claire has found some bugs and problems, and we've been addressing those as they come.
Finally, we decided to automate the last part of the process: plotting the data results. For that, we have been using some custom Python code based on the Plotly library for Python. That generates really nice interactive plots that can be added to webpages easily. See example on one of our microscope pages.
We have also added some code to plot power measurements for different wavelengths. Our whole QC workflow is now as automated as we can make it now, and the results go straight to the CAMDU page!