diff --git a/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java b/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java index 003cac1..3e68465 100644 --- a/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java +++ b/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java @@ -6,6 +6,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -45,8 +46,8 @@ public static enum SourceType { IMAGEPLUS, DATASET, URL }; private Component container; - public BigWarpSourceTableModel() { - + public BigWarpSourceTableModel() + { this(null); } diff --git a/src/main/java/bdv/ij/ApplyBigwarpPlugin.java b/src/main/java/bdv/ij/ApplyBigwarpPlugin.java index ed2989d..390095b 100644 --- a/src/main/java/bdv/ij/ApplyBigwarpPlugin.java +++ b/src/main/java/bdv/ij/ApplyBigwarpPlugin.java @@ -85,9 +85,7 @@ import net.imglib2.view.Views; /** - * * Apply a bigwarp transform to a 2d or 3d ImagePlus - * */ public class ApplyBigwarpPlugin implements PlugIn { @@ -825,18 +823,12 @@ public static List apply( final boolean wait, final WriteDestinationOptions writeOpts) { -// int numChannels = bwData.movingSourceIndexList.size(); final int numChannels = bwData.numMovingSources(); -// final List< SourceAndConverter< T >> sourcesxfm = BigWarp.wrapSourcesAsTransformed( -// bwData.sourceInfos, -// landmarks.getNumdims(), -// bwData ); final InvertibleRealTransform invXfm = new BigWarpTransform( landmarks, tranformTypeOption ).getTransformation(); for ( int i = 0; i < numChannels; i++ ) { final SourceAndConverter< T > movingSource = bwData.getMovingSource( i ); -// final int originalIdx = bwData.sources.indexOf(movingSource); ((WarpedSource)(movingSource.getSpimSource())).updateTransform(invXfm); ((WarpedSource)(movingSource.getSpimSource())).setIsTransformed(true); } @@ -858,12 +850,13 @@ public static List apply( if( writeOpts != null && writeOpts.n5Dataset != null && !writeOpts.n5Dataset.isEmpty()) { + final SourceAndConverter src = bwData.getMovingSource(0); final String unit = ApplyBigwarpPlugin.getUnit( bwData, resolutionOption ); - runN5Export( bwData, bwData.sources, fieldOfViewOption, + runN5Export( bwData, src, fieldOfViewOption, outputIntervalList.get( 0 ), interp, offset, res, unit, progressWriter, writeOpts, - Executors.newFixedThreadPool( nThreads ) ); + Executors.newFixedThreadPool(nThreads)); return null; } else @@ -959,6 +952,7 @@ private static double[] physicalOffsetFromPixelInterval(final RealInterval inter return out; } + @Deprecated public static & NumericType> void runN5Export( final BigWarpData data, final List< SourceAndConverter< S >> sources, @@ -975,8 +969,12 @@ public static & NumericType> void runN5Export( final int nd = BigWarp.detectNumDims( data.sources ); final double[] resolution = limit(nd,resolutionArg); -// final double[] offset = limit(nd,offsetArg); - final double[] offset = physicalOffsetFromPixelInterval(outputInterval, resolution); + final double[] offset = ApplyBigwarpPlugin.getPixelOffset( fieldOfViewOption, offsetArg, resolution, + outputInterval); + + System.out.println("resolution: " + Arrays.toString(resolution)); + System.out.println("offset : " + Arrays.toString(offset)); + System.out.println("interval : " + Intervals.toString(outputInterval)); // setup n5 parameters final String dataset = writeOpts.n5Dataset; @@ -1074,6 +1072,121 @@ public static & NumericType> void runN5Export( progressWriter.setProgress( 1.0 ); } + public static & NumericType> void runN5Export( + final BigWarpData data, + final SourceAndConverter sourceAndConverter, + final String fieldOfViewOption, + final Interval outputInterval, + final Interpolation interp, + final double[] offsetArg, + final double[] resolutionArg, + final String unit, + final ProgressWriter progressWriter, + final WriteDestinationOptions writeOpts, + final ExecutorService exec ) + { + + final int nd = BigWarp.detectNumDims( data.sources ); + final double[] resolution = limit(nd,resolutionArg); + + // pixel offset + final double[] offsetPixel = ApplyBigwarpPlugin.getPixelOffset( fieldOfViewOption, offsetArg, resolution, + outputInterval); + + // setup n5 parameters + final String dataset = writeOpts.n5Dataset; + final int[] blockSize = writeOpts.blockSize; + final Compression compression = writeOpts.compression; + if( dataset == null || dataset.isEmpty() ) + { + System.err.println("Problem with n5 dataset path: " + dataset); + return; + } + N5Writer n5; + try + { + n5 = new N5Factory().openWriter( writeOpts.pathOrN5Root ); + } + catch ( final RuntimeException e1 ) + { + System.err.println("Could not create n5 writer for: " + writeOpts.pathOrN5Root); + e1.printStackTrace(); + return; + } + + // build metadata + final OmeNgffMetadataParser parser = new OmeNgffMetadataParser(); + final String[] axesLabels = nd == 2 ? new String[]{"x", "y"} : new String[]{"x", "y", "z"}; + final Axis[] axes = new Axis[nd]; + for (int i = 0; i < nd; i++) + axes[i] = new Axis(Axis.SPACE, axesLabels[i], unit); + + // setup physical to pixel transform + final AffineTransform3D resolutionTransform = new AffineTransform3D(); + resolutionTransform.set( resolution[ 0 ], 0, 0 ); + resolutionTransform.set( resolution[ 1 ], 1, 1 ); + + if( resolution.length > 2 ) + resolutionTransform.set( resolution[ 2 ], 2, 2 ); + + final double[] offsetPhysical = new double[resolution.length]; + offsetPhysical[0] = resolution[0] * offsetPixel[0]; + offsetPhysical[1] = resolution[1] * offsetPixel[1]; + + if( resolution.length > 2 ) + offsetPhysical[2] = resolution[2] * offsetPixel[2]; + + final AffineTransform3D offsetTransform = new AffineTransform3D(); + offsetTransform.set( offsetPhysical[ 0 ], 0, 3 ); + offsetTransform.set( offsetPhysical[ 1 ], 1, 3 ); + + if( resolution.length > 2 ) + offsetTransform.set( offsetPhysical[ 2 ], 2, 3 ); + + final AffineTransform3D pixelRenderToPhysical = new AffineTransform3D(); + pixelRenderToPhysical.concatenate( resolutionTransform ); + pixelRenderToPhysical.concatenate( offsetTransform ); + + // render and write + final String srcName = sourceAndConverter.getSpimSource().getName(); + final BigWarpExporter exporter = BigWarpExporter.getExporter( data, + sourceAndConverter, interp, progressWriter ); + exporter.setRenderResolution( resolution ); + exporter.setOffset( offsetPixel ); + exporter.setInterval(Intervals.zeroMin(outputInterval)); + exporter.setSingleChannelNoStack(true); + final RandomAccessibleInterval imgExp = (RandomAccessibleInterval)exporter.exportRai((Source)sourceAndConverter.getSpimSource()); + final IntervalView img = Views.translateInverse( imgExp, Intervals.minAsLongArray( imgExp )); + + RandomAccessibleInterval imgToWrite; + if( nd == 2 ) + imgToWrite = Views.hyperSlice( img, 2, 0 ); + else + imgToWrite = img; + + final String destDataset = dataset; + + final OmeNgffMetadata metadata = OmeNgffMetadata.buildForWriting(nd, srcName, axes, new String[]{"s0"}, + new double[][]{resolution}, new double[][]{offsetPhysical}); + + try + { + N5Utils.save( imgToWrite, n5, destDataset + "/s0", blockSize, compression, exec ); + if( parser != null && metadata != null ) + parser.writeMetadata( metadata, n5, destDataset ); + + n5.close(); + } + catch ( final Exception e ) + { + e.printStackTrace(); + } + + + progressWriter.setProgress( 1.0 ); + System.out.println("done"); + } + @Override public void run( final String arg ) { diff --git a/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java b/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java index 8d7902e..aaa225d 100644 --- a/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java +++ b/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java @@ -489,8 +489,10 @@ public static void writeTpsN5( final String mvgSpaceName = data != null && data.numMovingSources() > 0 ? data.getMovingSource( 0 ).getSpimSource().getName() : "moving"; final String tgtSpaceName = data != null && data.numTargetSources() > 0 ? data.getTargetSource( 0 ).getSpimSource().getName() : "target"; - final String input= mvgSpaceName; - final String output= tgtSpaceName; + + // the TPS is an "inverse" transform from target space to moving space + final String input = tgtSpaceName; + final String output = mvgSpaceName; final String name = input + " to " + output; final String dataset = (n5Dataset == null) ? "" : n5Dataset; diff --git a/src/main/java/bigwarp/BigWarp.java b/src/main/java/bigwarp/BigWarp.java index 10f7357..a547e6c 100755 --- a/src/main/java/bigwarp/BigWarp.java +++ b/src/main/java/bigwarp/BigWarp.java @@ -1546,24 +1546,28 @@ public void exportAsImagePlus( boolean virtual, String path ) { if( writeOpts.n5Dataset != null && !writeOpts.n5Dataset.isEmpty()) { + @SuppressWarnings("rawtypes") + final SourceAndConverter activeSource = getCurrentSourceInActiveViewer(); + final String unit = ApplyBigwarpPlugin.getUnit( data, resolutionOption ); - // export async new Thread() { + @SuppressWarnings("unchecked") @Override public void run() { progressWriter.setProgress( 0.01 ); - ApplyBigwarpPlugin.runN5Export( data, data.sources, fieldOfViewOption, - outputIntervalList.get( 0 ), interp, + ApplyBigwarpPlugin.runN5Export( data, activeSource, + fieldOfViewOption, + outputIntervalList.get(0), interp, offsetSpec, res, unit, progressWriter, writeOpts, - Executors.newFixedThreadPool( nThreads ) ); + Executors.newFixedThreadPool( nThreads )); progressWriter.setProgress( 1.00 ); } }.start(); - } + } else { // export @@ -1575,6 +1579,12 @@ public void run() } } } + + private SourceAndConverter getCurrentSourceInActiveViewer() { + + BigWarpViewerFrame activeFrame = viewerFrameP.isActive() ? viewerFrameP : viewerFrameQ; + return activeFrame.getViewerPanel().state().getCurrentSource(); + } public void exportWarpField() { diff --git a/src/main/java/bigwarp/BigWarpARGBExporter.java b/src/main/java/bigwarp/BigWarpARGBExporter.java index e8fcde5..2e623e7 100644 --- a/src/main/java/bigwarp/BigWarpARGBExporter.java +++ b/src/main/java/bigwarp/BigWarpARGBExporter.java @@ -92,42 +92,43 @@ public boolean isRGB() return true; } + @SuppressWarnings("unchecked") @Override public RandomAccessibleInterval< ARGBType > exportRai() { final ArrayList< RandomAccessibleInterval< ARGBType > > raiList = new ArrayList< RandomAccessibleInterval< ARGBType > >(); - buildTotalRenderTransform(); - final AffineTransform3D srcXfm = new AffineTransform3D(); - - final int numChannels = bwData.numMovingSources(); - final VoxelDimensions voxdim = new FinalVoxelDimensions( unit, - resolutionTransform.get( 0, 0 ), - resolutionTransform.get( 1, 1 ), - resolutionTransform.get( 2, 2 )); - for ( int i = 0; i < numChannels; i++ ) { final Source src = bwData.getMovingSource( i ).getSpimSource(); - src.getSourceTransform(0, 0, srcXfm); + raiList.add( (RandomAccessibleInterval)exportRai(src)); + } + final RandomAccessibleInterval< ARGBType > raiStack = Views.stack( raiList ); - // in pixel space - final RealRandomAccessible raiRaw = ( RealRandomAccessible ) src.getInterpolatedSource( 0, 0, interp ); + return raiStack; + } + + @Override + public RandomAccessibleInterval exportRai(Source src) { + + buildTotalRenderTransform(); + final AffineTransform3D srcXfm = new AffineTransform3D(); + src.getSourceTransform(0, 0, srcXfm); - // the transform from world to new pixel coordinates - final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse(); - // but first need to transform from original pixel to world coordinates - pixelToPhysical.concatenate(srcXfm); + // in pixel space + final RealRandomAccessible raiRaw = (RealRandomAccessible)src.getInterpolatedSource(0, 0, interp); - // apply the transformations - final AffineRandomAccessible rai = RealViews.affine(raiRaw, pixelToPhysical); + // the transform from world to new pixel coordinates + final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse(); + // but first need to transform from original pixel to world coordinates + pixelToPhysical.concatenate(srcXfm); - raiList.add( Views.interval( Views.raster( rai ), outputInterval ) ); - } - final RandomAccessibleInterval< ARGBType > raiStack = Views.stack( raiList ); + // apply the transformations + final AffineRandomAccessible rai = RealViews.affine(raiRaw, pixelToPhysical); + + return Views.interval(Views.raster(rai), outputInterval); - return raiStack; } @Override diff --git a/src/main/java/bigwarp/BigWarpExporter.java b/src/main/java/bigwarp/BigWarpExporter.java index 226f8e2..640024e 100644 --- a/src/main/java/bigwarp/BigWarpExporter.java +++ b/src/main/java/bigwarp/BigWarpExporter.java @@ -84,6 +84,8 @@ public abstract class BigWarpExporter protected AffineTransform3D offsetTransform; + protected boolean singleChannelNoStack = false; + protected Interval outputInterval; protected Interpolation interp; @@ -114,57 +116,6 @@ public enum ParallelizationPolicy { private String exportPath; -// public BigWarpExporter( -// final List< SourceAndConverter< T >> sourcesIn, -// final List< ConverterSetup > convSetups, -// final int[] movingSourceIndexList, -// final int[] targetSourceIndexList, -// final Interpolation interp, -// final ProgressWriter progress ) -// { -// this.sources = new ArrayList>(); -// this.convSetups = convSetups; -// for( SourceAndConverter sac : sourcesIn ) -// { -// Source srcCopy = null; -// Source src = sac.getSpimSource(); -// if( src instanceof WarpedSource ) -// { -// WarpedSource ws = (WarpedSource)( sac.getSpimSource() ); -// WarpedSource wsCopy = new WarpedSource<>( ws.getWrappedSource(), ws.getName() ) ; -// -// if( ws.getTransform() != null ) -// { -// wsCopy.updateTransform( ws.getTransform().copy() ); -// wsCopy.setIsTransformed( true ); -// } -// srcCopy = wsCopy; -// } -// else -// srcCopy = src; -// -// SourceAndConverter copy = new SourceAndConverter<>( srcCopy, sac.getConverter() ); -// sources.add( copy ); -// } -// -// this.movingSourceIndexList = movingSourceIndexList; -// this.targetSourceIndexList = targetSourceIndexList; -// -// if( progress == null ) -// this.progress = new ProgressWriterConsole(); -// else -// this.progress = progress; -// -// this.setInterp( interp ); -// -// pixelRenderToPhysical = new AffineTransform3D(); -// resolutionTransform = new AffineTransform3D(); -// offsetTransform = new AffineTransform3D(); -// -// try { -// unit = sources.get( targetSourceIndexList[ 0 ] ).getSpimSource().getVoxelDimensions().unit(); -// } catch( Exception e ) {} -// } public BigWarpExporter( BigWarpData bwData, @@ -210,9 +161,14 @@ public BigWarpExporter( try { unit = bwData.getTargetSource( 0 ).getSpimSource().getVoxelDimensions().unit(); - } catch( final Exception e ) {} + } catch( final Exception e ) { + // if something goes wrong use the units of this source + unit = bwData.getMovingSource(0).getSpimSource().getVoxelDimensions().unit(); + } } + public abstract RandomAccessibleInterval exportRai(Source src); + public abstract RandomAccessibleInterval exportRai(); public abstract ImagePlus export(); @@ -270,6 +226,11 @@ public void setRenderResolution( double... res ) resolutionTransform.set( res[ i ], i, i ); } + public void setSingleChannelNoStack( boolean singleChannelNoStack ) + { + this.singleChannelNoStack = singleChannelNoStack; + } + /** * Set the offset of the output field of view in pixels. * @@ -854,9 +815,9 @@ public ExportThread(BigWarpExporter exporter, final boolean show ) public void run() { try { - //long startTime = System.currentTimeMillis(); + // long startTime = System.currentTimeMillis(); exporter.result = exporter.export(); - //long endTime = System.currentTimeMillis(); + // long endTime = System.currentTimeMillis(); // System.out.println("export took " + (endTime - startTime) + "ms"); if( show ) @@ -891,6 +852,27 @@ public void run() } } } + + @SuppressWarnings("unchecked") + public static BigWarpExporter getExporter( + final BigWarpData bwData, + final SourceAndConverter< T > source, + final Interpolation interp, + final ProgressWriter progressWriter ) + { + final Object baseType = source.getSpimSource().getType(); + if( baseType instanceof RealType ) + return new BigWarpRealExporter( bwData, bwData.converterSetups, interp, (RealType)baseType, progressWriter); + else if ( ARGBType.class.isInstance( baseType ) ) + { + return new BigWarpARGBExporter( (BigWarpData)bwData, bwData.converterSetups, interp, progressWriter ); + } + else + { + System.err.println( "Can't export type " + baseType.getClass() ); + return null; + } + } @SuppressWarnings( { "rawtypes", "unchecked" } ) public static BigWarpExporter getExporter( @@ -919,4 +901,5 @@ else if ( ARGBType.class.isInstance( baseType ) ) return null; } + } diff --git a/src/main/java/bigwarp/BigWarpInit.java b/src/main/java/bigwarp/BigWarpInit.java index 5174c80..fa3db9b 100644 --- a/src/main/java/bigwarp/BigWarpInit.java +++ b/src/main/java/bigwarp/BigWarpInit.java @@ -26,6 +26,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -36,11 +37,13 @@ import org.janelia.saalfeldlab.n5.N5Reader; import org.janelia.saalfeldlab.n5.N5URI; +import org.janelia.saalfeldlab.n5.bdv.N5Viewer; import org.janelia.saalfeldlab.n5.hdf5.N5HDF5Reader; import org.janelia.saalfeldlab.n5.imglib2.N5Utils; import org.janelia.saalfeldlab.n5.metadata.N5ViewerMultichannelMetadata; import org.janelia.saalfeldlab.n5.metadata.imagej.ImagePlusLegacyMetadataParser; import org.janelia.saalfeldlab.n5.metadata.imagej.N5ImagePlusMetadata; +import org.janelia.saalfeldlab.n5.ui.DataSelection; import org.janelia.saalfeldlab.n5.universe.N5DatasetDiscoverer; import org.janelia.saalfeldlab.n5.universe.N5Factory; import org.janelia.saalfeldlab.n5.universe.N5TreeNode; @@ -66,6 +69,7 @@ import bdv.tools.brightness.RealARGBColorConverterSetup; import bdv.tools.brightness.SetupAssignments; import bdv.tools.transformation.TransformedSource; +import bdv.util.BdvOptions; import bdv.util.RandomAccessibleIntervalMipmapSource; import bdv.util.RandomAccessibleIntervalSource; import bdv.util.volatiles.VolatileViews; @@ -681,6 +685,7 @@ public static < T extends NativeType > Source< T > loadN5Source( final String return loadN5Source( n5, n5Dataset, queue ); } + @SuppressWarnings("unchecked") public static < T extends NativeType> Source< T > loadN5Source( final N5Reader n5, final String n5Dataset, final SharedQueue queue ) { @@ -696,7 +701,7 @@ public static < T extends NativeType> Source< T > loadN5Source( final N5Reade if ( meta instanceof MultiscaleMetadata ) { - return openAsSourceMulti( n5, ( MultiscaleMetadata< ? > ) meta, queue, true ); + return (Source)openN5V( n5, ( MultiscaleMetadata< ? > ) meta, queue); } else { @@ -748,6 +753,19 @@ public static < T extends NativeType, M extends N5Metadata > Source< T > open return null; } + public static < T extends NativeType & NumericType> Source< T > openN5V( final N5Reader n5, final MultiscaleMetadata< ? > multiMeta, final SharedQueue sharedQueue ) + { + List> sources = new ArrayList<>(); + List converterSetups = new ArrayList<>(); + try { + N5Viewer.buildN5Sources(n5, new DataSelection(n5, Collections.singletonList(multiMeta)), sharedQueue, converterSetups, sources, BdvOptions.options()); + if( sources.size() > 0 ) + return (Source)sources.get(0).getSpimSource(); + } catch (IOException e) { } + + return null; + } + public static < T extends NativeType > Source< T > openAsSourceMulti( final N5Reader n5, final MultiscaleMetadata< ? > multiMeta, final SharedQueue sharedQueue, final boolean isVolatile ) { final String[] paths = multiMeta.getPaths(); diff --git a/src/main/java/bigwarp/BigWarpRealExporter.java b/src/main/java/bigwarp/BigWarpRealExporter.java index b8c7328..d7f40e5 100644 --- a/src/main/java/bigwarp/BigWarpRealExporter.java +++ b/src/main/java/bigwarp/BigWarpRealExporter.java @@ -130,36 +130,49 @@ public static boolean isTypeListFullyConsistent( List< SourceAndConverter< T return true; } + @SuppressWarnings("unchecked") @Override public RandomAccessibleInterval< T > exportRai() { final ArrayList< RandomAccessibleInterval< T > > raiList = new ArrayList< RandomAccessibleInterval< T > >(); + final int numChannels; + if( singleChannelNoStack ) + numChannels = 1; + else + numChannels = bwData.numMovingSources(); + + for ( int i = 0; i < numChannels; i++ ) + raiList.add( (RandomAccessibleInterval)exportRai( bwData.getMovingSource( i ).getSpimSource())); + + if( singleChannelNoStack ) + return raiList.get(0); + else { + return Views.stack( raiList ); + } + } + + @Override + public RandomAccessibleInterval exportRai(Source src) { + buildTotalRenderTransform(); final AffineTransform3D srcXfm = new AffineTransform3D(); + src.getSourceTransform(0, 0, srcXfm); - final int numChannels = bwData.numMovingSources(); - for ( int i = 0; i < numChannels; i++ ) - { - final Source src = bwData.getMovingSource( i ).getSpimSource(); - src.getSourceTransform(0, 0, srcXfm); + // in pixel space + final RealRandomAccessible raiRaw = (RealRandomAccessible)src.getInterpolatedSource(0, 0, interp); - // in pixel space - final RealRandomAccessible< T > raiRaw = ( RealRandomAccessible< T > ) src.getInterpolatedSource( 0, 0, interp ); + // the transform from world to new pixel coordinates + final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse(); - // the transform from world to new pixel coordinates - final AffineTransform3D pixelToPhysical = pixelRenderToPhysical.copy().inverse(); - // but first need to transform from original pixel to world coordinates - pixelToPhysical.concatenate(srcXfm); + // but first need to transform from original pixel to world coordinates + pixelToPhysical.concatenate(srcXfm); - // apply the transformations - final AffineRandomAccessible rai = RealViews.affine(raiRaw, pixelToPhysical); + // apply the transformations + final AffineRandomAccessible rai = RealViews.affine(raiRaw, pixelToPhysical); - raiList.add( Views.interval( Views.raster( rai ), outputInterval ) ); - } - final RandomAccessibleInterval< T > raiStack = Views.stack( raiList ); + return Views.interval(Views.raster(rai), outputInterval); - return raiStack; } @Override diff --git a/src/main/java/bigwarp/transforms/NgffTransformations.java b/src/main/java/bigwarp/transforms/NgffTransformations.java index d10e0c2..f8626b7 100644 --- a/src/main/java/bigwarp/transforms/NgffTransformations.java +++ b/src/main/java/bigwarp/transforms/NgffTransformations.java @@ -24,6 +24,7 @@ import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.Common; import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.graph.TransformGraph; import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.AffineCoordinateTransform; +import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.AffineCoordinateTransformAdapter; import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.CoordinateFieldCoordinateTransform; import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.CoordinateTransform; import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.CoordinateTransformAdapter; @@ -621,6 +622,7 @@ public static final int[] vectorAxisLastNgff( public static GsonBuilder gsonBuilder() { final GsonBuilder gb = new GsonBuilder(); + gb.registerTypeAdapter(AffineCoordinateTransform.class, new AffineCoordinateTransformAdapter()); gb.registerTypeAdapter(CoordinateTransform.class, new CoordinateTransformAdapter()); return gb; }