diff --git a/src/main/java/sc/fiji/bdvpg/command/BdvAppendSampleCommand.java b/src/main/java/sc/fiji/bdvpg/command/BdvAppendSampleCommand.java new file mode 100644 index 00000000..20925ce0 --- /dev/null +++ b/src/main/java/sc/fiji/bdvpg/command/BdvAppendSampleCommand.java @@ -0,0 +1,56 @@ +package sc.fiji.bdvpg.command; + +import bdv.util.BdvHandle; +import bdv.viewer.Source; +import org.scijava.ItemIO; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import sc.fiji.bdvpg.bdv.source.append.AddSourceToBdv; +import sc.fiji.bdvpg.bdv.source.displayopts.BrightnessAdjuster; +import sc.fiji.bdvpg.log.SystemLogger; +import sc.fiji.bdvpg.scijava.ScijavaBdvDefaults; +import sc.fiji.bdvpg.source.importer.samples.MandelbrotSourceGetter; +import sc.fiji.bdvpg.source.importer.samples.VoronoiSourceGetter; +import sc.fiji.bdvpg.source.importer.samples.Wave3DSourceGetter; + +@Plugin(type = Command.class, menuPath = ScijavaBdvDefaults.RootMenu+"Tools>Append Sample Source to Bdv Window") +public class BdvAppendSampleCommand implements Command { + + @Parameter(type = ItemIO.BOTH) + BdvHandle bdvh; + + @Parameter(choices = {"Mandelbrot", "Wave3D", "Voronoi", "Big Voronoi"}) + String sampleName; + + @Override + public void run() { + Source src; + switch(sampleName) { + + case "Mandelbrot": + src = (new MandelbrotSourceGetter()).get(); + break; + + case "Wave3D": + src = (new Wave3DSourceGetter()).get(); + break; + + case "Voronoi": + src = (new VoronoiSourceGetter(new long[]{512,512,1}, 256, true).get()); + break; + + case "Big Voronoi": + src = (new VoronoiSourceGetter(new long[]{2048,2048,2048}, 65536, false).get()); + break; + + default: + new SystemLogger().err("Invalid sample name"); + return; + } + new AddSourceToBdv(bdvh, src).run(); + + // DO NOT WORK + //new BrightnessAdjuster(bdvh, src, 0.01, 0.99).run(); + } +} diff --git a/src/main/java/sc/fiji/bdvpg/command/BdvWindowCreate.java b/src/main/java/sc/fiji/bdvpg/command/BdvWindowCreateCommand.java similarity index 98% rename from src/main/java/sc/fiji/bdvpg/command/BdvWindowCreate.java rename to src/main/java/sc/fiji/bdvpg/command/BdvWindowCreateCommand.java index 20fdff58..657dc03f 100644 --- a/src/main/java/sc/fiji/bdvpg/command/BdvWindowCreate.java +++ b/src/main/java/sc/fiji/bdvpg/command/BdvWindowCreateCommand.java @@ -19,7 +19,7 @@ @Plugin(type = Command.class, menuPath = ScijavaBdvDefaults.RootMenu+"Create Empty BDV Frame", label = "Creates an empty Bdv window") -public class BdvWindowCreate implements Command { +public class BdvWindowCreateCommand implements Command { @Parameter(label = "Create a 2D Bdv window") public boolean is2D = false; diff --git a/src/main/java/sc/fiji/bdvpg/scijava/bdv/ActiveBdvPreprocessor.java b/src/main/java/sc/fiji/bdvpg/scijava/bdv/ActiveBdvPreprocessor.java index bccd8427..098111e4 100644 --- a/src/main/java/sc/fiji/bdvpg/scijava/bdv/ActiveBdvPreprocessor.java +++ b/src/main/java/sc/fiji/bdvpg/scijava/bdv/ActiveBdvPreprocessor.java @@ -8,7 +8,7 @@ import org.scijava.object.ObjectService; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; -import sc.fiji.bdvpg.command.BdvWindowCreate; +import sc.fiji.bdvpg.command.BdvWindowCreateCommand; import sc.fiji.bdvpg.scijava.GuavaWeakCacheService; import java.lang.ref.WeakReference; @@ -54,7 +54,7 @@ public BdvHandle getValue() { { return (BdvHandle) - cs.run(BdvWindowCreate.class, + cs.run(BdvWindowCreateCommand.class, true, "is2D", false, "windowTitle", "Bdv", diff --git a/src/main/java/sc/fiji/bdvpg/source/importer/samples/MandelbrotSourceGetter.java b/src/main/java/sc/fiji/bdvpg/source/importer/samples/MandelbrotSourceGetter.java new file mode 100644 index 00000000..160891bd --- /dev/null +++ b/src/main/java/sc/fiji/bdvpg/source/importer/samples/MandelbrotSourceGetter.java @@ -0,0 +1,36 @@ +package sc.fiji.bdvpg.source.importer.samples; + +import bdv.viewer.Source; +import net.imglib2.FinalInterval; + +import java.util.function.Supplier; + +public class MandelbrotSourceGetter implements Runnable, Supplier { + + int maxIterations = 255; + + public void run() { + // Useless + } + + @Override + public Source get() { + return new Procedural3DImageShort( + p -> { + double re = p[0]; + double im = p[1]; + int i = 0; + for ( ; i < maxIterations; ++i ) + { + final double squre = re * re; + final double squim = im * im; + if ( squre + squim > 4 ) + break; + im = 2 * re * im + p[1]; + re = squre - squim + p[0]; + } + return i; + } + ).getSource(new FinalInterval(new long[]{ -2, -1, -0}, new long[]{ 1, 1, 0 }), "Mandelbrot Set"); + } +} diff --git a/src/main/java/sc/fiji/bdvpg/source/importer/samples/Procedural3DImageShort.java b/src/main/java/sc/fiji/bdvpg/source/importer/samples/Procedural3DImageShort.java new file mode 100644 index 00000000..28fd96ce --- /dev/null +++ b/src/main/java/sc/fiji/bdvpg/source/importer/samples/Procedural3DImageShort.java @@ -0,0 +1,85 @@ +package sc.fiji.bdvpg.source.importer.samples; + +import bdv.util.RealRandomAccessibleIntervalSource; +import bdv.viewer.Source; +import net.imglib2.*; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.numeric.integer.UnsignedShortType; + +import java.util.function.ToIntFunction; + +public class Procedural3DImageShort extends RealPoint implements RealRandomAccess { + final UnsignedShortType t; + + ToIntFunction evalFunction; + + public Procedural3DImageShort(ToIntFunction evalFunction) + { + super( 3 ); // number of dimensions is 3 + t = new UnsignedShortType(); + this.evalFunction=evalFunction; + } + + public Procedural3DImageShort(UnsignedShortType t) { + this.t = t; + } + + @Override + public RealRandomAccess copyRealRandomAccess() { + return copy(); + } + + @Override + public UnsignedShortType get() { + t.set( + evalFunction.applyAsInt(position) + ); + return t; + } + + @Override + public Procedural3DImageShort copy() { + Procedural3DImageShort a = new Procedural3DImageShort(evalFunction); + a.setPosition( this ); + return a; + } + + public RealRandomAccessible getRRA() { + + RealRandomAccessible rra = new RealRandomAccessible() { + @Override + public RealRandomAccess realRandomAccess() { + return copy(); + } + + @Override + public RealRandomAccess realRandomAccess(RealInterval realInterval) { + return copy(); + } + + @Override + public int numDimensions() { + return 3; + } + }; + + return rra; + } + + public Source getSource(final Interval interval, AffineTransform3D at3D, String name) { + return new RealRandomAccessibleIntervalSource<>( getRRA(), interval, new UnsignedShortType(), + new AffineTransform3D(), name ); + } + + public Source getSource(final Interval interval, String name) { + return new RealRandomAccessibleIntervalSource<>( getRRA(), interval, new UnsignedShortType(), + new AffineTransform3D(), name ); + } + + public Source getSource(String name) { + return new RealRandomAccessibleIntervalSource<>( getRRA(), new FinalInterval(new long[]{0,0,0}, new long[]{1,1,1}), new UnsignedShortType(), + new AffineTransform3D(), name ); + } + + +} diff --git a/src/main/java/sc/fiji/bdvpg/source/importer/samples/VoronoiSourceGetter.java b/src/main/java/sc/fiji/bdvpg/source/importer/samples/VoronoiSourceGetter.java new file mode 100644 index 00000000..f332a90d --- /dev/null +++ b/src/main/java/sc/fiji/bdvpg/source/importer/samples/VoronoiSourceGetter.java @@ -0,0 +1,160 @@ +package sc.fiji.bdvpg.source.importer.samples; + +import bdv.util.RandomAccessibleIntervalSource; +import bdv.viewer.Source; +import net.imglib2.*; +import net.imglib2.algorithm.util.Grids; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.interpolation.neighborsearch.NearestNeighborSearchInterpolatorFactory; +import net.imglib2.neighborsearch.NearestNeighborSearch; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +import java.util.Random; +import java.util.function.Supplier; + +public class VoronoiSourceGetter implements Runnable, Supplier { + + // Size of the image in pixels + final long[] imgSize; + // Number of random points that will define voronoi cells + final int numPts; + // Flags if the image should be computed completely + final boolean copyImg; + + public VoronoiSourceGetter(final long[] imgSize, int numPts, boolean copyImg) { + this.imgSize = imgSize; + this.numPts = numPts; + this.copyImg = copyImg; + } + + public void run() { + // Useless + } + + @Override + public Source get() { + RandomAccessibleInterval voronoi = getVoronoiTestLabelImage(imgSize, numPts, copyImg); + return new RandomAccessibleIntervalSource<>( voronoi, new FloatType(), new AffineTransform3D(), "Voronoi_"+numPts+" Pts_["+imgSize[0]+","+imgSize[1]+","+imgSize[2]+"]" ); + } + + public static RandomAccessibleInterval getVoronoiTestLabelImage(final long[] imgTestSize, int numPts, boolean copyImg) { + + // the interval in which to create random points + FinalInterval interval = new FinalInterval( imgTestSize ); + + // create an IterableRealInterval + IterableRealInterval< FloatType > realInterval = createRandomPoints( interval, numPts ); + + // using nearest neighbor search we will be able to return a value an any position in space + NearestNeighborSearch< FloatType > search = + new NearestNeighborSearchOnKDTree<>( + new KDTree<>( realInterval ) ); + + // make it into RealRandomAccessible using nearest neighbor search + RealRandomAccessible< FloatType > realRandomAccessible = + Views.interpolate( search, new NearestNeighborSearchInterpolatorFactory< FloatType >() ); + + // convert it into a RandomAccessible which can be displayed + RandomAccessible< FloatType > randomAccessible = Views.raster( realRandomAccessible ); + + // set the initial interval as area to view + RandomAccessibleInterval< FloatType > labelImage = Views.interval( randomAccessible, interval ); + + if (copyImg) { + final RandomAccessibleInterval< FloatType > labelImageCopy = new ArrayImgFactory( Util.getTypeFromInterval( labelImage ) ).create( labelImage ); + + // Image copied to avoid computing it on the fly + // https://github.com/imglib/imglib2-algorithm/blob/47cd6ed5c97cca4b316c92d4d3260086a335544d/src/main/java/net/imglib2/algorithm/util/Grids.java#L221 used for parallel copy + + Grids.collectAllContainedIntervals(imgTestSize, new int[]{64, 64, 64}).stream().forEach(blockinterval -> { + copy(labelImage, Views.interval(labelImageCopy, blockinterval)); + }); + + // Alternative non parallel copy + //LoopBuilder.setImages(labelImage, labelImageCopy).forEachPixel(Type::set); + return labelImageCopy; + + } else { + + return labelImage; + } + } + + /** + * Copy from a source that is just RandomAccessible to an IterableInterval. Latter one defines + * size and location of the copy operation. It will query the same pixel locations of the + * IterableInterval in the RandomAccessible. It is up to the developer to ensure that these + * coordinates match. + * + * Note that both, input and output could be Views, Img or anything that implements + * those interfaces. + * + * @param source - a RandomAccess as source that can be infinite + * @param target - an IterableInterval as target + */ + public static < T extends Type< T >> void copy(final RandomAccessible< T > source, + final IterableInterval< T > target ) + { + // create a cursor that automatically localizes itself on every move + Cursor< T > targetCursor = target.localizingCursor(); + RandomAccess< T > sourceRandomAccess = source.randomAccess(); + + // iterate over the input cursor + while ( targetCursor.hasNext()) + { + // move input cursor forward + targetCursor.fwd(); + + // set the output cursor to the position of the input cursor + sourceRandomAccess.setPosition( targetCursor ); + + // set the value of this pixel of the output image, every Type supports T.set( T type ) + targetCursor.get().set( sourceRandomAccess.get() ); + } + + } + + /** + * Create a number of n-dimensional random points in a certain interval + * having a random intensity 0...1 + * + * @param interval - the interval in which points are created + * @param numPoints - the amount of points + * + * @return a RealPointSampleList (which is an IterableRealInterval) + */ + public static RealPointSampleList< FloatType > createRandomPoints( + RealInterval interval, int numPoints ) + { + // the number of dimensions + int numDimensions = interval.numDimensions(); + + // a random number generator + Random rnd = new Random( 2001);//System.currentTimeMillis() ); + + // a list of Samples with coordinates + RealPointSampleList< FloatType > elements = + new RealPointSampleList<>( numDimensions ); + + for ( int i = 0; i < numPoints; ++i ) + { + RealPoint point = new RealPoint( numDimensions ); + + for ( int d = 0; d < numDimensions; ++d ) + point.setPosition( rnd.nextDouble() * + ( interval.realMax( d ) - interval.realMin( d ) ) + interval.realMin( d ), d ); + + // add a new element with a random intensity in the range 0...1 + elements.add( point, new FloatType( rnd.nextFloat()*255 ) ); + } + + return elements; + } + + +} diff --git a/src/main/java/sc/fiji/bdvpg/source/importer/samples/Wave3DSourceGetter.java b/src/main/java/sc/fiji/bdvpg/source/importer/samples/Wave3DSourceGetter.java new file mode 100644 index 00000000..6cd554fa --- /dev/null +++ b/src/main/java/sc/fiji/bdvpg/source/importer/samples/Wave3DSourceGetter.java @@ -0,0 +1,20 @@ +package sc.fiji.bdvpg.source.importer.samples; + +import bdv.viewer.Source; + +import java.util.function.Supplier; + +public class Wave3DSourceGetter implements Runnable, Supplier { + + @Override + public void run() { + // Useless + } + + @Override + public Source get() { + return new Procedural3DImageShort( + p -> (int) ((Math.sin(p[0]/20)*Math.sin(p[1]/40)*Math.sin(p[2]/5)+1)*100) + ).getSource("Wave 3D"); + } +}