Jan Eglinger
Facility for Advanced Imaging and Microscopy (FAIM)
Friedrich Miescher Institute for Biomedical Research (FMI)
Basel, Switzerland
Basel, March 7, 2018
//load ImageJ
%classpath config resolver imagej.public https://maven.imagej.net/content/groups/public
%classpath add mvn net.imagej imagej 2.0.0-rc-71
//create ImageJ object
ij = new net.imagej.ImageJ()
notebook = ij.notebook()
datasetIO = ij.scifio().datasetIO()
ops = ij.op()
"ImageJ initialized"
Added new repo: imagej.public
Mar 12, 2019 4:08:17 PM java.util.prefs.WindowsPreferences <init> WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.
ImageJ initialized
/* Required Imports */
import net.imglib2.type.numeric.real.FloatType
import net.imglib2.interpolation.randomaccess.FloorInterpolatorFactory
import net.imglib2.RandomAccessibleInterval
/* Utility Functions */
tile = { images ->
int[] gridLayout = images[0] in List ?
[images[0].size, images.size] : // 2D images list
[images.size] // 1D images list
RandomAccessibleInterval[] rais = images.flatten()
ij.notebook().mosaic(gridLayout, rais)
}
table_image = { array ->
img = ij.op().create().kernel(array as double[][], new FloatType())
ij.op().run("transform.scaleView", img,
[32,32] as double[],
new FloorInterpolatorFactory()
)
}
zoomedView = { img, factor ->
ij.op().run("transform.scaleView", img,
[factor,factor] as double[],
new FloorInterpolatorFactory()
)
}
null
null
Let's look at a point light source (ok, for better visibility, it's a square):
import net.imglib2.type.numeric.real.FloatType
pixels = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
image = ij.op().run("create.kernel", pixels, new FloatType())
ij.notebook().display([["Original": zoomedView(image,16)]])
Original |
---|
What happens if we look at this point through a microscope?
The microscope optics lead to a blurred image on our camera chip:
gaussKernel = ij.op().run("create.kernelGauss", [1,1])
// zoomedView(gaussKernel, 16)
result = ij.op().run("filter.convolve", image, gaussKernel)
ij.notebook().display([["Original": zoomedView(image,16), "Image": zoomedView(result,16)]])
Original | Image |
---|---|
What happens if we have two points close together?
import net.imglib2.type.numeric.real.FloatType
pixels = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
image2 = ij.op().run("create.kernel", pixels, new FloatType())
ij.notebook().display([["One point": zoomedView(image,16), "Image of one point": zoomedView(result,16), "Two points": zoomedView(image2,16)]])
One point | Image of one point | Two points |
---|---|---|
The microscope optics lead to a blurred image on our camera chip:
gaussKernel = ij.op().run("create.kernelGauss", [1,1])
// zoomedView(gaussKernel, 16)
result2 = ij.op().run("filter.convolve", image2, gaussKernel)
ij.notebook().display([["One point": zoomedView(image,16), "Image of one point": zoomedView(result,16), "Two points": zoomedView(image2,16), "Image of two points": zoomedView(result2,16)]])
One point | Image of one point | Two points | Image of two points |
---|---|---|---|
...
In digital images, we can model this process by defining a pixel neighborhood
$\begin{bmatrix} i_{-1,-1} & i_{0,-1} & i_{1,-1}\\ i_{-1,0} & i_{0,0} & i_{1,0}\\ i_{-1,1} & i_{-1,1} & i_{1,1} \end{bmatrix}$
... and multiplying every pixel in the neighborhood with a certain weight, defined in a kernel:
$\begin{bmatrix} k_{1,1} & k_{2,1} & k_{3,1} \\ k_{1,2} & k_{2,2} & k_{3,2} \\ k_{1,3} & k_{2,3} & k_{3,3} \end{bmatrix}$
For example, if we define a 3x3 neighborhood and a kernel full of 1
s: $\begin{bmatrix}
1 & 1 & 1 \\
1 & 1 & 1 \\
1 & 1 & 1
\end{bmatrix}$
... each pixel in the result will be the sum of all the pixels in the neighborhood:
$result_{0,0} = i_{-1,-1} + i_{0,-1} + i_{1,-1} + i_{-1,0} + i_{0,0} + i_{1,0} + i_{-1,1} + i_{-1,1} + i_{1,1}$
Digital image convolution is a process where each pixel is assigned the result of a matrix multiplication:
$result = (i)mage * (k)ernel$
$result = \begin{bmatrix} i_{1,1} & i_{2,1} & i_{3,1} & i_{4,1} & i_{5,1}\\ i_{1,2} & i_{2,2} & i_{3,2} & i_{4,2} & i_{5,2}\\ i_{1,3} & i_{2,3} & \color{red}{i_{3,3}} & i_{4,3} & i_{5,3}\\ i_{1,3} & i_{2,4} & i_{3,4} & i_{4,4} & i_{5,4}\\ i_{1,3} & i_{2,5} & i_{3,5} & i_{4,5} & i_{5,5} \end{bmatrix} * \begin{bmatrix} k_{1,1} & k_{2,1} & k_{3,1} \\ k_{1,2} & k_{2,2} & k_{3,2} \\ k_{1,3} & k_{2,3} & k_{3,3} \end{bmatrix} $
$result_{3,3} = i_{2,2} k_{3,3} + i_{3,2} k_{2,3} + i_{4,2} k_{1,3} + i_{2,3} k_{3,2} + ...$
0 & 0 & 0 \ 0 & 1 & 0 \ 0 & 0 & 0 \end{bmatrix}$
\frac{1}{9} & \frac{1}{9} & \frac{1}{9} \ \frac{1}{9} & \frac{1}{9} & \frac{1}{9} \ \frac{1}{9} & \frac{1}{9} & \frac{1}{9} \end{bmatrix}$
-1 & 0 & 1 \ -2 & 0 & 2 \ -1 & 0 & 1 \end{bmatrix}$
0 & 1 & 0 \ 1 & -4 & 1 \ 0 & 1 & 0 \end{bmatrix}$
Approximate representation of a Gauss filter kernel
$\frac{1}{256} \begin{bmatrix} 1 & 4 & 6 & 4 & 1 \\ 4 & 16 & 24 & 16 & 4 \\ 6 & 24 & 36 & 24 & 6 \\ 4 & 16 & 24 & 16 & 4 \\ 1 & 4 & 6 & 4 & 1 \end{bmatrix}$
https://imagej.net/docs/guide/146-29.html#toc-Subsection-29.8
https://imagej.net/docs/guide/146-29.html#toc-Subsection-29.10
Questions ?
image = ij.io().open("https://imagej.net/images/blobs.gif")
cropped = ij.op().run("hyperSliceView", image, 2, 0)
dims = new long[2]
cropped.dimensions(dims)
dims
import net.imglib2.type.numeric.real.FloatType
kernelArray = [[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 1, -8, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]]
kernelArray2 = [[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]]
kernel = ij.op().run("create.kernel", kernelArray2, new FloatType())
zoomedView(kernel, 32)
//extended = ij.op().run("intervalView", ij.op().run("extendBorderView", cropped), cropped)
result = ij.op().run("filter.convolve", cropped, kernel)
ij.notebook().display([["Input": cropped, "Output": result]])