66import java .util .ArrayList ;
77import java .util .Arrays ;
88import java .util .Collection ;
9+ import java .util .HashMap ;
910import java .util .List ;
11+ import java .util .Map ;
1012import java .util .concurrent .Callable ;
1113import java .util .function .Function ;
1214
4345import net .preibisch .mvrecon .fiji .spimdata .XmlIoSpimData2 ;
4446import net .preibisch .mvrecon .fiji .spimdata .boundingbox .BoundingBox ;
4547import net .preibisch .mvrecon .fiji .spimdata .imgloaders .OMEZarrAttibutes ;
48+ import net .preibisch .mvrecon .fiji .spimdata .imgloaders .AllenOMEZarrLoader .OMEZARREntry ;
4649import net .preibisch .mvrecon .process .export .ExportN5Api ;
4750import net .preibisch .mvrecon .process .interestpointregistration .TransformationTools ;
4851import net .preibisch .mvrecon .process .n5api .N5ApiTools ;
@@ -176,12 +179,6 @@ else if ( numTimepoints > numTimepointsXML )
176179 {
177180 this .xmlOutURI = URITools .toURI ( xmlOutURIString );
178181 System .out .println ( "XML: " + xmlOutURI );
179-
180- if ( storageType == StorageFormat .ZARR )
181- {
182- System .out .println ( "BDV project for OME-ZARR not yet supported (but very soon!)" );
183- return null ;
184- }
185182 }
186183
187184 BoundingBox boundingBox = Import .getBoundingBox ( dataGlobal , viewIdsGlobal , boundingBoxName );
@@ -324,86 +321,20 @@ else if ( storageType == StorageFormat.N5 || storageType == StorageFormat.ZARR )
324321 }
325322
326323 // setup datasets and metadata
327- MultiResolutionLevelInfo [][] mrInfos ;
328-
329- if ( bdv )
330- {
331- System .out .println ( "Creating BDV compatible container at '" + outPathURI + "' ... " );
332-
333- if ( storageType == StorageFormat .N5 )
334- driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/N5" );
335- else
336- driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/HDF5" );
337-
338- driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/OutputXML" , xmlOutURI );
339-
340- final long [] bb = boundingBox .dimensionsAsLongArray ();
341-
342- final ArrayList < ViewSetup > setups = new ArrayList <>();
343- final ArrayList < TimePoint > tps = new ArrayList <>();
344-
345- for ( int t = 0 ; t < numTimepoints ; ++t )
346- tps .add ( new TimePoint ( t ) );
347-
348- // extract the resolution of the s0 export
349- // TODO: this is inaccurate, we should actually estimate it from the final transformn that is applied
350- // TODO: this is a hack (returns 1,1,1) so the export downsampling pyramid is working
351- final VoxelDimensions vx = new FinalVoxelDimensions ( "micrometer" , new double [] { 1 , 1 , 1 } );// dataGlobal.getSequenceDescription().getViewSetupsOrdered().iterator().next().getVoxelSize();
352- final double [] resolutionS0 = OMEZarrAttibutes .getResolutionS0 ( vx , anisotropyFactor , Double .NaN );
353-
354- System .out .println ( "Resolution of level 0: " + Util .printCoordinates ( resolutionS0 ) + " " + "m" ); //vx.unit() might not be OME-ZARR compatiblevx.unit() );
355-
356- final VoxelDimensions vxNew = new FinalVoxelDimensions ( "micrometer" , resolutionS0 );
357-
358- for ( int c = 0 ; c < numChannels ; ++c )
359- {
360- setups .add (
361- new ViewSetup (
362- c ,
363- "setup " + c ,
364- new FinalDimensions ( bb ),
365- vxNew ,
366- new Tile ( 0 ),
367- new Channel ( c , "Channel " + c ),
368- new Angle ( 0 ),
369- new Illumination ( 0 ) ) );
370- }
371-
372- final SpimData2 dataFusion =
373- SpimData2Tools .createNewSpimDataForFusion ( storageType , outPathURI , xmlOutURI , setups , tps );
374-
375- new XmlIoSpimData2 ().save ( dataFusion , xmlOutURI );
376-
377- final Collection <ViewDescription > vds = dataFusion .getSequenceDescription ().getViewDescriptions ().values ();
378-
379- mrInfos = new MultiResolutionLevelInfo [ vds .size () ][];
380-
381- vds .stream ().parallel ().forEach ( vd ->
382- {
383- final int c = vd .getViewSetup ().getChannel ().getId ();
384- final int t = vd .getTimePointId ();
385-
386- if ( storageType == StorageFormat .N5 )
387- {
388- mrInfos [ c + t *c ] = N5ApiTools .setupBdvDatasetsN5 (
389- driverVolumeWriter , vd , dt , bb , compression , blockSize , downsamplings );
390-
391- driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/N5" );
392- }
393- else // HDF5
394- {
395- mrInfos [ c + t *numChannels ] = N5ApiTools .setupBdvDatasetsHDF5 (
396- driverVolumeWriter , vd , dt , bb , compression , blockSize , downsamplings );
397- }
398- });
399-
400- // TODO: set extra attributes to load the state
401- }
402- else if ( storageType == StorageFormat .ZARR ) // OME-Zarr export
324+ MultiResolutionLevelInfo [][] mrInfos = null ;
325+
326+ // OME-Zarr export
327+ // this code needs refactoring some sort of refactoring. When exporting OME-ZARR, we first create the OME-ZARR container,
328+ // and if it is BDV-XML, we only create the XML in the next if statement. If it is N5/HDF5, there
329+ // is code that creates the N5/HDF5 container and the XML in one if statement. The reason is that
330+ // HDF5/N5 containers with XML may be different that OME-ZARR's; they are always the same no matter
331+ // if it is a BDV project or not
332+ if ( storageType == StorageFormat .ZARR )
403333 {
404334 System .out .println ( "Creating 5D OME-ZARR metadata for '" + outPathURI + "' ... " );
405335
406- driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "OME-ZARR" );
336+ if ( !bdv )
337+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "OME-ZARR" );
407338
408339 final long [] dim3d = boundingBox .dimensionsAsLongArray ();
409340
@@ -427,8 +358,10 @@ else if ( storageType == StorageFormat.ZARR ) // OME-Zarr export
427358 blockSize5d , //5d
428359 ds ); // 5d
429360
361+ final MultiResolutionLevelInfo [] mrInfo = mrInfos [ 0 ];
362+
430363 final Function <Integer , AffineTransform3D > levelToMipmapTransform =
431- (level ) -> MipmapTransforms .getMipmapTransformDefault ( mrInfos [ 0 ] [level ].absoluteDownsamplingDouble () );
364+ (level ) -> MipmapTransforms .getMipmapTransformDefault ( mrInfo [level ].absoluteDownsamplingDouble () );
432365
433366 // extract the resolution of the s0 export
434367 // TODO: this is inaccurate, we should actually estimate it from the final transformn that is applied
@@ -455,7 +388,107 @@ else if ( storageType == StorageFormat.ZARR ) // OME-Zarr export
455388 // final GsonBuilder builder = new GsonBuilder().registerTypeAdapter( CoordinateTransformation.class, new CoordinateTransformationAdapter() );
456389 driverVolumeWriter .setAttribute ( "/" , "multiscales" , meta );
457390 }
458- else // simple (no bdv project) HDF5/N5 export
391+
392+ if ( bdv )
393+ {
394+ System .out .println ( "Creating BDV compatible container at '" + outPathURI + "' ... " );
395+
396+ if ( storageType == StorageFormat .N5 )
397+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/N5" );
398+ else if ( storageType == StorageFormat .ZARR )
399+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/OME-ZARR" );
400+ else
401+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/HDF5" );
402+
403+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/OutputXML" , xmlOutURI );
404+
405+ final long [] bb = boundingBox .dimensionsAsLongArray ();
406+
407+ final ArrayList < ViewSetup > setups = new ArrayList <>();
408+ final ArrayList < TimePoint > tps = new ArrayList <>();
409+
410+ for ( int t = 0 ; t < numTimepoints ; ++t )
411+ tps .add ( new TimePoint ( t ) );
412+
413+ // extract the resolution of the s0 export
414+ // TODO: this is inaccurate, we should actually estimate it from the final transformn that is applied
415+ // TODO: this is a hack (returns 1,1,1) so the export downsampling pyramid is working
416+ final VoxelDimensions vx = new FinalVoxelDimensions ( "micrometer" , new double [] { 1 , 1 , 1 } );// dataGlobal.getSequenceDescription().getViewSetupsOrdered().iterator().next().getVoxelSize();
417+ final double [] resolutionS0 = OMEZarrAttibutes .getResolutionS0 ( vx , anisotropyFactor , Double .NaN );
418+
419+ System .out .println ( "Resolution of level 0: " + Util .printCoordinates ( resolutionS0 ) + " " + "m" ); //vx.unit() might not be OME-ZARR compatiblevx.unit() );
420+
421+ final VoxelDimensions vxNew = new FinalVoxelDimensions ( "micrometer" , resolutionS0 );
422+
423+ for ( int c = 0 ; c < numChannels ; ++c )
424+ {
425+ setups .add (
426+ new ViewSetup (
427+ c ,
428+ "setup " + c ,
429+ new FinalDimensions ( bb ),
430+ vxNew ,
431+ new Tile ( 0 ),
432+ new Channel ( c , "Channel " + c ),
433+ new Angle ( 0 ),
434+ new Illumination ( 0 ) ) );
435+ }
436+
437+ final Map < ViewId , OMEZARREntry > viewIdToPath ;
438+
439+ if ( storageType == StorageFormat .ZARR )
440+ {
441+ viewIdToPath = new HashMap <>();
442+
443+ for ( int c = 0 ; c < numChannels ; ++c )
444+ for ( int t = 0 ; t < numTimepoints ; ++t )
445+ {
446+ final OMEZARREntry omeZarrEntry = new OMEZARREntry (
447+ mrInfos [ 0 ][ 0 ].dataset .substring (0 , mrInfos [ 0 ][ 0 ].dataset .lastIndexOf ( "/" ) ),
448+ new int [] { c , t } );
449+
450+ viewIdToPath .put ( new ViewId ( t , c ), omeZarrEntry );
451+ }
452+ }
453+ else
454+ {
455+ viewIdToPath = null ;
456+ }
457+
458+ final SpimData2 dataFusion =
459+ SpimData2Tools .createNewSpimDataForFusion ( storageType , outPathURI , xmlOutURI , viewIdToPath , setups , tps );
460+
461+ new XmlIoSpimData2 ().save ( dataFusion , xmlOutURI );
462+
463+ if ( storageType != StorageFormat .ZARR )
464+ {
465+ final Collection <ViewDescription > vds = dataFusion .getSequenceDescription ().getViewDescriptions ().values ();
466+
467+ mrInfos = new MultiResolutionLevelInfo [ vds .size () ][];
468+ final MultiResolutionLevelInfo myMrInfo [][] = mrInfos ;
469+
470+ vds .stream ().parallel ().forEach ( vd ->
471+ {
472+ final int c = vd .getViewSetup ().getChannel ().getId ();
473+ final int t = vd .getTimePointId ();
474+
475+ if ( storageType == StorageFormat .N5 )
476+ {
477+ myMrInfo [ c + t *c ] = N5ApiTools .setupBdvDatasetsN5 (
478+ driverVolumeWriter , vd , dt , bb , compression , blockSize , downsamplings );
479+
480+ driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/FusionFormat" , "BDV/N5" );
481+ }
482+ else // HDF5
483+ {
484+ myMrInfo [ c + t *numChannels ] = N5ApiTools .setupBdvDatasetsHDF5 (
485+ driverVolumeWriter , vd , dt , bb , compression , blockSize , downsamplings );
486+ }
487+ });
488+ }
489+ // TODO: set extra attributes to load the state
490+ }
491+ else if ( storageType == StorageFormat .N5 || storageType == StorageFormat .HDF5 ) // simple (no bdv project) HDF5/N5 export
459492 {
460493 mrInfos = new MultiResolutionLevelInfo [ numChannels * numTimepoints ][];
461494
@@ -483,7 +516,6 @@ else if ( storageType == StorageFormat.ZARR ) // OME-Zarr export
483516 }
484517 }
485518
486-
487519 // TODO: set extra attributes to load the state
488520 driverVolumeWriter .setAttribute ( "/" , "Bigstitcher-Spark/MultiResolutionInfos" , mrInfos );
489521
@@ -504,4 +536,4 @@ public static void main(final String... args) throws SpimDataException
504536
505537 System .exit (new CommandLine (new CreateFusionContainer ()).execute (args ));
506538 }
507- }
539+ }
0 commit comments