Open ctrueden opened 10 years ago
Hi Curtis
Eventually it would be nice to have something as below. Still need Rolling-ball and a better convolver design. By the way, are you planning to put the ops in namespaces eventually?? (like ops.threshold.otsu(...) ).
(Farther down below is where I am at as of today, not as pretty but it works.)
# @OpService ops
# @Dataset input
# @OUTPUT ImgPlus thresholded
# convert to float
converted=ops.convert.toFloat(input)
# background subtraction
backgroundsubtracted=ops.backgroundsubtraction.rollingball(converted, 50)
# Laplacian of Gaussian
log=ops.filter.log(backgroundsubtracted, 3.0)
# threshold
thresholded=ops.threshold.otsu(log)
And here is what it looks like today
# @DatasetService data
# @DisplayService display
# @IOService io
# @OpService ops
# @net.imagej.Dataset inputData
# @OUTPUT net.imglib2.meta.ImgPlus thresholded
from net.imglib2.meta import ImgPlus
from net.imglib2.type.numeric.real import FloatType
from net.imglib2.img.display.imagej import ImageJFunctions
from ij import ImagePlus
from ij.plugin.filter import BackgroundSubtracter
from jarray import array
from fiji.plugin.trackmate.detection import DetectionUtils
from net.imagej.ops.convert import ConvertPixCopy
###############################################################
# Step 1: Rolling ball background subtraction (still uses IJ1)
###############################################################
# wrap as ImagePlus
imp=ImageJFunctions.wrap(inputData, "wrapped")
# create and call background subtractor
bgs=BackgroundSubtracter()
bgs.rollingBallBackground(imp.getProcessor(), 50.0, False, False, True, True, True)
# wrap the result of background subtraction as Img
iplus=ImagePlus("bgs", imp.getProcessor())
imgBgs=ImageJFunctions.wrapShort(iplus)
###############################################################
# Step 2: Laplacian of Gaussian Filtering
###############################################################
# convert to 32 bit
imgBgs32=ops.run("createimg", imgBgs, FloatType())
ops.convert(imgBgs32, imgBgs, ConvertPixCopy() )
# create the Laplacian of Gaussian filter
kernel = DetectionUtils.createLoGKernel( 3.0, 2, array([1.0, 1.0], 'd' ) )
# create the output Img for convolution
log=ImgPlus( ops.run("createimg", inputData.getImgPlus(), FloatType() ) )
# apply the log filter
ops.convolve(log, imgBgs32, kernel)
###############################################################
# Step 3: Threshold
###############################################################
# apply the threshold operation
thresholded = ops.run("triangle", log)
display.createDisplay("thresholded", data.create(ImgPlus(thresholded)))
Thanks for the update, @bnorthan. I think there are some great targets for the framework embedded in your "ideal" and "current" scripts above. It would be good to make an explicit list of those and file them as issues, so we can make concrete progress toward it.
Regarding namespaces: I actually thought about that during the last hackathon, but tentatively was against it (in my own head) since it would potentially complicate things. They can certainly be simulated using names like threshold_otsu
, threshold_triangle
, etc. If you really want the dot though, it makes things more complicated, because while you could write threshold.otsu
for the op name, you cannot embed a dot into a Java method name. So writing ops.threshold.otsu
wouldn't work. I guess I could tackle that as part of issue #19, though.
I do think namespaces are a perfect fit for things like the threshold methods. @dietzc? @dscho? @hinerm? Any opinion?
just my 2 cents: I think addressing namespaces as part of #19 makes sense. But this is rather a nice to have (priority = low). Anyway, targeting the scripts mentioned by @bnorthan makes completely sense.
Hi
Quick update on this. Once I get these pull requests done it should be possible to concisely write a spot detector using ops.
https://github.com/imagej/imagej-ops/pull/94 https://github.com/imagej/imagej-ops/pull/76 https://github.com/imagej/imagej-ops/pull/48
This code also uses IJ1 to generate a result table so it also demonstrates interoperability.
# @DatasetService data
# @DisplayService display
# @OpService ops
# @net.imagej.Dataset inputData
from net.imglib2.meta import ImgPlus
from net.imglib2.img.display.imagej import ImageJFunctions
from jarray import array
from ij import IJ
# create a log kernel
logKernel=ops.logKernel(2, 1.0);
# convolve with log kernel
logFiltered=ops.convolve(inputData, logKernel);
# display log filter result
display.createDisplay("log", ImgPlus(logFiltered));
# otsu threshold and display
thresholded = ops.run("threshold.otsu", logFiltered)
display.createDisplay("thresholded", ImgPlus(thresholded));
# convert to imagej1 imageplus so we can run analyze particles
impThresholded=ImageJFunctions.wrap(thresholded, "wrapped")
# convert to mask and analyze particles
IJ.run(impThresholded, "Convert to Mask", "")
IJ.run(impThresholded, "Analyze Particles...", "display add");
IJ.run("Close"); '''
@bnorthan That is beautiful!
@ctrueden, @dscho, @dietzc
Looks like you guys are getting lots done. Thanks for merging those pull requests. I tested this script this morning using the new 'master' and using the hela cells-image from 'sample images'.
I'll to try polish off the loose ends needed to make this script really work out of the box. In particular, we need Dataset
:left_right_arrow: ImagePlus
converters in SciJava so that the "Enable ImageJ2 data structures" option can go away.
Today, @hinerm looked a bit at the imagej-legacy
improvements that will be needed to improve this. But I just wanted to comment quickly that I was not able to get the script fully working while at the hackathon (even with "Enable ImageJ2 data structures" enabled). And I haven't had time to play with it again since then. Will try again as time allows...
I made a couple of small changes to make it run with the latest imagej release. Though the result may not make sense for ND images. Is there a repository we can put these kind of example scripts in??
# @DatasetService data
# @DisplayService display
# @OpService ops
# @net.imagej.Dataset inputData
from net.imglib2.meta import ImgPlus
from net.imglib2.img.display.imagej import ImageJFunctions
from jarray import array
from ij import IJ
# create a log kernel
logKernel=ops.logKernel(inputData.numDimensions(), 1.0);
# convolve with log kernel
logFiltered=ops.convolve(inputData, logKernel);
# display log filter result
display.createDisplay("log", ImgPlus(logFiltered));
# otsu threshold and display
thresholded = ops.run("threshold.otsu",logFiltered)
display.createDisplay("thresholded", ImgPlus(thresholded));
# convert to imagej1 imageplus so we can run analyze particles
impThresholded=ImageJFunctions.wrap(thresholded, "wrapped")
# convert to mask and analyze particles
IJ.run(impThresholded, "Convert to Mask", "")
IJ.run(impThresholded, "Analyze Particles...", "display add");
IJ.run("Close");```
@bnorthan Absolutely - all the SciJava scripting components have example scripts in a src/main/resources/script-templates/${LANGUAGE}
directory, (e.g. beanshell).
Any component can follow this same pattern and I believe the scripts will be automatically detected as examples in the script editor.
Since your script uses ImageJ classes and is demonstrating ops use I think it makes sense to keep it in the imagej-ops component, e.g. in a new src/main/resources/script-templates/Ops
directory?
Hi @hinerm
Since my script is Jython I assume I should add it to the scripting-jython repo in the below directory?? Is that correct? Thanks for letting me know.
https://github.com/scijava/scripting-jython/tree/master/src/main/resources/script-templates/
@bnorthan Script templates can go in any component's src/main/resources/script-templates
directory, but they should go in a component that makes sense for their dependencies.
Since your script uses ij.IJ
, it actually makes the most sense to put it in imagej-legacy since that's our IJ 1.x/IJ2 bridge component.
We're about to do a pass through our scripting repos to rename script-templates
to script_templates
as the former interferes with javadoc generation... so let's just put your script in imagej-legacy/src/main/resources/script_templates/
just kidding.. we want to put it here:
https://github.com/imagej/imagej-legacy/tree/master/src/main/resources/script_templates/Python
Hi Mark
I'm having trouble building/testing imagej-legacy with maven. The LegacyOpenerTest is failing in "TestPaulsMacro" on 'assertEquals(3, (int) nResults)'. Any ideas why?? Anything I could do differently to fix it?? Thanks --
@bnorthan because it is the worst unit test ever. It shares options with your Fiji installation, and requires ImageJ2 to be disabled. So open up Fiji and run Edit>Options>ImageJ2
and disable everything. Then run the test. Awesome!
I agree that it is the worst unit test ever – I wrote it. But then, it tests something very crucial: that the macro you run works no matter what your defaults are. And what it showed is that there is a problem in that ImageJ2 modifies the results of that one test depending on global settings. That is just an undesirable situation for users because it means that their macros might fail depending on something outside their control, or even worse: not fail, but instead yield irreproducible results.
Certainly one of the best things I could do would be to fix the bug in SCIFIO (I assume) causing the value discrepancy. But we should probably also temporarily set and then re-set the Imagej2 options values to ensure a consistent test (and even test once with and without IJ2 enabled)
@hinerm Agreed on all counts. Furthermore, we should add a global setting (maybe via system property) to disable IJ options persistence completely, for the purposes of unit tests. I.e.: all unit tests should be testing with the default options, unless they explicitly want to do otherwise.
Thanks @hinerm. The tests work now. I pushed a new branch (very simple, I just added the one script).
Let me know if anyone has any comments on the script. If it looks OK I can merge it. Thanks
Looks fantastic to me @bnorthan :+1:
The OPS library is focused on image processing, and as such it would be best if the front-facing example script did more than gimmicky operations. Rather, it should perform a really common and useful—but simple—image analysis workflow. We want something succinct that elegantly performs some powerful image processing.
We need to update both the
README.md
as well as the using-ops tutorial.