{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Extending ImageJ: Ops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook illustrates how to create new `Op` plugins, and run them with ImageJ's `OpService`.\n", "\n", "The plugins are in Groovy cells, but coded in Java style." ] }, { "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": "e41688aa-04b8-41c7-b347-57d02a52c9bf", "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": [ "%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", "ij = new net.imagej.ImageJ()\n", "\"ImageJ v${ij.getVersion()} is ready to go.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `narf` - a very simple op" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a \"Hello, world\" style op, to illustrate the basics:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "name='narf', priority=0.0, enabled=true, pluginType=Op" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import net.imagej.ops.AbstractOp\n", "import net.imagej.ops.Op\n", "import org.scijava.ItemIO\n", "import org.scijava.plugin.Parameter\n", "import org.scijava.plugin.Plugin\n", "import org.scijava.plugin.PluginInfo\n", "\n", "// The @Plugin annotation allows this Op to be discovered by the OpService.\n", "// We declare the type of op, the name of the op, and any optional aliases...\n", "@Plugin(type = Op.class, name = \"narf\")\n", "public class Narf extends AbstractOp {\n", " \n", " // INPUTS, declared using @Parameter notation\n", " @Parameter\n", " private String input\n", " \n", " // OUTPUTS, declared using @Parameter notation\n", " @Parameter(type = ItemIO.OUTPUT)\n", " private String output\n", " \n", " @Override\n", " public void run() {\n", " // The job of the run method is to populate any outputs using the inputs\n", " output = \"Egads! \" + input.toUpperCase()\n", " }\n", "}\n", "\n", "// The @Plugin annotation is processed by the javac compiler,\n", "// which is used to generate the metadata in class bytecode.\n", "// Unfortunately, the Groovy compiler doesn't invoke the javac\n", "// compiler, so we need to register the plugin manually!\n", "narfInfo = new PluginInfo(Narf.class, Op.class)\n", "ij.plugin().addPlugin(narfInfo)\n", "narfInfo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now you can start using your new Op." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Egads! PUT SOME TROUSERS ON" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// Execute our Op and get the result\n", "result = ij.op().run(\"narf\", \"Put some trousers on\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `ramp` - an op which produces an image" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example illustrates how to create a new Op that constructs a ramp image." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "name='ramp', priority=0.0, enabled=true, pluginType=Op" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import net.imagej.ops.AbstractOp\n", "import net.imagej.ops.Op\n", "import org.scijava.ItemIO\n", "import org.scijava.plugin.Parameter\n", "import org.scijava.plugin.Plugin\n", "import org.scijava.plugin.PluginInfo\n", "import net.imglib2.Cursor;\n", "import net.imglib2.img.array.ArrayImg\n", "import net.imglib2.img.array.ArrayImgs\n", "import net.imglib2.img.basictypeaccess.array.DoubleArray\n", "import net.imglib2.type.numeric.RealType\n", "import net.imglib2.type.numeric.real.DoubleType\n", "\n", "// The @Plugin annotation allows this Op to be discovered by the OpService.\n", "// We declare the type of op, the name of the op, and any optional aliases...\n", "@Plugin(type = Op.class, name = \"ramp\")\n", "public class Ramp> extends AbstractOp {\n", "\n", " // INPUTS, declared using @Parameter annotation\n", " @Parameter(required = false)\n", " private int size = 256\n", "\n", " // OUTPUTS, declared using @Parameter notation\n", " @Parameter(type = ItemIO.OUTPUT)\n", " private ArrayImg rampImg\n", "\n", " @Override\n", " public void run() {\n", " rampImg = ArrayImgs.doubles(size, size)\n", "\n", " Cursor c = rampImg.localizingCursor()\n", " long[] pos = new long[rampImg.numDimensions()]\n", " \n", " // Iterate the image and get the each pixel location\n", " // Every pixel value is assigned its locations sum,\n", " // so generate the ramp pattern image.\n", " while (c.hasNext()) {\n", " c.fwd()\n", " c.localize(pos)\n", " c.get().setReal(sum(pos))\n", " }\n", " }\n", " \n", " // a sum method to be called in our Op\n", " private float sum(long[] pos) {\n", " float sum = 0\n", " for (long p : pos) {\n", " sum += p\n", " }\n", " return sum\n", " }\n", "}\n", "\n", "// The @Plugin annotation is processed by the javac compiler,\n", "// which is used to generate the metadata in class bytecode.\n", "// Unfortunately, the Groovy compiler doesn't invoke the javac\n", "// compiler, so we need to register the plugin manually!\n", "rampInfo = new PluginInfo(Ramp.class, Op.class)\n", "ij.plugin().addPlugin(rampInfo)\n", "rampInfo" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ramp = ij.op().run(\"ramp\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the resulting image is 256x256, since we did not specify the `size` parameter, and it is annotated with `required = false`. We can optionally pass the size to get a differently sized gradient:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "smallRamp = ij.op().run(\"ramp\", 120)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `blobs` - a more complex op" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This next example illustrates how to create a new op that constructs a 'random blobs' image." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "name='blobs', priority=0.0, enabled=true, pluginType=Op" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import java.util.Random\n", "import net.imagej.ops.AbstractOp\n", "import net.imagej.ops.Op\n", "import net.imglib2.RandomAccess\n", "import net.imglib2.RandomAccessibleInterval\n", "import net.imglib2.img.array.ArrayImgs\n", "import net.imglib2.type.numeric.RealType\n", "import net.imglib2.util.IntervalIndexer\n", "import net.imglib2.util.Intervals\n", "import org.scijava.ItemIO\n", "import org.scijava.log.LogService\n", "import org.scijava.plugin.Parameter\n", "import org.scijava.plugin.Plugin\n", "import org.scijava.plugin.PluginInfo\n", "\n", "// The @Plugin annotation allows this Op to be discovered by the OpService.\n", "// We declare the type of op, the name of the op, and any optional aliases...\n", "@Plugin(type = Op.class, name = \"blobs\")\n", "public class RandomBlobs> extends AbstractOp {\n", "\n", " // OUTPUTS, declared using @Parameter notation\n", " @Parameter(type = ItemIO.OUTPUT)\n", " private RandomAccessibleInterval image\n", " \n", " @Parameter\n", " private LogService log\n", " \n", " @Parameter\n", " private int blobNum\n", " \n", " @Parameter\n", " private int blobSize\n", " \n", " @Parameter\n", " private int xDim\n", " \n", " @Parameter\n", " private int yDim\n", " \n", " @Parameter(required = false)\n", " private long seed = 0xcafebabe\n", " \n", " @Override\n", " public void run() {\n", " // produce a XxY float64 array-backed image using the input parameters\n", " image = ArrayImgs.doubles(xDim, yDim)\n", " long[] pos = new long[image.numDimensions()]\n", " \n", " long[] blobCenter = new long[image.numDimensions()]\n", " long[] dims = new long[image.numDimensions()]\n", " image.dimensions(dims)\n", " \n", " // get the total number elements of the image\n", " long total = Intervals.numElements(image)\n", " \n", " Random r = new Random(seed)\n", " \n", " RandomAccess ra = image.randomAccess(image)\n", " \n", " // Iterate to generate each blob\n", " for (int i = 0; i < blobNum; i++) {\n", " // generate a random positon in [0, total)\n", " long index = (long) (r.nextDouble() * total)\n", " // convert the linear index to the 2-D index\n", " // For example, index = 59662, dims = [256,256],\n", " // then blobCenter = [14,233]\n", " IntervalIndexer.indexToPosition(index, dims, blobCenter)\n", " \n", " // For generating current blob, it is necessary to scan\n", " // the whole image to determine the elements which are\n", " // locate in the radius of the blobCenter.\n", " for (int j = 0; j < total; j++) {\n", " IntervalIndexer.indexToPosition(j, dims, pos)\n", " double dist = distance(pos, blobCenter)\n", " if (dist > blobSize) {\n", " continue\n", " }\n", " \n", " // This element is in the radius of the blobCenter, so it is \n", " // assigned with value inversely proportional to the distance.\n", " // Namely, if the distance is 0.0, then the norm is 1.0; if the\n", " // distance is blobSize, then the norm is 0.0, and so on.\n", " ra.setPosition(pos)\n", " double norm = 1.0 - dist / blobSize\n", " ra.get().setReal(Math.max(ra.get().getRealDouble(), norm))\n", " }\n", " }\n", " }\n", " /**\n", " * Computes distance between the given position and a center point.\n", " */\n", " private double distance(long[] pos, long[] center) {\n", " long sumDistSquared = 0\n", " for (int d = 0; d < center.length; d++) {\n", " long dist = pos[d] - center[d]\n", " sumDistSquared += dist * dist\n", " }\n", " return Math.sqrt(sumDistSquared)\n", " }\n", "}\n", "\n", "// The @Plugin annotation is processed by the javac compiler,\n", "// which is used to generate the metadata in class bytecode.\n", "// Unfortunately, the Groovy compiler doesn't invoke the javac\n", "// compiler, so we need to register the plugin manually!\n", "blobsInfo = new PluginInfo(RandomBlobs.class, Op.class)\n", "ij.plugin().addPlugin(blobsInfo)\n", "blobsInfo" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "blobs = ij.op().run(\"blobs\", 30, 15, 256, 256)" ] } ], "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": "307px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }