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 ])