Skip to content

Commit

Permalink
Choose appropriate screen shot output datatype, #1191
Browse files Browse the repository at this point in the history
  • Loading branch information
tischi committed Nov 26, 2024
1 parent 67a4870 commit c3e0b5c
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import sc.fiji.bdvpg.bdv.BdvHandleHelper;
import sc.fiji.bdvpg.bdv.navigate.ViewerTransformAdjuster;
import sc.fiji.bdvpg.bdv.navigate.ViewerTransformChanger;
import sc.fiji.bdvpg.scijava.command.BdvPlaygroundActionCommand;

import java.util.ArrayList;
Expand All @@ -60,8 +58,8 @@ public class ScreenShotMakerCommand extends DynamicCommand implements BdvPlaygro
persist = false,
callback = "showNumPixels",
min = "0.0",
style="format:#.00",
stepSize = "0.01")
style="format:#.000",
stepSize = "0.001")
public Double targetSamplingInXY = 1D;

@Parameter(label="Pixel unit", persist = false, choices = {"micrometer"} )
Expand Down Expand Up @@ -95,10 +93,16 @@ public void initialize() {

// init screenshot sampling
//
final MutableModuleItem< Double > targetSamplingItem = //
final MutableModuleItem< Double > targetSamplingItem =
getInfo().getMutableInput("targetSamplingInXY", Double.class);
targetSamplingItem.setValue( this, getTargetSampling() );
}

protected double getTargetSampling()
{
double viewerVoxelSpacing = BdvHandleHelper.getViewerVoxelSpacing( bdvHandle );
targetSamplingItem.setValue( this, 2 * viewerVoxelSpacing );
double targetSampling = 2 * viewerVoxelSpacing;
return targetSampling;
}

// callback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.embl.mobie.MoBIE;
import org.embl.mobie.command.CommandConstants;
import org.embl.mobie.lib.bdv.ScreenShotMaker;
import org.scijava.module.MutableModuleItem;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import sc.fiji.bdvpg.bdv.BdvHandleHelper;
Expand All @@ -51,14 +52,13 @@ public class ScreenShotStackMakerCommand extends ScreenShotMakerCommand
static { net.imagej.patcher.LegacyInjector.preinit(); }

@Parameter(label="Slice distance (in above units)",
persist = true,
persist = false,
min = "0.0",
style="format:#.00",
stepSize = "0.01")
public Double physicalSliceDistance = 1D;
style="format:#.000",
stepSize = "0.001")
public Double targetSamplingInZ = 1D;

@Parameter(label="Number of slices ab" +
"ove & below current",
@Parameter(label="Number of slices above & below current",
description = "For example, entering 5 here will result in:\n5 above + 1 current + 5 below = 11 slices in total.",
persist = false)
public Integer numSlices = 5;
Expand All @@ -84,7 +84,7 @@ public void run()
System.out.println( screenToPhysicalScale );

// move viewer to starting point
viewerTransform.translate( 0, 0, - numSlices * physicalSliceDistance * screenToPhysicalScale );
viewerTransform.translate( 0, 0, - numSlices * targetSamplingInZ * screenToPhysicalScale );
bdvHandle.getViewerPanel().state().setViewerTransform( viewerTransform );
bdvHandle.getViewerPanel().requestRepaint();

Expand All @@ -97,7 +97,7 @@ public void run()
for ( int sliceIndex = 0; sliceIndex < numSlices; sliceIndex++ )
{
// adapt viewer transform
viewerTransform.translate( 0, 0, physicalSliceDistance * screenToPhysicalScale );
viewerTransform.translate( 0, 0, targetSamplingInZ * screenToPhysicalScale );
bdvHandle.getViewerPanel().state().setViewerTransform( viewerTransform );
bdvHandle.getViewerPanel().requestRepaint();

Expand Down Expand Up @@ -131,7 +131,7 @@ public void run()
bdvHandle.getViewerPanel().state().setViewerTransform( initialViewerTransform );
bdvHandle.getViewerPanel().requestRepaint();

calibration.pixelDepth = physicalSliceDistance;
calibration.pixelDepth = targetSamplingInZ;

ImagePlus rgbImage = new ImagePlus( "RGB stack", rgbStack );
rgbImage.setDimensions( 1, rgbStack.size(), 1 );
Expand All @@ -143,4 +143,14 @@ public void run()
compositeImage.setCalibration( calibration );
compositeImage.show();
}

@Override
public void initialize()
{
super.initialize();

final MutableModuleItem< Double > targetSamplingItem =
getInfo().getMutableInput("targetSamplingInZ", Double.class);
targetSamplingItem.setValue( this, getTargetSampling() );
}
}
108 changes: 77 additions & 31 deletions src/main/java/org/embl/mobie/lib/bdv/ScreenShotMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@
import ij.process.LUT;
import net.imglib2.*;
import net.imglib2.Cursor;
import net.imglib2.ops.parse.token.Real;
import net.imglib2.roi.geom.real.WritableBox;
import net.imglib2.type.Type;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.type.numeric.integer.ByteType;
import net.imglib2.type.numeric.integer.ShortType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.util.Util;
import org.embl.mobie.lib.util.MoBIEHelper;
import org.embl.mobie.lib.util.ThreadHelper;
import org.embl.mobie.lib.annotation.Annotation;
Expand All @@ -65,13 +72,15 @@
import org.embl.mobie.lib.source.SourceHelper;
import sc.fiji.bdvpg.bdv.BdvHandleHelper;
import sc.fiji.bdvpg.services.SourceAndConverterServices;
import sc.fiji.bdvpg.sourceandconverter.SourceAndConverterHelper;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static sc.fiji.bdvpg.bdv.BdvHandleHelper.getLevel;
import static sc.fiji.bdvpg.bdv.BdvHandleHelper.getViewerVoxelSpacing;
Expand Down Expand Up @@ -135,7 +144,7 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing

