{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Fundamentals of ImageJ\n", "This tutorial presents the basic concepts and usage of the ImageJ API." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The ImageJ gateway" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first step when working with ImageJ is to get or create an *ImageJ gateway*. This gateway provides access to ImageJ operations and data structures." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Added new repo: scijava.public\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "832329ae-8c8a-400d-b000-9c1c58f26c36", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "ImageJ v2.0.0-rc-71 is ready to go." ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// Behind a firewall? Configure your proxy settings here.\n", "//System.setProperty(\"http.proxyHost\",\"myproxy.domain\")\n", "//System.setProperty(\"http.proxyPort\",\"8080\")\n", "//System.setProperty(\"https.proxyHost\",\"myproxy.domain\")\n", "//System.setProperty(\"https.proxyPort\",\"8080\")\n", "\n", "// Load the ImageJ library from the remote Maven repository.\n", "%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public\n", "%classpath add mvn net.imagej imagej 2.0.0-rc-71\n", "\n", "// OR: Load ImageJ with Fiji plugins from the remote Maven repository.\n", "//%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public\n", "//%classpath add mvn sc.fiji fiji 2.0.0-pre-10\n", "\n", "// OR: Load ImageJ with Fiji plugins from a local Fiji installation.\n", "//%classpath add jar '/Applications/Fiji.app/jars/*'\n", "//%classpath add jar '/Applications/Fiji.app/jars/bio-formats/*'\n", "//%classpath add jar '/Applications/Fiji.app/plugins/*'\n", "\n", "// Create an ImageJ2 gateway:\n", "// Java Syntax:\n", "// ImageJ ij = new net.imagej.ImageJ();\n", "// or\n", "// import net.imagej.Imagej;\n", "// ImageJ ij = new ImageJ();\n", "ij = new net.imagej.ImageJ()\n", "\"ImageJ v${ij.getVersion()} is ready to go.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Services" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ImageJ's functionality is divided into *services*. Each service provides some API methods for performing related tasks.\n", "\n", "The gateway provides easy access to this slew of services. Here are some example service calls:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "There are 1562 plugins available.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[WARNING] Ignoring negative sigma value.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "There are 413 menu items total.\n" ] }, { "data": { "text/plain": [ "null" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// The plugin service manages the available plugins (see \"Plugins\" below).\n", "pluginCount = ij.plugin().getIndex().size()\n", "println(\"There are \" + pluginCount + \" plugins available.\")\n", "\n", "// The log service is used for logging messages.\n", "ij.log().warn(\"Ignoring negative sigma value.\")\n", "\n", "// The status service is used to report the current status of operations.\n", "// Within a notebook like this, the call does not do anything visible.\n", "ij.status().showStatus(\"Processing data file 34 of 97...\")\n", "\n", "// The menu service organizes a menu hierarchy for modules (see \"Modules\" below).\n", "menuItemCount = ij.menu().getMenu().size()\n", "println(\"There are \" + menuItemCount + \" menu items total.\")\n", "\n", "// The platform service handles platform-specific functionality.\n", "// E.g., it can open a URL in the default web browser for your system:\n", "// ij.platform().open(new URL(\"https://imagej.net/\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Comparison with ImageJ 1.x\n", "
\n", "\n", "[ImageJ 1.x](https://imagej.net/ImageJ1) has a similar concept with the `ij.ImageJ` class, which is created using `new ImageJ()` and cached statically as a singleton. This allows the ImageJ instance to be recovered later by calling `IJ.getInstance()`, and simplifies the API in some ways. However, the assumption that there will only ever be one ImageJ per JVM limits its flexibility, and the fact that `ij.ImageJ` extends `java.awt.Frame` makes ImageJ 1.x difficult to use [headless](https://imagej.net/Headless) or with user interfaces other than Java AWT. Furthermore, [ImageJ 1.x](https://imagej.net/ImageJ1) is not service-driven, which makes it less extensible; see the SciJava in Detail tutorial notebook for more information." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How to explore the API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One very easy way to explore the API is via an [Integrated Development Environment (IDE)](https://imagej.net/IDEs) such as [Eclipse](https://imagej.net/Eclipse) or [IntelliJ IDEA](https://imagej.net/IDEA). You can use the IDE's autocomplete feature to list all of the available methods of a particular object. E.g., in Eclipse, if you press ctrl+space after a period (`.`) character on an object, you will see a list of methods which are available:\n", "\n", "

\n", "\n", "Unfortunately, here in this notebook software, the autocompletion logic is not (yet!) advanced enough to fully offer such a feature—there is only rudimentary method completion support by pressing tab. Hence, the `NotebookService` also includes a handy method for inspecting an object's methods. Here is an example which will show the available methods of a List:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a2213a37-b877-420b-bc5c-ab466813e81b", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// In Groovy, [] creates an ArrayList in this situation\n", "myList = [\"quick\", \"brown\", \"fox\"]\n", "ij.notebook().methods(myList)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Try it yourself on the ImageJ service of your choice!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Complete list of built-in services" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is the complete list of built-in services accessible from the ImageJ gateway:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c108d773-2a4a-415c-a801-3e30073940d5", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ij.notebook().methods(ij).findAll{ it.get(\"returns\").endsWith(\"Service\") }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plugins" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ImageJ is built on the [SciJava plugin framework](http://imagej.net/SciJava_Common). Essentially everything in ImageJ is a [plugin](http://imagej.net/Plugins).\n", "\n", "There are many kinds of plugins, and you can also define your own new kinds. Some of the most central built-in plugin types are:\n", "\n", "* `Service` – A collection of related functionality. See \"Services\" above.\n", "* `Command` – A routine which can be executed. See \"Modules\" below.\n", "* `IOPlugin` – A plugin for reading and/or writing data to/from external locations. See \"Data I/O\" below.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Complete list of built-in plugin types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `PluginService` provides access to meta-information about available plugins. Here is a count of plugins organized by kind:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "scrolled": false }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "36527bef-4b93-4032-a185-1d420e909052", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Gather a count of each kind of plugin.\n", "kindCounts = [:]\n", "ij.plugin().getPlugins().forEach{plugin ->\n", " kind = plugin.getPluginType().getName()\n", " // HACK: Report all Op plugin subtypes as simply \"Op\", to avoid overwhelming the list.\n", " if (kind.startsWith('net.imagej.ops.Ops') || kind.startsWith('net.imagej.ops.features'))\n", " kind = 'net.imagej.ops.Op'\n", " kindCounts.put(kind, kindCounts.getOrDefault(kind, 0) + 1)\n", "}\n", "// Build a table which reports this information nicely.\n", "kindCounts.keySet().sort().stream().map{kind -> [\n", " \"package\": (kind =~ /^.*\\./)[0][0..-2],\n", " \"class\": (kind =~ /\\.[^\\.]*$/)[0][1..-1],\n", " \"count\": kindCounts.get(kind)\n", "]}.collect()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data I/O" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The I/O service provides a generalized API for reading and writing from external sources." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is an example invocation which opens an image from a remote URL:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[INFO] Verifying GIF format\n", "[INFO] Reading dimensions\n", "[INFO] Reading data blocks\n" ] }, { "data": { "text/html": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "snowflake = ij.io().open(\"https://imagej.net/images/snowflake.gif\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Saving data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, you can use the I/O service to save data to an external destination:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Saved to '/Users/curtis/Desktop/snowflake.png'; length = 2965" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "destPath = System.getProperty(\"user.home\") + \"/Desktop/snowflake.png\"\n", "ij.io().save(snowflake, destPath)\n", "\"Saved to '$destPath'; length = ${new File(destPath).length()}\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Bio-Formats\n", "\n", "Out of the box, ImageJ does not include the [Bio-Formats](https://imagej.net/Bio-Formats) plugin for reading and writing life sciences images.\n", "\n", "One easy way to enable it is to use [Fiji](https://fiji.sc/), which comes bundled with Bio-Formats and many other plugins useful in the life sciences. To initialize an ImageJ gateway that includes the Fiji plugins, replace the first cell of this notebook with the following code:\n", "\n", "```groovy\n", "%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public\n", "%classpath add mvn sc.fiji fiji 2.0.0-pre-9\n", "ij = new net.imagej.ImageJ()\n", "```\n", "\n", "The `ij.io().open(...)` command should then be capable of opening any Bio-Formats-supported format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modules" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A SciJava _module_ is an executable snippet of code with _typed inputs and outputs_. You can think of them as subroutines, also called _functions_ or _methods_ depending on the programming language.\n", "\n", "The two most common flavors of module are _commands_ and _scripts_. A `Command` is a plugin written in Java, whereas a script is written in one of the many available [SciJava scripting languages](https://imagej.net/Scripting). Most users who need to code a module will use a script, because they are simpler to write." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scripts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a sample module, written as a script:\n", "```python\n", "#@input String name\n", "#@output String greeting\n", "#@output int length\n", "greeting = \"Hello, \" + name + \"!\"\n", "length = name.length()\n", "```\n", "You can write scripts such as the above e.g. into ImageJ's [Script Editor](https://imagej.net/Script_Editor), and then run them directly. You can also run them in notebooks using `ij.script().run(...)`.\n", "\n", "ImageJ (or more precisely: the SciJava script framework) will harvest the input parameters—in this case, the `name` string—from the user in a way appropriate to the execution context. For example, when running from the ImageJ user interface, a [Swing](https://en.wikipedia.org/wiki/Swing_(Java)) dialog box will appear asking the user to type in a name. But when running from the command line, the input values can be passed as arguments to the command line invocation; see the [Scripting Headless](http://imagej.net/Scripting_Headless) page for details. The module outputs—in this case, `greeting` and `length`—will then be displayed to the user in a manner appropriate to the situation: when running graphically, one or more windows will typically pop up; when running from the CLI, output values will be logged to the standard output stream.\n", "\n", "The [Script Parameters](http://imagej.net/Script_Parameters) page provides more information about using these parameters in your scripts.\n", "\n", "Now let's run this script here in the notebook, and see what happens:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "993e2b07-fe10-4656-b016-634c8e9ed14f", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Write the script as a string constant, so it can be passed to the script service.\n", "script = \"\"\"\n", "#@input String name\n", "#@output String greeting\n", "#@output int length\n", "greeting = \"Hello, \" + name + \"!\"\n", "length = name.length()\n", "\"\"\"\n", "\n", "// Run the script, passing input key/value pairs using a map.\n", "inputs = [\"name\": System.getProperty(\"user.name\")]\n", "module = ij.script().run(\"greeting.groovy\", script, true, inputs).get();\n", "\n", "// Extract the module output values.\n", "[\"greeting\" : module.getOutput(\"greeting\"),\n", " \"length\" : module.getOutput(\"length\")]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Commands" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is the same example module, but written as a command:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class Hello" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// NB: While this is a Groovy cell, the class definition here is valid Java.\n", "\n", "import org.scijava.ItemIO;\n", "import org.scijava.command.Command;\n", "import org.scijava.plugin.Parameter;\n", "import org.scijava.plugin.Plugin;\n", "\n", "@Plugin(type = Command.class)\n", "public class Hello implements Command {\n", " \n", " @Parameter\n", " private String name;\n", " \n", " @Parameter(type = ItemIO.OUTPUT)\n", " private String greeting;\n", " \n", " @Parameter(type = ItemIO.OUTPUT)\n", " private int length;\n", " \n", " @Override\n", " public void run() {\n", " greeting = \"Hello, \" + name + \"!\";\n", " length = name.length();\n", " }\n", "}\n", "\n", "// Save a reference to the class, for use in the next cell.\n", "greetingCommand = Hello.class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, commands are more verbose. But there are advantages to using Java, such as performance, reusability, type safety, and powerful IDE features like autocompletion.\n", "\n", "Let's run this command using the command service:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "2cad48bf-9859-4bb6-89b9-0a1a23e1ea78", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Run the command, passing input key/value pairs using a map.\n", "inputs = [\"name\": \"John Jacob Jingleheimer Schmidt\"]\n", "module = ij.command().run(greetingCommand, true, inputs).get()\n", "\n", "// Extract the module output values.\n", "[\"greeting\" : module.getOutput(\"greeting\"),\n", " \"length\" : module.getOutput(\"length\")]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Complete list of built-in modules" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `ModuleService` provides access to meta-information about available modules. Here is a list of all modules built in to ImageJ:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8b1d57c0-01f7-4962-a283-cff21bf17324", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "modules = []\n", "ij.module().getModules().stream().map{module -> [\n", " \"id\": module.getIdentifier(),\n", " \"location\": module.getLocation().replaceAll('.*/(.*\\\\.jar)$', '$1'),\n", " \"version\": module.getVersion()\n", "]}.collect()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Further reading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information about how to write commands and scripts, see the \"Extending ImageJ\" tutorial notebooks." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The true meat of ImageJ is [ImageJ Ops](https://imagej.net/Ops), a library for reusable image processing. An `Op` plugin is a form of `Command`, and therefore a module.\n", "\n", "Please proceed to the [ImageJ Ops](2-ImageJ-Ops.ipynb) tutorial notebook for a primer with lots of examples!" ] } ], "metadata": { "kernelspec": { "display_name": "Groovy", "language": "groovy", "name": "groovy" }, "language_info": { "codemirror_mode": "groovy", "file_extension": ".groovy", "mimetype": "", "name": "Groovy", "nbconverter_exporter": "", "version": "2.5.6" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "165px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }