Curtis Rueden,
UW-Madison LOCI
Brian Northan,
True North Intelligent Algorithms LLC
Go from this:
from ij import IJ, ImagePlus, WindowManager
from ij.gui import GenericDialog
def run():
# fail if no images are open
wList = WindowManager.getIDList()
if wList is None:
IJ.noImage()
return
# build list of image titles
titles = []
for w in wList:
imp = WindowManager.getImage(w)
titles.append("" if imp is None else imp.getTitle())
# prompt user for image inputs
gd = GenericDialog("Label Copier")
gd.addChoice("Source Image:", titles, titles[0])
gd.addChoice("Target Image:", titles, titles[0])
gd.showDialog()
if gd.wasCanceled():
return
source = WindowManager.getImage(wList[gd.getNextChoiceIndex()])
target = WindowManager.getImage(wList[gd.getNextChoiceIndex()])
# ensure images are compatible
if source.getStackSize() != target.getStackSize():
IJ.error("Source and target images must have same stack size.")
return
# copy the labels
for i in range(1, source.getStackSize()):
label = source.getStack().getSliceLabel(i)
target.getStack().setSliceLabel(label, i)
run()
To this:
# @ImagePlus source
# @ImagePlus target
from ij import IJ
def run():
# ensure images are compatible
if source.getStackSize() != target.getStackSize():
IJ.error("Source and target images must have same stack size.")
return
# copy the labels
for i in range(1, source.getStackSize()):
label = source.getStack().getSliceLabel(i)
target.getStack().setSliceLabel(label, i)
run()
[
key to open the Script EditorYou should see:
# @String(label="Please enter your name",description="Name field") name
# @OUTPUT String greeting
# A Jython script with parameters.
# It is the duty of the scripting framework to harvest
# the 'name' parameter from the user, and then display
# the 'greeting' output parameter, based on its type.
greeting = "Hello, " + name + "!"
You can use any Java type for a parameter.
Select Templates ▶ JavaScript ▶ Widgets
But only some types appear in the user interface:
boolean | Boolean | checkbox |
byte | short | int | long | numeric field |
Byte | Short | Integer | Long | numeric field |
BigInteger | BigDecimal | numeric field |
char | Character | String | text field |
Dataset | ImagePlus (>=2) | dropdown list |
ColorRGB | color chooser |
Date | date chooser |
File | file chooser (opener) |
Some parameters have configurable styles:
Numbers with style="slider" | field + slider |
Numbers with style="scroll bar" | field + scroll bar |
String with style="text area" | text area |
String with style="password" | password field |
String with choices={"foo", "bar", ...} | dropdown list |
String with style="radioButtonHorizontal" | radio buttons |
String with style="radioButtonVertical" | radio buttons |
File with style="save" | file chooser (saver) |
File with style="directory" | directory chooser |
String with visibility=MESSAGE | label (non-editable) |
Certain parameters are populated automatically:
Service | Context |
from the application context |
Dataset | ImagePlus |
from the active image |
DataView | DatasetView |
from the active image |
Display | ImageDisplay |
from the active image |
Overlay |
from the active image's ROI |
The ImageJ2 data model is still unstable; some of these will change.
More things are possible in Java code:
See the widget-demo tutorial for details.
Explore data using ImageJ2 Data Model (beta)
You should see:
# @OpService ops
# @Dataset data
# @UIService ui
# to run this tutorial run 'file->Open Samples->Confocal Series' and make sure that
# confocal-series.tif is the active image
from net.imglib2.util import Intervals
from net.imagej.axis import Axes
# first take a look at the size and type of each dimension
for d in range(data.numDimensions()):
print "axis d: type: "+str(data.axis(d).type())+" length: "+str(data.dimension(d))
img=data.getImgPlus()
xLen = data.dimension(data.dimensionIndex(Axes.X))
yLen = data.dimension(data.dimensionIndex(Axes.Y))
zLen = data.dimension(data.dimensionIndex(Axes.Z))
cLen = data.dimension(data.dimensionIndex(Axes.CHANNEL))
# crop a channel
c0=ops.image().crop(img, Intervals.createMinMax(0, 0, 0,0,xLen-1, yLen-1, 0, zLen-1))
# crop both channels at z=12
z12=ops.image().crop(img, Intervals.createMinMax(0,0,0,12, xLen-1, yLen-1, cLen-1, 12))
# crop channel 0 at z=12
c0z12=ops.image().crop(img, Intervals.createMinMax(0,0,0,12, xLen-1, yLen-1, 0, 12))
# crop an roi at channel 0, z=12
roiC0z12=ops.image().crop(img, Intervals.createMinMax(150,150,0,12, 200, 200, 0, 12))
# display all the cropped images
ui.show("C0", c0)
ui.show("z12", z12)
ui.show("C0z12", c0z12)
ui.show("roiC0z12", roiC0z12)
The first few lines specify the parameters and import useful utilities
# @OpService ops
# @Dataset data
# @UIService ui
from net.imglib2.util import Intervals
from net.imagej.axis import Axes
Loop through the dimensions and print the axis type and length of each dimension
for d in range(data.numDimensions()):
print "axis d: type: "+str(data.axis(d).type())+" length: "+str(data.dimension(d))
img=data.getImgPlus()
xLen = data.dimension(data.dimensionIndex(Axes.X))
yLen = data.dimension(data.dimensionIndex(Axes.Y))
zLen = data.dimension(data.dimensionIndex(Axes.Z))
cLen = data.dimension(data.dimensionIndex(Axes.CHANNEL))
Use ops to crop various intervals.
# crop a channel
c0=ops.image().crop(img, Intervals.createMinMax(0, 0, 0,0,xLen-1, yLen-1, 0, zLen-1))
# crop both channels at z=12
z12=ops.image().crop(img, Intervals.createMinMax(0,0,0,12, xLen-1, yLen-1, cLen-1, 12))
# crop channel 0 at z=12
c0z12=ops.image().crop(img, Intervals.createMinMax(0,0,0,12, xLen-1, yLen-1, 0, 12))
# crop an roi at channel 0, z=12
roiC0z12=ops.image().crop(img, Intervals.createMinMax(150,150,0,12, 200, 200, 0, 12))
Use the ui service to show our cropped images
ui.show("C0", c0)
ui.show("z12", z12)
ui.show("C0z12", c0z12)
ui.show("roiC0z12", roiC0z12)
You should see:
# @OpService ops
# @UIService ui
# @Dataset inputData
# @Double sigma
from net.imglib2.img.display.imagej import ImageJFunctions
from ij import IJ
from java.lang import Integer
# create a log kernel
logKernel=ops.create().kernelLog(Integer(inputData.numDimensions()), sigma);
# convolve with log kernel
logFiltered=ops.filter().convolve(inputData, logKernel)
# display log filter result
ui.show("log", logFiltered)
# otsu threshold and display
thresholded = ops.threshold().otsu(logFiltered)
ui.show("thresholded", 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")
The parameters
# @OpService ops
# @UIService ui
# @Dataset inputData
# @Double sigma
Import the ImageJFunctions and IJ classes
from net.imglib2.img.display.imagej import ImageJFunctions
from ij import IJ
from java.lang import Integer
Use ops to implement a segmentation pipeline.
# create a log kernel
logKernel=ops.create().kernelLog(Integer(inputData.numDimensions()), sigma);
# convolve with log kernel
logFiltered=ops.filter().convolve(inputData, logKernel)
# display log filter result
ui.show("log", logFiltered)
# otsu threshold and display
thresholded = ops.threshold().otsu(logFiltered)
ui.show("thresholded", thresholded)
Use IJ1 functions to analyze
# 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")
Add the option to use deconvolution as an additional preprocessing step
# @OpService ops
# @UIService ui
# @Dataset inputData
# @Boolean deconvolve
from net.imglib2.img.display.imagej import ImageJFunctions
from ij import IJ
if (deconvolve):
gKernel=ops.create().kernelGauss(inputData.numDimensions(), 3.0)
inputData=ops.deconvolve().richardsonLucy(inputData, gKernel, 10)
ui.show("deconvolved", inputData)
You should see:
# @OpService ops
# @Dataset data
# @UIService ui
from net.imagej.ops import Ops
from net.imagej import ImgPlus
from net.imglib2 import FinalDimensions
from net.imglib2.type.logic import BitType
from net.imagej.axis import Axes
# first take a look at the size and type of each dimension
for d in range(data.numDimensions()):
print "axis d: type: "+str(data.axis(d).type())+" length: "+str(data.dimension(d))
xDim = data.dimensionIndex(Axes.X)
yDim = data.dimensionIndex(Axes.Y)
zDim = data.dimensionIndex(Axes.Z)
cDim = data.dimensionIndex(Axes.CHANNEL)
# create the otsu op
otsu=ops.op(Ops.Threshold.Otsu, data)
# create memory for the thresholded image
thresholded=ops.create().img(data.getImgPlus(), BitType())
# call slice wise thresholde axis to process, in this case [0,1] means process the
# first two axes (x and y)
ops.slicewise(thresholded, data.getImgPlus(), otsu, [xDim,yDim])
# try again with [xDim, yDim, zDim] is the result different?? Why??
# create an ImgPlus using the thresholded img, copy meta data from the input
thresholdedPlus=ImgPlus(thresholded, data.getImgPlus(), True)
ui.show("thresholded", thresholdedPlus)
The first half of the script sets up the parameters, imports classes, and prints out the size and type of the input axes:
# @OpService ops
# @Dataset data
# @UIService ui
from net.imagej.ops import Ops
from net.imagej import ImgPlus
from net.imglib2 import FinalDimensions
from net.imglib2.type.logic import BitType
from net.imagej.axis import Axes
# first take a look at the size and type of each dimension
for d in range(data.numDimensions()):
print "axis d: type: "+str(data.axis(d).type())+" length: "+str(data.dimension(d))
xDim = data.dimensionIndex(Axes.X)
yDim = data.dimensionIndex(Axes.Y)
zDim = data.dimensionIndex(Axes.Z)
cDim = data.dimensionIndex(Axes.CHANNEL)
Create an Otsu (threshold) op and memory for the result
# create the otsu op
otsu=ops.op(Ops.Threshold.Otsu, data)
# create memory for the thresholded image
thresholded=ops.create().img(data.getImgPlus(), BitType())
Then call the slicewise op passing, the output, the input, the threshold op and the axes to process.
ops.slicewise(thresholded, data.getImgPlus(), otsu, [xDim,yDim])
# try again with [xDim, yDim, zDim] is the result different?? Why??
# create an ImgPlus using the thresholded img, copy meta data from the input
thresholdedPlus=ImgPlus(thresholded, data.getImgPlus(), True)
ui.show("thresholded", thresholdedPlus)
ops.slicewise(thresholded, data.getImgPlus(), otsu, [xDim,yDim,zDim])
You should see:
# @OpService ops
# @UIService ui
# @Dataset data
from net.imagej.ops import Ops
from net.imagej.axis import Axes
# get the dimension to project
pDim = data.dimensionIndex(Axes.Z)
# generate the projected dimension
projectedDimensions=[data.dimension(d) for d in range(0, data.numDimensions()) if d!=pDim]
print projectedDimensions
# create memory for projections
maxProjection=ops.create().img(projectedDimensions)
sumProjection=ops.create().img(projectedDimensions)
# use op service to get the max op
maxOp = ops.op(Ops.Stats.Max, data)
# use op service to get the sum op
sumOp = ops.op(Ops.Stats.Sum, sumProjection.firstElement(), data)
# call the project op passing
# maxProjection: img to put projection in
# image: img to project
# op: the op used to generate the projection (in this case "max")
# dimensionToProject: the dimension to project
ops.image().project(maxProjection, data, maxOp, pDim)
# project again this time use sum projection
ops.image().project(sumProjection, data, sumOp, pDim)
# display the results
ui.show("max projection", maxProjection)
ui.show("sum projection", sumProjection)
Set up the Parameters and import classe
# @OpService ops
# @UIService ui
# @Dataset data
from net.imagej.ops import Ops
from net.imagej.axis import Axes
Calculate projected dimensions and generate memory
# get the dimension to project
pDim = data.dimensionIndex(Axes.Z)
# generate the projected dimension
projectedDimensions=[data.dimension(d) for d in range(0, data.numDimensions()) if d!=pDim]
print projectedDimensions
# create memory for projections
maxProjection=ops.create().img(projectedDimensions)
sumProjection=ops.create().img(projectedDimensions)
Create max and sum ops
# use op service to get the max op
maxOp = ops.op(Ops.Stats.Max, data)
# use op service to get the sum op
sumOp = ops.op(Ops.Stats.Sum, sumProjection.firstElement(), data)
Call projection op(s) and display outputs
# call the project op passing
# maxProjection: img to put projection in
# image: img to project
# op: the op used to generate the projection (in this case "max")
# dimensionToProject: the dimension to project
ops.image().project(maxProjection, data, maxOp, pDim)
# project again this time use sum projection
ops.image().project(sumProjection, data, sumOp, pDim)
# display the results
ui.show("max projection", maxProjection)
ui.show("sum projection", sumProjection)
You should see:
# @OpService ops
# @UIService ui
# @Dataset image
# @Dataset template
'''
This example is an 'ops' version of:
http://fiji.sc/ImgLib2_Examples#Example_6c_-_Complex_numbers_and_Fourier_transforms
for which the code and images can be found
https://github.com/imglib/imglib2-tutorials
'''
from net.imglib2.img.display.imagej import ImageJFunctions
from net.imglib2.type.numeric.complex import ComplexFloatType
from net.imglib2.outofbounds import OutOfBoundsMirrorExpWindowingFactory
from net.imglib2.converter import ComplexImaginaryFloatConverter
from net.imglib2.converter import ComplexPhaseFloatConverter
from net.imglib2.converter import ComplexRealFloatConverter
# perform fft of the template
# basic fft call with no parameters
#templateFFT=ops.filter().fft(template.getImgPlus())
# alternatively to pass an outofbounds factory we have to pass every parameter. We want:
# output='None', input=template, borderSize=10 by 10, fast='True', outOfBoundsFactor=OutOfBoundsMirrorExpWindowingFactory
templateFFT=ops.filter().fft(None, template.getImgPlus(), [10, 10], True, OutOfBoundsMirrorExpWindowingFactory(0.25))
# display fft (by default in generalized log power spectrum)
ImageJFunctions.show(templateFFT).setTitle("fft power spectrum")
# display fft phase spectrum
ImageJFunctions.show( templateFFT,ComplexPhaseFloatConverter() ).setTitle( "fft phase spectrum" )
# display fft real values
ImageJFunctions.show( templateFFT,ComplexRealFloatConverter() ).setTitle( "fft real values" )
# display fft imaginary values
ImageJFunctions.show( templateFFT, ComplexImaginaryFloatConverter() ).setTitle( "fft imaginary values" )
# complex invert the fft of the template
c = ComplexFloatType()
for t in templateFFT:
c.set(t)
t.complexConjugate()
c.mul(t)
t.div(c)
# create Img memory for inverse FFT and compute inverse
templateInverse=ops.create().img([template.dimension(0), template.dimension(1)])
ops.filter().ifft(templateInverse, templateFFT)
ui.show("template inverse", templateInverse)
# convolve templateInverse with image
final=ops.filter().convolve(image, templateInverse)
ui.show("final", final)
Set up the parameters and import several classes from imglib2
# @OpService ops
# @UIService ui
# @Dataset image
# @Dataset template
from net.imglib2.img.display.imagej import ImageJFunctions
from net.imglib2.type.numeric.complex import ComplexFloatType
from net.imglib2.outofbounds import OutOfBoundsMirrorExpWindowingFactory
from net.imglib2.converter import ComplexImaginaryFloatConverter
from net.imglib2.converter import ComplexPhaseFloatConverter
from net.imglib2.converter import ComplexRealFloatConverter
Perform the FFT of the template using Ops
# perform fft of the template
# basic fft call with no parameters
#templateFFT=ops.filter().fft(template.getImgPlus())
# alternatively to pass an outofbounds factory we have to pass every parameter. We want:
# output='None', input=template, borderSize=10 by 10, fast='True', outOfBoundsFactor=OutOfBoundsMirrorExpWindowingFactory
templateFFT=ops.filter().fft(None, template.getImgPlus(), [10, 10], True, OutOfBoundsMirrorExpWindowingFactory(0.25))
Use utilities from imglib2 to display the power spectrum, phase, real and imaginary values of the FFT
# display fft (by default in generalized log power spectrum)
ImageJFunctions.show(templateFFT).setTitle("fft power spectrum")
# display fft phase spectrum
ImageJFunctions.show( templateFFT,ComplexPhaseFloatConverter() ).setTitle( "fft phase spectrum" )
# display fft real values
ImageJFunctions.show( templateFFT,ComplexRealFloatConverter() ).setTitle( "fft real values" )
# display fft imaginary values
ImageJFunctions.show( templateFFT, ComplexImaginaryFloatConverter() ).setTitle( "fft imaginary values" )
Use imglib2 to calculate the inverse of the template fft:
# complex invert the fft of the template
c = ComplexFloatType()
for t in templateFFT:
c.set(t)
t.complexConjugate()
c.mul(t)
t.div(c)
Calculate the inverse of the template, convolve with the image and display the result
# create Img memory for inverse FFT and compute inverse
templateInverse=ops.create().img([template.dimension(0), template.dimension(1)])
ops.filter().ifft(templateInverse, templateFFT)
ui.show("template inverse", templateInverse)
# convolve templateInverse with image
final=ops.filter().convolve(image, templateInverse)
ui.show("final", final)
SciJava principal investigators:
Kevin Eliceiri |
Jason Swedlow |
Pavel Tomancak |
Anne Carpenter |
Michael Berthold |
SciJava developers:
Barry DeZonia |
Christian Dietz |
Mark Hiner |
Lee Kamentsky |
Brian Northan |
Tobias Pietzsch |
Stephan Preibisch |
Curtis Rueden |
Stephan Saalfeld |
Johannes Schindelin |
And everyone supporting open science and open software!