IJ.log( "Fetching data from " + sacs.size() + " image(s)..." );

final ArrayList< RandomAccessibleInterval< FloatType > > floatCaptures = new ArrayList<>();
final ArrayList< RandomAccessibleInterval< ? extends RealType< ? > > > realCaptures = new ArrayList<>();
final ArrayList< RandomAccessibleInterval< BitType > > maskCaptures = new ArrayList<>();
final ArrayList< RandomAccessibleInterval< ARGBType > > argbCaptures = new ArrayList<>();

Expand All @@ -152,13 +161,42 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing

// IJ.log( ThreadHelper.getNumIoThreads() + " threads working on blocks of " + Arrays.toString( blockSize ) );
final long currentTimeMillis = System.currentTimeMillis();

List< ? > types = sacs.stream()
.map( sac -> Util.getTypeFromInterval( sac.getSpimSource().getSource( 0, 0 ) ) )
.collect( Collectors.toList() );

boolean allByte = sacs.stream()
.map( sac -> Util.getTypeFromInterval( sac.getSpimSource().getSource( 0, 0 ) ) )
.allMatch( t -> t instanceof UnsignedByteType );

boolean allShort = sacs.stream()
.map( sac -> Util.getTypeFromInterval( sac.getSpimSource().getSource( 0, 0 ) ) )
.allMatch( t -> t instanceof UnsignedShortType );

