ImageJ Jupyter notebooks updated; ImageJ + Python; retiring the SciJava Jupyter Kernel

cellprofiler
imagej
python
tutorials
jupyter
scikit-image

#1

The ImageJ tutorial notebooks have now fully switched over to the stock BeakerX Groovy kernel. Previously the notebooks used a custom BeakerX-based kernel called the SciJava Jupyter Kernel (SJJK).

Newly added is an initial “ImageJ with Python Kernel” notebook illustrating how to use ImageJ from Python, so that it can be fully combined with other tools available from the Python software ecosystem, including NumPy, SciPy, scikit-image, CellProfiler, OpenCV, ITK and more.

To facilitate this update, I released ImageJ 2.0.0-rc-71 (and Fiji 2.0.0-pre-10) that includes support for images-inside-tables in the vanilla BeakerX Groovy kernel. This is the same code we wrote for the SJJK, but now migrated to imagej-notebook.

The current plan is now to retire the SciJava Jupyter Kernel. If you use this kernel and would prefer it not be retired, please respond here letting us know why not. In my view, the only thing SJJK can do that the Groovy kernel cannot is switch languages per cell, but I personally do not use this feature. It is still possible to mix languages in the Groovy (or other regular BeakerX) kernel by using the script service to run scripts in any supported SciJava script language. Do you know of something else that the SJJK can do that is still missing from plain Groovy notebooks? If so, please speak up, and we can discuss what to do about it.

One huge advantage of plain Groovy notebooks is the %classpath magic syntax to load whatever artifacts you want upfront, including a specific version of ImageJ itself, so that notebooks are fully reproducible and extensible. SJJK always suffered from the fact that it shipped a particular version of ImageJ, which quickly became outdated, and it was not possible to override this version in the notebook.

One downside of plain Groovy notebooks is that BeakerX always wants to handle rendering of List and Map with its TableDisplay logic. Unfortunately, SciJava Table extends List (I regret this design decision, and am considering changing it, although it would be disruptive to do so), and BeakerX’s built-in table rendering does not support invoking its Displayer extensions inside table cells. So we cannot register a Displayer to show the SciJava Table objects directly as cell outputs. That is why the updated ImageJ tutorial notebooks use ij.notebook().display([[...]]) around the [[...]] list-of-maps/table expressions.

The good news is: I have added an extensibility mechanism to imagej-notebook, such that if a Converter plugin exists that can go from some type Foo to a net.imagej.notebook.mime.MIMEObject (e.g., an HTMLObject), then Foo is automatically registered via BeakerX’s Displayer mechanism. So whenever a cell outputs a Foo, it will magically render as HTML by calling the appropriate converter.

Happy to elaborate on any of these details with more clarity if anyone is interested; just ask.


Additional jars for scijava jupyter kernel
#2

If you think it would be better to not extend List here, let’s change it rather sooner than later, before more projects are picking up the API… (since we anyway only recently changed the package of the table classes from net.imagej.table to org.scijava.table).


#3

I agree that changing it sooner would be better. However, I have some urgent deadlines right now that mean I cannot work on it this week. Also, I’d like to discuss here a bit before we commit to changing the API. Here is a brainstorm of reasons for and against changing it. Thoughts and opinions welcome.

Reasons to make Table no longer extend List

  • The List interface brings in a large API, all of which should be tested. Implementing Table directly is somewhat involved—although less now since I maximized the use of default methods in the interface and introduced the Tables utility class as a helper.
  • Tables are 2D structures; implementing List<Column> means we are imposing a column-centric paradigm, which is awkward/inappropriate for some backing implementations, particularly row-centric ones. We could instead have table.columns() and table.rows() that offer List<Column> and List<Row> objects respectively. (And Column and Row would probably both extend the same base interface, since they are the same concept transposed.)
  • The List interface limits us to 2^{31}-1 elements. Maybe we want to enable tables to get bigger than that? We’d have to change to long indices everywhere, though.
  • The List interface imposes boundedness and support for random access. Probably we want all Table implementations to be bounded and randomly accessible, though, so I mention these here mainly for completeness.

Reasons not to change it

  • Some existing code in the wild would break.
  • The current API is tested and working fine as-is. Costs developer time to reengineer it.
  • Images-inside-lists-of-maps style tables would still not render as desired out of the box with BeakerX; need to explicitly wrap the list-of-maps to a SciJava Table first.
  • For things larger than 2^{31}-1 elements, we already have ImgLib2.

#4

Thanks @ctrueden, this is looking great! I didn’t realize how extensive the plotting options were in the BeakerX Groovy kernel.

Plotting has been one of the major missing pieces for us in our workflows. We usually save final results into tables and then plot elsewhere, but now we could do everything in Groovy jupyter notebooks. I also noticed columns of scijava tables can be directly given as x and y inputs in plots and it just works, not sure if changing from List would complicate this.

I would think having tables with more than 2^31 elements is a rare use case and there are better data structures for that (ImgLib2 etc…). We have worked a lot with large tables (5 GBs etc) in the past and recently switched to a different data model that makes multithreading easier. But I understand it’s easier to change sooner rather than later. We use the scijava tables a lot in our code, but it sounds like the change wouldn’t be that disruptive. I guess it just depends on how you will implement it.

I had a look at the “ImageJ with Python Kernel” but I didn’t see any examples with Tables there. I am curious, is there a way to quickly convert from a scijava table to a pandas dataframe?

Also, if you initialize using your local Fiji instance can you use locally installed plugins in Python as well? Do you have any suggestions when setting this up?