Skip to content

Commit 3da8310

Browse files
authored
Add quantized dfield export and fix n5 export (#172)
* perf(wip): toward export of quantized dfields * perf(wip): toward reading and writing quantized transforms * add n5 "format" to exporter * read from n5 formaat if no fragment given in transform URI * style: code formatting * fix: n5 dfield, treat invfield group as inverse * fix: generic RealComposite compilation issue * chore: bump n5 artifact versions * fix: make n5 source loading consistent with n5 viewer * wip: toward fixing n5 export when an offset exists * fix: serializing ngff affines * fix(wip): toward fixing n5 export * feat(wip): more flexible bigwarp exporter * exporter can output a single rai * when writing to n5 export only active source * fix physical offset for ngff metadata * fix/perf: use N5Viewer method for opening multiscale data * fix: offset typo in ApplyBigWarpPlugin * fix: correct input/output space for TPS transform export * style: ApplyBigwarpPlugin formatting
1 parent 86f04af commit 3da8310

9 files changed

+258
-115
lines changed

src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.net.URI;
77
import java.net.URISyntaxException;
88
import java.util.ArrayList;
9+
import java.util.function.Consumer;
910
import java.util.function.Function;
1011
import java.util.function.Supplier;
1112

@@ -45,8 +46,8 @@ public static enum SourceType { IMAGEPLUS, DATASET, URL };
4546

4647
private Component container;
4748

48-
public BigWarpSourceTableModel() {
49-
49+
public BigWarpSourceTableModel()
50+
{
5051
this(null);
5152
}
5253

src/main/java/bdv/ij/ApplyBigwarpPlugin.java

+125-12
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@
8585
import net.imglib2.view.Views;
8686

8787
/**
88-
*
8988
* Apply a bigwarp transform to a 2d or 3d ImagePlus
90-
*
9189
*/
9290
public class ApplyBigwarpPlugin implements PlugIn
9391
{
@@ -825,18 +823,12 @@ public static <T> List<ImagePlus> apply(
825823
final boolean wait,
826824
final WriteDestinationOptions writeOpts) {
827825

828-
// int numChannels = bwData.movingSourceIndexList.size();
829826
final int numChannels = bwData.numMovingSources();
830-
// final List< SourceAndConverter< T >> sourcesxfm = BigWarp.wrapSourcesAsTransformed(
831-
// bwData.sourceInfos,
832-
// landmarks.getNumdims(),
833-
// bwData );
834827

835828
final InvertibleRealTransform invXfm = new BigWarpTransform( landmarks, tranformTypeOption ).getTransformation();
836829
for ( int i = 0; i < numChannels; i++ )
837830
{
838831
final SourceAndConverter< T > movingSource = bwData.getMovingSource( i );
839-
// final int originalIdx = bwData.sources.indexOf(movingSource);
840832
((WarpedSource<?>)(movingSource.getSpimSource())).updateTransform(invXfm);
841833
((WarpedSource<?>)(movingSource.getSpimSource())).setIsTransformed(true);
842834
}
@@ -858,12 +850,13 @@ public static <T> List<ImagePlus> apply(
858850

859851
if( writeOpts != null && writeOpts.n5Dataset != null && !writeOpts.n5Dataset.isEmpty())
860852
{
853+
final SourceAndConverter<T> src = bwData.getMovingSource(0);
861854
final String unit = ApplyBigwarpPlugin.getUnit( bwData, resolutionOption );
862-
runN5Export( bwData, bwData.sources, fieldOfViewOption,
855+
runN5Export( bwData, src, fieldOfViewOption,
863856
outputIntervalList.get( 0 ), interp,
864857
offset, res, unit,
865858
progressWriter, writeOpts,
866-
Executors.newFixedThreadPool( nThreads ) );
859+
Executors.newFixedThreadPool(nThreads));
867860
return null;
868861
}
869862
else
@@ -959,6 +952,7 @@ private static double[] physicalOffsetFromPixelInterval(final RealInterval inter
959952
return out;
960953
}
961954

955+
@Deprecated
962956
public static <S, T extends NativeType<T> & NumericType<T>> void runN5Export(
963957
final BigWarpData<S> data,
964958
final List< SourceAndConverter< S >> sources,
@@ -975,8 +969,12 @@ public static <S, T extends NativeType<T> & NumericType<T>> void runN5Export(
975969
final int nd = BigWarp.detectNumDims( data.sources );
976970
final double[] resolution = limit(nd,resolutionArg);
977971

978-
// final double[] offset = limit(nd,offsetArg);
979-
final double[] offset = physicalOffsetFromPixelInterval(outputInterval, resolution);
972+
final double[] offset = ApplyBigwarpPlugin.getPixelOffset( fieldOfViewOption, offsetArg, resolution,
973+
outputInterval);
974+
975+
System.out.println("resolution: " + Arrays.toString(resolution));
976+
System.out.println("offset : " + Arrays.toString(offset));
977+
System.out.println("interval : " + Intervals.toString(outputInterval));
980978

981979
// setup n5 parameters
982980
final String dataset = writeOpts.n5Dataset;
@@ -1074,6 +1072,121 @@ public static <S, T extends NativeType<T> & NumericType<T>> void runN5Export(
10741072
progressWriter.setProgress( 1.0 );
10751073
}
10761074

1075+
public static <S,T extends NativeType<T> & NumericType<T>> void runN5Export(
1076+
final BigWarpData<S> data,
1077+
final SourceAndConverter<S> sourceAndConverter,
1078+
final String fieldOfViewOption,
1079+
final Interval outputInterval,
1080+
final Interpolation interp,
1081+
final double[] offsetArg,
1082+
final double[] resolutionArg,
1083+
final String unit,
1084+
final ProgressWriter progressWriter,
1085+
final WriteDestinationOptions writeOpts,
1086+
final ExecutorService exec )
1087+
{
1088+
1089+
final int nd = BigWarp.detectNumDims( data.sources );
1090+
final double[] resolution = limit(nd,resolutionArg);
1091+
1092+
// pixel offset
1093+
final double[] offsetPixel = ApplyBigwarpPlugin.getPixelOffset( fieldOfViewOption, offsetArg, resolution,
1094+
outputInterval);
1095+
1096+
// setup n5 parameters
1097+
final String dataset = writeOpts.n5Dataset;
1098+
final int[] blockSize = writeOpts.blockSize;
1099+
final Compression compression = writeOpts.compression;
1100+
if( dataset == null || dataset.isEmpty() )
1101+
{
1102+
System.err.println("Problem with n5 dataset path: " + dataset);
1103+
return;
1104+
}
1105+
N5Writer n5;
1106+
try
1107+
{
1108+
n5 = new N5Factory().openWriter( writeOpts.pathOrN5Root );
1109+
}
1110+
catch ( final RuntimeException e1 )
1111+
{
1112+
System.err.println("Could not create n5 writer for: " + writeOpts.pathOrN5Root);
1113+
e1.printStackTrace();
1114+
return;
1115+
}
1116+
1117+
// build metadata
1118+
final OmeNgffMetadataParser parser = new OmeNgffMetadataParser();
1119+
final String[] axesLabels = nd == 2 ? new String[]{"x", "y"} : new String[]{"x", "y", "z"};
1120+
final Axis[] axes = new Axis[nd];
1121+
for (int i = 0; i < nd; i++)
1122+
axes[i] = new Axis(Axis.SPACE, axesLabels[i], unit);
1123+
1124+
// setup physical to pixel transform
1125+
final AffineTransform3D resolutionTransform = new AffineTransform3D();
1126+
resolutionTransform.set( resolution[ 0 ], 0, 0 );
1127+
resolutionTransform.set( resolution[ 1 ], 1, 1 );
1128+
1129+
if( resolution.length > 2 )
1130+
resolutionTransform.set( resolution[ 2 ], 2, 2 );
1131+
1132+
final double[] offsetPhysical = new double[resolution.length];
1133+
offsetPhysical[0] = resolution[0] * offsetPixel[0];
1134+
offsetPhysical[1] = resolution[1] * offsetPixel[1];
1135+
1136+
if( resolution.length > 2 )
1137+
offsetPhysical[2] = resolution[2] * offsetPixel[2];
1138+
1139+
final AffineTransform3D offsetTransform = new AffineTransform3D();
1140+
offsetTransform.set( offsetPhysical[ 0 ], 0, 3 );
1141+
offsetTransform.set( offsetPhysical[ 1 ], 1, 3 );
1142+
1143+
if( resolution.length > 2 )
1144+
offsetTransform.set( offsetPhysical[ 2 ], 2, 3 );
1145+
1146+
final AffineTransform3D pixelRenderToPhysical = new AffineTransform3D();
1147+
pixelRenderToPhysical.concatenate( resolutionTransform );
1148+
pixelRenderToPhysical.concatenate( offsetTransform );
1149+
1150+
// render and write
1151+
final String srcName = sourceAndConverter.getSpimSource().getName();
1152+
final BigWarpExporter<?> exporter = BigWarpExporter.getExporter( data,
1153+
sourceAndConverter, interp, progressWriter );
1154+
exporter.setRenderResolution( resolution );
1155+
exporter.setOffset( offsetPixel );
1156+
exporter.setInterval(Intervals.zeroMin(outputInterval));
1157+
exporter.setSingleChannelNoStack(true);
1158+
final RandomAccessibleInterval<T> imgExp = (RandomAccessibleInterval<T>)exporter.exportRai((Source<T>)sourceAndConverter.getSpimSource());
1159+
final IntervalView<T> img = Views.translateInverse( imgExp, Intervals.minAsLongArray( imgExp ));
1160+
1161+
RandomAccessibleInterval<T> imgToWrite;
1162+
if( nd == 2 )
1163+
imgToWrite = Views.hyperSlice( img, 2, 0 );
1164+
else
1165+
imgToWrite = img;
1166+
1167+
final String destDataset = dataset;
1168+
1169+
final OmeNgffMetadata metadata = OmeNgffMetadata.buildForWriting(nd, srcName, axes, new String[]{"s0"},
1170+
new double[][]{resolution}, new double[][]{offsetPhysical});
1171+
1172+
try
1173+
{
1174+
N5Utils.save( imgToWrite, n5, destDataset + "/s0", blockSize, compression, exec );
1175+
if( parser != null && metadata != null )
1176+
parser.writeMetadata( metadata, n5, destDataset );
1177+
1178+
n5.close();
1179+
}
1180+
catch ( final Exception e )
1181+
{
1182+
e.printStackTrace();
1183+
}
1184+
1185+
1186+
progressWriter.setProgress( 1.0 );
1187+
System.out.println("done");
1188+
}
1189+
10771190
@Override
10781191
public void run( final String arg )
10791192
{

src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,10 @@ public static void writeTpsN5(
489489

490490
final String mvgSpaceName = data != null && data.numMovingSources() > 0 ? data.getMovingSource( 0 ).getSpimSource().getName() : "moving";
491491
final String tgtSpaceName = data != null && data.numTargetSources() > 0 ? data.getTargetSource( 0 ).getSpimSource().getName() : "target";
492-
final String input= mvgSpaceName;
493-
final String output= tgtSpaceName;
492+
493+
// the TPS is an "inverse" transform from target space to moving space
494+
final String input = tgtSpaceName;
495+
final String output = mvgSpaceName;
494496
final String name = input + " to " + output;
495497

496498
final String dataset = (n5Dataset == null) ? "" : n5Dataset;

src/main/java/bigwarp/BigWarp.java

+15-5
Original file line numberDiff line numberDiff line change
@@ -1546,24 +1546,28 @@ public void exportAsImagePlus( boolean virtual, String path )
15461546
{
15471547
if( writeOpts.n5Dataset != null && !writeOpts.n5Dataset.isEmpty())
15481548
{
1549+
@SuppressWarnings("rawtypes")
1550+
final SourceAndConverter activeSource = getCurrentSourceInActiveViewer();
1551+
15491552
final String unit = ApplyBigwarpPlugin.getUnit( data, resolutionOption );
1550-
// export async
15511553
new Thread()
15521554
{
1555+
@SuppressWarnings("unchecked")
15531556
@Override
15541557
public void run()
15551558
{
15561559
progressWriter.setProgress( 0.01 );
1557-
ApplyBigwarpPlugin.runN5Export( data, data.sources, fieldOfViewOption,
1558-
outputIntervalList.get( 0 ), interp,
1560+
ApplyBigwarpPlugin.runN5Export( data, activeSource,
1561+
fieldOfViewOption,
1562+
outputIntervalList.get(0), interp,
15591563
offsetSpec, res, unit,
15601564
progressWriter, writeOpts,
1561-
Executors.newFixedThreadPool( nThreads ) );
1565+
Executors.newFixedThreadPool( nThreads ));
15621566

15631567
progressWriter.setProgress( 1.00 );
15641568
}
15651569
}.start();
1566-
}
1570+
}
15671571
else
15681572
{
15691573
// export
@@ -1575,6 +1579,12 @@ public void run()
15751579
}
15761580
}
15771581
}
1582+
1583+
private SourceAndConverter<?> getCurrentSourceInActiveViewer() {
1584+
1585+
BigWarpViewerFrame activeFrame = viewerFrameP.isActive() ? viewerFrameP : viewerFrameQ;
1586+
return activeFrame.getViewerPanel().state().getCurrentSource();
1587+
}
15781588

15791589
public void exportWarpField()
15801590
{

src/main/java/bigwarp/BigWarpARGBExporter.java

+23-22
Original file line numberDiff line numberDiff line change
@@ -92,42 +92,43 @@ public boolean isRGB()
9292
return true;
9393
}
9494

95+
@SuppressWarnings("unchecked")
9596
@Override
9697
public RandomAccessibleInterval< ARGBType > exportRai()
9798
{
9899
final ArrayList< RandomAccessibleInterval< ARGBType > > raiList = new ArrayList< RandomAccessibleInterval< ARGBType > >();
99100

100-
buildTotalRenderTransform();
101-
final AffineTransform3D srcXfm = new AffineTransform3D();
102-
103-
104101
final int numChannels = bwData.numMovingSources();
105-
final VoxelDimensions voxdim = new FinalVoxelDimensions( unit,
106-
resolutionTransform.get( 0, 0 ),
107-
resolutionTransform.get( 1, 1 ),
108-
resolutionTransform.get( 2, 2 ));
109-
110102
for ( int i = 0; i < numChannels; i++ )
111103
{
112104
final Source<ARGBType> src = bwData.getMovingSource( i ).getSpimSource();
113-
src.getSourceTransform(0, 0, srcXfm);
105+
raiList.add( (RandomAccessibleInterval<ARGBType>)exportRai(src));
106+
}
107+
final RandomAccessibleInterval< ARGBType > raiStack = Views.stack( raiList );
114108

115-
// in pixel space
116-
final RealRandomAccessible<ARGBType> raiRaw = ( RealRandomAccessible<ARGBType> ) src.getInterpolatedSource( 0, 0, interp );
109+
return raiStack;
110+
}
111+
112+
@Override
113+
public RandomAccessibleInterval<?> exportRai(Source<?> src) {
114+
115+
buildTotalRenderTransform();
116+
final AffineTransform3D srcXfm = new AffineTransform3D();
117+
src.getSourceTransform(0, 0, srcXfm);
117118

118-
// the transform from world to new pixel coordinates
119-
final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse();
120-
// but first need to transform from original pixel to world coordinates
121-
pixelToPhysical.concatenate(srcXfm);
119+
// in pixel space
120+
final RealRandomAccessible<ARGBType> raiRaw = (RealRandomAccessible<ARGBType>)src.getInterpolatedSource(0, 0, interp);
122121

123-
// apply the transformations
124-
final AffineRandomAccessible<ARGBType, AffineGet> rai = RealViews.affine(raiRaw, pixelToPhysical);
122+
// the transform from world to new pixel coordinates
123+
final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse();
124+
// but first need to transform from original pixel to world coordinates
125+
pixelToPhysical.concatenate(srcXfm);
125126

126-
raiList.add( Views.interval( Views.raster( rai ), outputInterval ) );
127-
}
128-
final RandomAccessibleInterval< ARGBType > raiStack = Views.stack( raiList );
127+
// apply the transformations
128+
final AffineRandomAccessible<ARGBType, AffineGet> rai = RealViews.affine(raiRaw, pixelToPhysical);
129+
130+
return Views.interval(Views.raster(rai), outputInterval);
129131

130-
return raiStack;
131132
}
132133

133134
@Override

0 commit comments

Comments
 (0)