Mercurial > repos > perssond > s3segmenter
comparison save_tifffile_pyramid.py @ 1:41e8efe8df43 draft
"planemo upload for repository https://github.com/ohsu-comp-bio/S3segmenter commit c8f72e04db2cc6cc26f0359d5aa3b1a972bc6d53"
author | watsocam |
---|---|
date | Fri, 11 Mar 2022 23:37:49 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:37acf42a824b | 1:41e8efe8df43 |
---|---|
1 import numpy as np | |
2 import tifffile | |
3 import skimage.transform | |
4 | |
5 PHYSICAL_SIZE_UNIT = ['Ym', 'Zm', 'Em', 'Pm', 'Tm', 'Gm', 'Mm', 'km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm', 'µm', 'nm', 'pm', 'fm', 'am', 'zm', 'ym', 'Å', 'thou', 'li', 'in', 'ft', 'yd', 'mi', 'ua', 'ly', 'pc', 'pt', 'pixel', 'reference frame'] | |
6 | |
7 def normalize_image_shape(img): | |
8 assert img.ndim in (2, 3), ( | |
9 'image must be 2D (Y, X) or 3D (C, Y, X)' | |
10 ) | |
11 if img.ndim == 2: | |
12 img = img.reshape(1, *img.shape) | |
13 assert np.argmin(img.shape) == 0, ( | |
14 '3D image must be in (C, Y, X) order' | |
15 ) | |
16 return img | |
17 | |
18 def save_pyramid( | |
19 out_img, output_path, | |
20 pixel_sizes=(1, 1), | |
21 pixel_size_units=('µm', 'µm'), | |
22 channel_names=None, | |
23 software=None, | |
24 is_mask=False | |
25 ): | |
26 assert '.ome.tif' in str(output_path) | |
27 assert len(pixel_sizes) == len(pixel_size_units) == 2 | |
28 assert out_img.ndim in (2, 3), ( | |
29 'image must be either 2D (Y, X) or 3D (C, Y, X)' | |
30 ) | |
31 | |
32 img_shape_ori = out_img.shape | |
33 out_img = normalize_image_shape(out_img) | |
34 img_shape = out_img.shape | |
35 | |
36 size_x, size_y = np.array(pixel_sizes, dtype=float) | |
37 unit_x, unit_y = pixel_size_units | |
38 | |
39 assert (unit_x in PHYSICAL_SIZE_UNIT) & (unit_y in PHYSICAL_SIZE_UNIT), ( | |
40 f'pixel_size_units must be a tuple of the followings: ' | |
41 f'{", ".join(PHYSICAL_SIZE_UNIT)}' | |
42 ) | |
43 | |
44 n_channels = img_shape[0] | |
45 if channel_names == None: | |
46 channel_names = [f'Channel {i}' for i in range(n_channels)] | |
47 else: | |
48 if type(channel_names) == str: | |
49 channel_names = [channel_names] | |
50 n_channel_names = len(channel_names) | |
51 assert n_channel_names == n_channels, ( | |
52 f'number of channel_names ({n_channel_names}) must match ' | |
53 f'number of channels ({n_channels})' | |
54 ) | |
55 | |
56 if software == None: | |
57 software = '' | |
58 | |
59 metadata = { | |
60 'Creator': software, | |
61 'Pixels': { | |
62 'PhysicalSizeX': size_x, | |
63 'PhysicalSizeXUnit': unit_x, | |
64 'PhysicalSizeY': size_y, | |
65 'PhysicalSizeYUnit': unit_y, | |
66 }, | |
67 'Channel': {'Name': channel_names}, | |
68 | |
69 } | |
70 | |
71 max_size = np.max(img_shape) | |
72 subifds = np.ceil(np.log2(max_size / 1024)).astype(int) | |
73 | |
74 # use optimal tile size for disk space | |
75 tile_size = 16*np.ceil( | |
76 np.array(img_shape[1:]) / (2**subifds) / 16 | |
77 ).astype(int) | |
78 options = { | |
79 'tile': tuple(tile_size) | |
80 } | |
81 | |
82 with tifffile.TiffWriter(output_path, bigtiff=True) as tiff_out: | |
83 tiff_out.write( | |
84 data=out_img, | |
85 metadata=metadata, | |
86 software=software, | |
87 subifds=subifds, | |
88 **options | |
89 ) | |
90 for i in range(subifds): | |
91 if i == 0: | |
92 down_2x_img = downsize_img_channels(out_img, is_mask=is_mask) | |
93 else: | |
94 down_2x_img = downsize_img_channels(down_2x_img, is_mask=is_mask) | |
95 tiff_out.write( | |
96 data=down_2x_img, | |
97 subfiletype=1, | |
98 **options | |
99 ) | |
100 | |
101 out_img = out_img.reshape(img_shape_ori) | |
102 return | |
103 | |
104 def downsize_channel(img, is_mask): | |
105 if is_mask: | |
106 return img[::2, ::2] | |
107 else: | |
108 return skimage.transform.downscale_local_mean(img, (2, 2)).astype(img.dtype) | |
109 | |
110 def downsize_img_channels(img, is_mask): | |
111 return np.array([ | |
112 downsize_channel(c, is_mask=is_mask) | |
113 for c in img | |
114 ]) |