for ( SourceAndConverter< ? > sac : sacs )
{
final RandomAccessibleInterval< FloatType > floatCapture
= ArrayImgs.floats( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );
final RandomAccessibleInterval< BitType > maskCapture
RandomAccessibleInterval< ? extends RealType< ? > > realRAI;

if ( allByte )
{
// ImageJ 8-bit
realRAI = ArrayImgs.unsignedBytes( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );
}
else if ( allShort )
{
// ImageJ 16-bit
realRAI = ArrayImgs.unsignedShorts( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );
}
else
{
// ImageJ 32-bit
realRAI = ArrayImgs.floats( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );
}

final RandomAccessibleInterval< BitType > maskRAI
= ArrayImgs.bits( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );
final RandomAccessibleInterval< ARGBType > argbCapture
final RandomAccessibleInterval< ARGBType > argbRAI
= ArrayImgs.argbs( screenshotDimensions[ 0 ], screenshotDimensions[ 1 ] );

Source< ? > source = sac.getSpimSource();
Expand Down Expand Up @@ -188,30 +226,30 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing
//RandomAccessibleInterval< ? > sourceInterval = source.getSource( currentTimepoint, level );

// to collect raw data
final IntervalView< FloatType > floatCrop = Views.interval( floatCapture, interval );
final Cursor< FloatType > floatCursor = Views.iterable( floatCrop ).localizingCursor();
final RandomAccess< FloatType > floatAccess = floatCrop.randomAccess();
final IntervalView< ? extends RealType< ? > > realCrop = Views.interval( realRAI, interval );
final Cursor< ? extends RealType< ? > > targetCursor = Views.iterable( realCrop ).localizingCursor();
final RandomAccess< ? extends RealType< ? > > targetAccess = realCrop.randomAccess();

// to collect masks
final RandomAccess< BitType > maskAccess = Views.interval( maskCapture, interval ).randomAccess();
final RandomAccess< BitType > maskAccess = Views.interval( maskRAI, interval ).randomAccess();

// to collect colored data
final RandomAccess< ARGBType > argbAccess = Views.interval( argbCapture, interval ).randomAccess();
final RandomAccess< ARGBType > argbAccess = Views.interval( argbRAI, interval ).randomAccess();

final double[] canvasPosition = new double[ 3 ];
final double[] sourceRealPosition = new double[ 3 ];

final ARGBType argbType = new ARGBType();

// iterate through the target image in pixel units
while ( floatCursor.hasNext() )
while ( targetCursor.hasNext() )
{
// set the positions
floatCursor.fwd();
floatCursor.localize( canvasPosition );
floatAccess.setPosition( floatCursor );
maskAccess.setPosition( floatCursor );
argbAccess.setPosition( floatCursor );
targetCursor.fwd();
targetCursor.localize( canvasPosition );
targetAccess.setPosition( targetCursor );
maskAccess.setPosition( targetCursor );
argbAccess.setPosition( targetCursor );
targetCanvasToSourceTransform.apply( canvasPosition, sourceRealPosition );
sourceAccess.setPosition( sourceRealPosition );

Expand All @@ -220,7 +258,7 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing
if ( sourceMask.test( new RealPoint( sourceRealPosition ) ) )
{
maskAccess.get().set( true );
setFloatPixelValue( sourceAccess, floatAccess );
setPixelValue( sourceAccess, targetAccess );
setArgbPixelValue( converter, sourceAccess, argbAccess, argbType );
}
else
Expand Down Expand Up @@ -251,9 +289,9 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing

ThreadHelper.waitUntilFinished( futures );

floatCaptures.add( floatCapture );
maskCaptures.add( maskCapture );
argbCaptures.add( argbCapture );
realCaptures.add( realRAI );
maskCaptures.add( maskRAI );
argbCaptures.add( argbRAI );
displayRanges.add( displayRange );
}

Expand All @@ -262,13 +300,17 @@ public void run( List< SourceAndConverter< ? > > sacs, double targetVoxelSpacing
final double[] voxelSpacing = new double[ 3 ];
Arrays.fill( voxelSpacing, targetVoxelSpacing );

if ( ! floatCaptures.isEmpty() )
if ( ! realCaptures.isEmpty() )
{
rgbImagePlus = createRGBImagePlus( voxelUnit, argbCaptures, voxelSpacing, sacs );

// TODO: not could return multiple images here, one per sac,
// this would also help with the datatype
// one has to think about the pros and cons of having them in one image...
compositeImagePlus = createCompositeImagePlus(
voxelSpacing,
voxelUnit,
floatCaptures,
realCaptures,
maskCaptures,
displayRanges );
}
Expand All @@ -291,20 +333,23 @@ private void setArgbPixelValue( Converter converter, RealRandomAccess< ? > acces
argbCaptureAccess.get().set( argbType.get() );
}

private void setFloatPixelValue( RealRandomAccess< ? extends Type< ? > > access, RandomAccess< FloatType > floatCaptureAccess )
private void setPixelValue(
RealRandomAccess< ? extends Type< ? > > sourceAccess,
RandomAccess< ? extends RealType< ? > > targetAccess )
{
final Type< ? > type = access.get();
final Type< ? > type = sourceAccess.get();
if ( type instanceof RealType )
{
floatCaptureAccess.get().setReal( ( ( RealType ) type ).getRealDouble() );
double realDouble = ( ( RealType ) type ).getRealDouble();
targetAccess.get().setReal( realDouble );
}
else if ( type instanceof AnnotationType )
{
try
{
final Annotation annotation = ( Annotation ) ( ( AnnotationType< ? > ) type ).getAnnotation();
if ( annotation != null )
floatCaptureAccess.get().setReal( annotation.label() );
targetAccess.get().setReal( annotation.label() );
}
catch ( Exception e )
{
Expand Down Expand Up @@ -417,19 +462,20 @@ private ImagePlus asImagePlus( RandomAccessibleInterval< ARGBType > argbCapture,
public static CompositeImage createCompositeImagePlus(
double[] voxelSpacing,
String voxelUnit,
ArrayList< RandomAccessibleInterval< FloatType > > floatCaptures,
ArrayList< RandomAccessibleInterval< BitType > > maskCaptures,
ArrayList< RandomAccessibleInterval< ? extends RealType< ? > > > realRAIs,
ArrayList< RandomAccessibleInterval< BitType > > maskRAIs,
ArrayList< double[] > displayRanges )
{
final ImagePlus imp = ImageJFunctions.wrap( Views.stack( floatCaptures ), "Floats" );
final ImagePlus mask = ImageJFunctions.wrap( Views.stack( maskCaptures ), "Masks" );

final ImagePlus imp = ImageJFunctions.wrap( Views.stack( (ArrayList) realRAIs ), "Floats" );
final ImagePlus mask = ImageJFunctions.wrap( Views.stack( maskRAIs ), "Masks" );

// duplicate: otherwise it is virtual and cannot be modified
final ImagePlus dup = new Duplicator().run( imp );

IJ.run( dup,
"Properties...",
"channels="+floatCaptures.size()
"channels="+realRAIs.size()
+" slices=1 frames=1 unit=" + voxelUnit
+" pixel_width=" + voxelSpacing[ 0 ]
+" pixel_height=" + voxelSpacing[ 1 ]
Expand Down

0 comments on commit c3e0b5c

Please sign in to comment.