Code
%mavenRepo scijava.public https://maven.scijava.org/content/groups/public
%maven sc.fiji:bigdataviewer-vistools:1.0.0-beta-29
%maven net.imagej:ij:1.53t
%maven net.imglib2:imglib2-ij:2.0.0-beta-46
Stephan Saalfeld
May 2, 2022
Import dependencies. BigDataViewer Vistools is the convenience API that we use to display the results of our experiments and it includes all ImgLib2 related dependencies that we need.
We define the Juliaset as a function in 2D real space using a BiConsumer
lambda. The BiConsumer
receives two parameters, the first one (x) is the 2D coordinate, the second one (y) is the target of the function whose value will be set in place, here we use an IntType
. We also have to provide a Supplier
for instances of the target such that multiple threads can each create their own.
import io.github.spencerpark.jupyter.kernel.display.common.*;
import io.github.spencerpark.jupyter.kernel.display.mime.*;
import net.imglib2.img.display.imagej.*;
import net.imglib2.view.*;
import net.imglib2.*;
getKernelInstance().getRenderer().createRegistration(RandomAccessibleInterval.class)
.preferring(MIMEType.IMAGE_PNG)
.supporting(MIMEType.IMAGE_JPEG, MIMEType.IMAGE_GIF)
.register((rai, context) -> Image.renderImage(
ImageJFunctions.wrap(rai, rai.toString()).getBufferedImage(),
context));
getKernelInstance().getRenderer().createRegistration(RandomAccessible.class)
.preferring(MIMEType.IMAGE_PNG)
.supporting(MIMEType.IMAGE_JPEG, MIMEType.IMAGE_GIF)
.register((ra, context) -> Image.renderImage(
ImageJFunctions.wrap(
Views.interval(
ra,
new FinalInterval(
Arrays.copyOf(
new long[]{512, 512},
ra.numDimensions()))),
ra.toString()).getBufferedImage(),
context));
getKernelInstance().getRenderer().createRegistration(RealRandomAccessible.class)
.preferring(MIMEType.IMAGE_PNG)
.supporting(MIMEType.IMAGE_JPEG, MIMEType.IMAGE_GIF)
.register((rra, context) -> Image.renderImage(
ImageJFunctions.wrap(
Views.interval(
Views.raster(rra),
new FinalInterval(
Arrays.copyOf(
new long[]{512, 512},
rra.numDimensions()))),
rra.toString()).getBufferedImage(),
context));
import bdv.util.*;
import net.imglib2.position.FunctionRealRandomAccessible;
import net.imglib2.type.numeric.integer.*;
import net.imglib2.util.Intervals;
import net.imglib2.realtransform.*;
var juliaset = new FunctionRealRandomAccessible<>(
2,
(x, fx) -> {
int i = 0;
double v = 0, c = x.getDoublePosition(0), d = x.getDoublePosition(1);
for (; i < 255 && v < 4096; ++i) {
final double e = c * c - d * d;
d = 2 * c * d;
c = e + 0.3;
d += 0.6;
v = Math.sqrt(c * c + d * d);
++i;
}
fx.set(i);
},
UnsignedByteType::new);
Now we show this function with BigDataViewer. Use your mouse and keyboard to zoom in until you reach the precision limit of double
.
Cool? I think so! Now let’s embed one of the two parameters of the Juliaset as a third dimension. The code is almost the same except that we introduce a a variable a.
var juliaset3 = new FunctionRealRandomAccessible<>(
3,
(x, fx) -> {
int i = 0;
double v = 0, c = x.getDoublePosition(0), d = x.getDoublePosition(1), a = x.getDoublePosition(2);
for (; i < 255 && v < 4096; ++i) {
final double e = c * c - d * d;
d = 2 * c * d;
c = e + a;
d += 0.6;
v = Math.sqrt(c * c + d * d);
++i;
}
fx.set(i);
},
IntType::new);
And now we show this as a 3D volume in BigDataViewer. You can scroll through the ‘z’-dimension or arbitrarily slice through the 3D volume.
This was using a stateless function, i.e. each RealRandomAccess
uses the same instance. If you have stateful functions, you want to use a function provider. Spot the difference:
final var rnd = new Random();
var juliaset3Stripes = new FunctionRealRandomAccessible<>(
3,
() ->
{
final int offset = rnd.nextInt(100);
return (x, fx) -> {
int i = 0;
double v = 0, c = x.getDoublePosition(0), d = x.getDoublePosition(1), a = x.getDoublePosition(2);
for (; i < 255 && v < 4096; ++i) {
final double e = c * c - d * d;
d = 2 * c * d;
c = e + a;
d += 0.6;
v = Math.sqrt(c * c + d * d);
++i;
}
fx.set(i + offset);
};
},
IntType::new);