44import itk
55from numcodecs import Blosc
66import zarr
7+ from pathlib import Path
8+ import numpy as np
79
810def compress_encode (input_filepath ,
911 output_directory ,
12+ multiscale = True ,
1013 chunk_size = 64 ,
1114 cname = 'zstd' ,
1215 clevel = 5 ,
1316 shuffle = True ):
1417 image = itk .imread (input_filepath )
1518 image_da = itk .xarray_from_image (image )
16- dataset_name = input_filepath
19+ dataset_name = str ( Path ( input_filepath ))
1720 image_ds = image_da .to_dataset (name = dataset_name )
1821
1922 store_name = output_directory
@@ -31,6 +34,37 @@ def compress_encode(input_filepath,
3134
3235 zarr .consolidate_metadata (store )
3336
37+ if multiscale :
38+ # multi-resolution pyramid
39+ pyramid = [image_da ]
40+ reduced = image
41+ while not np .all (np .array (itk .size (reduced )) < 64 ):
42+ scale = len (pyramid )
43+ shrink_factors = [2 ** scale ]* 3
44+ reduced = itk .bin_shrink_image_filter (image , shrink_factors = shrink_factors )
45+ reduced_da = itk .xarray_from_image (reduced )
46+ pyramid .append (reduced_da )
47+
48+ pyramid_group_paths = ["" ]
49+ for scale in range (1 , len (pyramid )):
50+ pyramid_group_paths .append ('scale_{0}' .format (scale ))
51+
52+ for scale in range (1 , len (pyramid )):
53+ ds = pyramid [scale ].to_dataset (name = dataset_name )
54+ ds .to_zarr (store ,
55+ mode = 'w' ,
56+ group = pyramid_group_paths [scale ],
57+ compute = True ,
58+ encoding = {dataset_name : {'chunks' : [chunk_size ]* 3 , 'compressor' : compressor }})
59+
60+ # Re-consolidate entire dataset
61+ zarr .consolidate_metadata (store )
62+ for scale in range (1 , len (pyramid )):
63+ store = zarr .DirectoryStore (str (Path (store_name ) / pyramid_group_paths [scale ]))
64+ # Also consolidate the metadata on the pyramid scales so they can be used independently
65+ zarr .consolidate_metadata (store )
66+
67+
3468if __name__ == '__main__' :
3569 parser = argparse .ArgumentParser ('Convert and encode a medical image file in a compressed Zarr directory store.' )
3670 parser .add_argument ('input_filepath' , help = 'Path to input image file, e.g. a NIFTI file.' )
@@ -40,11 +74,13 @@ def compress_encode(input_filepath,
4074 parser .add_argument ('--chunk-size' , default = 64 , type = int , help = 'Compression chunk size along one dimension.' )
4175 parser .add_argument ('--cname' , default = 'zstd' , help = 'Base compression codec.' )
4276 parser .add_argument ('--clevel' , default = 5 , type = int , help = 'Compression level.' )
77+ parser .add_argument ('--no-multi-scale' , action = 'store_true' , help = 'Do not generate a multi-scale pyramid.' )
4378
4479 args = parser .parse_args ()
4580
4681 compress_encode (args .input_filepath ,
4782 args .output_directory ,
83+ multiscale = not args .no_multi_scale ,
4884 chunk_size = args .chunk_size ,
4985 cname = args .cname ,
5086 clevel = args .clevel ,
0 commit comments