Skip to content

References

BioSource

javabridge_open = False module-attribute

BioSource

Bases: OmeSource

bioformats compatible image source

Source code in OmeSliCC\BioSource.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class BioSource(OmeSource):
    """bioformats compatible image source"""

    filename: str
    """original filename"""
    indexes: list
    """list of relevant series indexes"""

    def __init__(self,
                 filename: str,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False):

        super().__init__()

        open_javabridge()

        xml_metadata = bioformats.get_omexml_metadata(filename)
        self.bio_ome_metadata = bioformats.OMEXML(xml_metadata)
        self.metadata = XmlDict.xml2dict(xml_metadata)
        if 'OME' in self.metadata:
            self.metadata = self.metadata['OME']
            self.has_ome_metadata = True
        self.reader = ImageReader(filename)

        #self.reader.rdr.getSeriesCount()
        # good images have StageLabel in metadata?
        self.indexes = []
        # TODO: use self.metadata instead of self.bio_ome_metadata
        for i in range(self.bio_ome_metadata.get_image_count()):
            pmetadata = self.bio_ome_metadata.image(i).Pixels
            if pmetadata.PhysicalSizeX is not None:
                dtype = np.dtype(pmetadata.PixelType)
                self.indexes.append(i)
                self.sizes.append((pmetadata.SizeX, pmetadata.SizeY))
                self.sizes_xyzct.append((pmetadata.SizeX, pmetadata.SizeY, pmetadata.SizeZ, pmetadata.SizeC, pmetadata.SizeT))
                self.pixel_types.append(dtype)
                self.pixel_nbits.append(dtype.itemsize * 8)

        self._init_metadata(filename,
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

        self.is_rgb = self.get_nchannels() in (3, 4)

        self.dimension_order = 'yx'
        if self.get_nchannels() > 1:
            self.dimension_order += 'c'

    def _find_metadata(self):
        self._get_ome_metadata()

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
        y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
        c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
        if x1 < 0 or y1 < 0:
            x1, y1 = self.sizes[level]
        if t is None:
            t = 0
        if z is None:
            z = 0
        xywh = (x0, y0, x1 - x0, y1 - y0)
        # don't 'rescale' to 0-1!
        image = self.reader.read(series=self.indexes[level], XYWH=xywh, c=c, z=z, t=t, rescale=False)
        out = redimension_data(image, self.dimension_order, self.get_dimension_order())
        return out

    def close(self):
        self.reader.close()

bio_ome_metadata = bioformats.OMEXML(xml_metadata) instance-attribute

dimension_order = 'yx' instance-attribute

filename instance-attribute

original filename

has_ome_metadata = True instance-attribute

indexes = [] instance-attribute

list of relevant series indexes

is_rgb = self.get_nchannels() in (3, 4) instance-attribute

metadata = XmlDict.xml2dict(xml_metadata) instance-attribute

reader = ImageReader(filename) instance-attribute

__init__(filename, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\BioSource.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(self,
             filename: str,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False):

    super().__init__()

    open_javabridge()

    xml_metadata = bioformats.get_omexml_metadata(filename)
    self.bio_ome_metadata = bioformats.OMEXML(xml_metadata)
    self.metadata = XmlDict.xml2dict(xml_metadata)
    if 'OME' in self.metadata:
        self.metadata = self.metadata['OME']
        self.has_ome_metadata = True
    self.reader = ImageReader(filename)

    #self.reader.rdr.getSeriesCount()
    # good images have StageLabel in metadata?
    self.indexes = []
    # TODO: use self.metadata instead of self.bio_ome_metadata
    for i in range(self.bio_ome_metadata.get_image_count()):
        pmetadata = self.bio_ome_metadata.image(i).Pixels
        if pmetadata.PhysicalSizeX is not None:
            dtype = np.dtype(pmetadata.PixelType)
            self.indexes.append(i)
            self.sizes.append((pmetadata.SizeX, pmetadata.SizeY))
            self.sizes_xyzct.append((pmetadata.SizeX, pmetadata.SizeY, pmetadata.SizeZ, pmetadata.SizeC, pmetadata.SizeT))
            self.pixel_types.append(dtype)
            self.pixel_nbits.append(dtype.itemsize * 8)

    self._init_metadata(filename,
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

    self.is_rgb = self.get_nchannels() in (3, 4)

    self.dimension_order = 'yx'
    if self.get_nchannels() > 1:
        self.dimension_order += 'c'

_asarray_level(level, **slicing)

Source code in OmeSliCC\BioSource.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
    y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
    c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
    if x1 < 0 or y1 < 0:
        x1, y1 = self.sizes[level]
    if t is None:
        t = 0
    if z is None:
        z = 0
    xywh = (x0, y0, x1 - x0, y1 - y0)
    # don't 'rescale' to 0-1!
    image = self.reader.read(series=self.indexes[level], XYWH=xywh, c=c, z=z, t=t, rescale=False)
    out = redimension_data(image, self.dimension_order, self.get_dimension_order())
    return out

_find_metadata()

Source code in OmeSliCC\BioSource.py
62
63
def _find_metadata(self):
    self._get_ome_metadata()

close()

Source code in OmeSliCC\BioSource.py
81
82
def close(self):
    self.reader.close()

close_javabridge()

Source code in OmeSliCC\BioSource.py
103
104
def close_javabridge():
    javabridge.kill_vm()

open_javabridge()

Source code in OmeSliCC\BioSource.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def open_javabridge():
    global javabridge_open

    javabridge.start_vm(class_path=bioformats.JARS)

    rootLoggerName = javabridge.get_static_field("org/slf4j/Logger", "ROOT_LOGGER_NAME", "Ljava/lang/String;")
    rootLogger = javabridge.static_call("org/slf4j/LoggerFactory", "getLogger",
                                        "(Ljava/lang/String;)Lorg/slf4j/Logger;", rootLoggerName)
    logLevel = javabridge.get_static_field("ch/qos/logback/classic/Level", 'WARN',
                                           "Lch/qos/logback/classic/Level;")
    javabridge.call(rootLogger, "setLevel", "(Lch/qos/logback/classic/Level;)V", logLevel)

    javabridge_open = True

GeneratorSource

data = source.asdask(tile_size) module-attribute

dtype = np.uint8 module-attribute

pixel_size = [(1, 'um')] module-attribute

seed = 0 module-attribute

shape = list(reversed(size)) + [3] module-attribute

size = (256, 256, 256) module-attribute

source = GeneratorSource(size, tile_size, dtype, pixel_size, seed) module-attribute

tile_shape = list(reversed(tile_size[:2])) module-attribute

tile_size = (256, 256, 1) module-attribute

GeneratorSource

Bases: OmeSource

Source code in OmeSliCC\GeneratorSource.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class GeneratorSource(OmeSource):
    def __init__(self, size, tile_size, dtype=np.uint8, source_pixel_size: list = None, seed=None):
        super().__init__()

        size_xyzct = list(size)
        tile_shape = list(np.flip(tile_size))
        if len(size_xyzct) < 3:
            size_xyzct += [1]
        if len(tile_shape) < 3:
            tile_shape += [1]
        size_xyzct += [3]
        tile_shape += [3]
        if len(size_xyzct) < 5:
            size_xyzct += [1]
        if len(tile_shape) < 5:
            tile_shape += [1]
        self.size = size
        self.sizes = [size[:2]]
        self.sizes_xyzct = [size_xyzct]
        self.tile_shape = tile_shape
        dtype = np.dtype(dtype)
        self.dtype = dtype
        self.pixel_types = [dtype]
        self.pixel_nbits.append(dtype.itemsize * 8)

        self._init_metadata('generator', source_pixel_size=source_pixel_size)

        if np.dtype(dtype).kind != 'f':
            self.max_val = 2 ** (8 * np.dtype(dtype).itemsize) - 1
        else:
            self.max_val = 1

        self.is_rgb = True

        if seed is not None:
            np.random.seed(seed)

        self.color_value_table = [np.sin(np.divide(range(dim), dim, dtype=np.float32) * np.pi)
                                  for dim in np.flip(size)]

    def _find_metadata(self):
        self._get_ome_metadata()

    def calc_color(self, *args, **kwargs):
        channels = []
        channel = None
        range0 = kwargs['range0']
        for index, value in enumerate(reversed(args)):
            #channel = np.sin((value + range0[index]) / self.size[index] * np.pi)
            channel = self.color_value_table[index][value + range0[index]]
            channels.append(channel)
        while len(channels) < 3:
            channels.append(channel)
        return np.stack(channels, axis=-1)

    def get_tile(self, indices, tile_size=None):
        # indices / tile size in x,y(,z,...)
        if not tile_size:
            tile_size = np.flip(self.tile_shape)
        range0 = indices
        range1 = np.min([np.array(indices) + np.array(tile_size), self.size], 0)
        shape = list(reversed(range1 - range0))
        tile = np.fromfunction(self.calc_color, shape, dtype=int, range0=range0)
        # apply noise to each channel separately
        for channeli in range(3):
            noise = np.random.random(size=shape) - 0.5
            tile[..., channeli] = np.clip(tile[..., channeli] + noise, 0, 1)
        if self.dtype.kind != 'f':
            tile *= self.max_val
        tile = tile.astype(self.dtype)
        return tile

    def _asarray_level(self, level: int = None, **slicing) -> np.ndarray:
        # ignore level and c
        slices = get_numpy_slicing('xyzt', **slicing)
        indices, tile_size = [], []
        for slice1, axis_size in zip(slices, self.size):
            if axis_size > 0:
                if isinstance(slice1, slice):
                    indices.append(slice1.start)
                    tile_size.append(slice1.stop - slice1.start)
                else:
                    indices.append(slice1)
                    tile_size.append(1)
        data = self.get_tile(indices, tile_size)
        if data.ndim < 4:
            data = np.expand_dims(data, 0)
        data = np.moveaxis(data, -1, 0)
        if data.ndim < 5:
            data = np.expand_dims(data, 0)
        return data

color_value_table = [np.sin(np.divide(range(dim), dim, dtype=np.float32) * np.pi) for dim in np.flip(size)] instance-attribute

dtype = dtype instance-attribute

is_rgb = True instance-attribute

max_val = 2 ** 8 * np.dtype(dtype).itemsize - 1 instance-attribute

pixel_types = [dtype] instance-attribute

size = size instance-attribute

sizes = [size[:2]] instance-attribute

sizes_xyzct = [size_xyzct] instance-attribute

tile_shape = tile_shape instance-attribute

__init__(size, tile_size, dtype=np.uint8, source_pixel_size=None, seed=None)

Source code in OmeSliCC\GeneratorSource.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def __init__(self, size, tile_size, dtype=np.uint8, source_pixel_size: list = None, seed=None):
    super().__init__()

    size_xyzct = list(size)
    tile_shape = list(np.flip(tile_size))
    if len(size_xyzct) < 3:
        size_xyzct += [1]
    if len(tile_shape) < 3:
        tile_shape += [1]
    size_xyzct += [3]
    tile_shape += [3]
    if len(size_xyzct) < 5:
        size_xyzct += [1]
    if len(tile_shape) < 5:
        tile_shape += [1]
    self.size = size
    self.sizes = [size[:2]]
    self.sizes_xyzct = [size_xyzct]
    self.tile_shape = tile_shape
    dtype = np.dtype(dtype)
    self.dtype = dtype
    self.pixel_types = [dtype]
    self.pixel_nbits.append(dtype.itemsize * 8)

    self._init_metadata('generator', source_pixel_size=source_pixel_size)

    if np.dtype(dtype).kind != 'f':
        self.max_val = 2 ** (8 * np.dtype(dtype).itemsize) - 1
    else:
        self.max_val = 1

    self.is_rgb = True

    if seed is not None:
        np.random.seed(seed)

    self.color_value_table = [np.sin(np.divide(range(dim), dim, dtype=np.float32) * np.pi)
                              for dim in np.flip(size)]

_asarray_level(level=None, **slicing)

Source code in OmeSliCC\GeneratorSource.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def _asarray_level(self, level: int = None, **slicing) -> np.ndarray:
    # ignore level and c
    slices = get_numpy_slicing('xyzt', **slicing)
    indices, tile_size = [], []
    for slice1, axis_size in zip(slices, self.size):
        if axis_size > 0:
            if isinstance(slice1, slice):
                indices.append(slice1.start)
                tile_size.append(slice1.stop - slice1.start)
            else:
                indices.append(slice1)
                tile_size.append(1)
    data = self.get_tile(indices, tile_size)
    if data.ndim < 4:
        data = np.expand_dims(data, 0)
    data = np.moveaxis(data, -1, 0)
    if data.ndim < 5:
        data = np.expand_dims(data, 0)
    return data

_find_metadata()

Source code in OmeSliCC\GeneratorSource.py
49
50
def _find_metadata(self):
    self._get_ome_metadata()

calc_color(*args, **kwargs)

Source code in OmeSliCC\GeneratorSource.py
52
53
54
55
56
57
58
59
60
61
62
def calc_color(self, *args, **kwargs):
    channels = []
    channel = None
    range0 = kwargs['range0']
    for index, value in enumerate(reversed(args)):
        #channel = np.sin((value + range0[index]) / self.size[index] * np.pi)
        channel = self.color_value_table[index][value + range0[index]]
        channels.append(channel)
    while len(channels) < 3:
        channels.append(channel)
    return np.stack(channels, axis=-1)

get_tile(indices, tile_size=None)

Source code in OmeSliCC\GeneratorSource.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def get_tile(self, indices, tile_size=None):
    # indices / tile size in x,y(,z,...)
    if not tile_size:
        tile_size = np.flip(self.tile_shape)
    range0 = indices
    range1 = np.min([np.array(indices) + np.array(tile_size), self.size], 0)
    shape = list(reversed(range1 - range0))
    tile = np.fromfunction(self.calc_color, shape, dtype=int, range0=range0)
    # apply noise to each channel separately
    for channeli in range(3):
        noise = np.random.random(size=shape) - 0.5
        tile[..., channeli] = np.clip(tile[..., channeli] + noise, 0, 1)
    if self.dtype.kind != 'f':
        tile *= self.max_val
    tile = tile.astype(self.dtype)
    return tile

OmeSource

OmeSource

OME-compatible image source (base class)

Source code in OmeSliCC\OmeSource.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
class OmeSource:
    """OME-compatible image source (base class)"""
    """Internal image format is [TCZYX]"""

    metadata: dict
    """metadata dictionary"""
    has_ome_metadata: bool
    """has ome metadata"""
    dimension_order: str
    """source dimension order"""
    output_dimension_order: str
    """data dimension order"""
    source_pixel_size: list
    """original source pixel size"""
    target_pixel_size: list
    """target pixel size"""
    sizes: list
    """x/y size pairs for all pages"""
    sizes_xyzct: list
    """xyzct size for all pages"""
    pixel_types: list
    """pixel types for all pages"""
    pixel_nbits: list
    """#bits for all pages"""
    channels: list
    """channel information for all image channels"""
    position: list
    """source position information"""
    rotation: float
    """source rotation information"""

    default_properties_order = 'xyzct'
    default_physical_unit = 'µm'

    def __init__(self):
        self.metadata = {}
        self.has_ome_metadata = False
        self.dimension_order = ''
        self.output_dimension_order = ''
        self.source_pixel_size = []
        self.target_pixel_size = []
        self.sizes = []
        self.sizes_xyzct = []
        self.pixel_types = []
        self.pixel_nbits = []
        self.channels = []

    def _init_metadata(self,
                       source_reference: str,
                       source_pixel_size: list = None,
                       target_pixel_size: list = None,
                       source_info_required: bool = False):

        self.source_reference = source_reference
        self.target_pixel_size = target_pixel_size
        self._find_metadata()
        if (len(self.source_pixel_size) == 0 or self.source_pixel_size[0][0] == 0
                or self.source_pixel_size[0][1] == '' or self.source_pixel_size[0][1] == 'inch') \
                and source_pixel_size is not None:
            # if pixel size is not set, or default/unspecified value
            self.source_pixel_size = source_pixel_size
        if len(self.source_pixel_size) == 0 or self.source_pixel_size[0][0] == 0:
            msg = f'{source_reference}: No source pixel size in metadata or provided'
            if source_info_required:
                raise ValueError(msg)
            else:
                logging.warning(msg)
        self._init_sizes()

    def _get_ome_metadata(self):
        images = ensure_list(self.metadata.get('Image', {}))[0]
        pixels = images.get('Pixels', {})
        self.source_pixel_size = []
        size = float(pixels.get('PhysicalSizeX', 0))
        if size > 0:
            self.source_pixel_size.append((size, pixels.get('PhysicalSizeXUnit', self.default_physical_unit)))
        size = float(pixels.get('PhysicalSizeY', 0))
        if size > 0:
            self.source_pixel_size.append((size, pixels.get('PhysicalSizeYUnit', self.default_physical_unit)))
        size = float(pixels.get('PhysicalSizeZ', 0))
        if size > 0:
            self.source_pixel_size.append((size, pixels.get('PhysicalSizeZUnit', self.default_physical_unit)))

        position = []
        for plane in ensure_list(pixels.get('Plane', [])):
            position = []
            if 'PositionX' in plane:
                position.append((float(plane.get('PositionX')), plane.get('PositionXUnit', self.default_physical_unit)))
            if 'PositionY' in plane:
                position.append((float(plane.get('PositionY')), plane.get('PositionYUnit', self.default_physical_unit)))
            if 'PositionZ' in plane:
                position.append((float(plane.get('PositionZ')), plane.get('PositionZUnit', self.default_physical_unit)))
            #c, z, t = plane.get('TheC'), plane.get('TheZ'), plane.get('TheT')
            break
        self.position = position

        rotation = None
        annotations = self.metadata.get('StructuredAnnotations')
        if annotations is not None:
            if not isinstance(annotations, (list, tuple)):
                annotations = [annotations]
            for annotation_item in annotations:
                for annotations2 in annotation_item.values():
                    if not isinstance(annotations2, (list, tuple)):
                        annotations2 = [annotations2]
                    for annotation in annotations2:
                        value = annotation.get('Value')
                        unit = None
                        if isinstance(value, dict) and 'Modulo' in value:
                            modulo = value.get('Modulo', {}).get('ModuloAlongZ', {})
                            unit = modulo.get('Unit')
                            value = modulo.get('Label')
                        elif isinstance(value, str) and value.lower().startswith('angle'):
                            if ':' in value:
                                value = value.split(':')[1].split()
                            elif '=' in value:
                                value = value.split('=')[1].split()
                            else:
                                value = value.split()[1:]
                            if len(value) >= 2:
                                unit = value[1]
                            value = value[0]
                        else:
                            value = None
                        if value is not None:
                            rotation = float(value)
                            if 'rad' in unit.lower():
                                rotation = np.rad2deg(rotation)
        self.rotation = rotation

        self.source_mag = 0
        objective_id = images.get('ObjectiveSettings', {}).get('ID', '')
        for objective in ensure_list(self.metadata.get('Instrument', {}).get('Objective', [])):
            if objective.get('ID', '') == objective_id:
                self.source_mag = float(objective.get('NominalMagnification', 0))
        nchannels = self.get_nchannels()
        channels = []
        for channel0 in ensure_list(pixels.get('Channel', [])):
            channel = {'label': channel0.get('Name', '')}
            color = channel0.get('Color')
            if color:
                channel['color'] = int_to_rgba(int(color))
            channels.append(channel)
        if len(channels) == 0:
            if nchannels == 3:
                channels = [{'label': ''}]
            else:
                channels = [{'label': str(channeli)} for channeli in range(nchannels)]
        self.channels = channels

    def _init_sizes(self):
        # normalise source pixel sizes
        standard_units = {'nano': 'nm', 'micro': 'µm', 'milli': 'mm', 'centi': 'cm'}
        pixel_size = []
        for pixel_size0 in self.source_pixel_size:
            pixel_size1 = check_round_significants(pixel_size0[0], 6)
            unit1 = pixel_size0[1]
            for standard_unit in standard_units:
                if unit1.lower().startswith(standard_unit):
                    unit1 = standard_units[standard_unit]
            pixel_size.append((pixel_size1, unit1))
        if 0 < len(pixel_size) < 2:
            pixel_size.append(pixel_size[0])
        self.source_pixel_size = pixel_size

        if self.target_pixel_size is None:
            self.target_pixel_size = self.source_pixel_size

        if 0 < len(self.target_pixel_size) < 2:
            self.target_pixel_size.append(self.target_pixel_size[0])

        # set source mags
        self.source_mags = [self.source_mag]
        for i, size in enumerate(self.sizes):
            if i > 0:
                mag = self.source_mag * np.mean(np.divide(size, self.sizes[0]))
                self.source_mags.append(check_round_significants(mag, 3))

        if self.target_pixel_size:
            self.best_level, self.best_factor, self.full_factor = get_best_level_factor(self, self.target_pixel_size)
        else:
            self.best_level = 0
            self.best_factor = 1

        if self.dimension_order == '':
            x, y, z, c, t = self.get_size_xyzct()
            if t > 1:
                self.dimension_order += 't'
            if c > 1:
                self.dimension_order += 'c'
            if z > 1:
                self.dimension_order += 'z'
            self.dimension_order += 'yx'

        self.output_dimension_order = 'tczyx'

    def get_source_dask(self):
        raise NotImplementedError('Implement method in subclass')

    def get_mag(self) -> float:
        mag = self.source_mag
        # get effective mag at target pixel size
        if self.target_pixel_size:
            mag *= np.mean(self.full_factor)
        return check_round_significants(mag, 3)

    def get_dimension_order(self) -> str:
        return self.output_dimension_order

    def get_physical_size(self) -> tuple:
        physical_size = []
        for size, pixel_size in zip(self.get_size_xyzct(), self.get_pixel_size()):
            physical_size.append((np.multiply(size, pixel_size[0]), pixel_size[1]))
        return tuple(physical_size)

    def get_pixel_type(self, level: int = 0) -> np.dtype:
        return self.pixel_types[level]

    def get_pixel_nbytes(self, level: int = 0) -> int:
        return self.pixel_nbits[level] // 8

    def get_channels(self) -> list:
        return self.channels

    def get_size(self) -> tuple:
        # size at target pixel size
        return tuple(np.round(np.multiply(self.sizes[self.best_level], self.best_factor)).astype(int))

    def get_size_xyzct(self) -> tuple:
        xyzct = list(self.sizes_xyzct[self.best_level]).copy()
        size = self.get_size()
        xyzct[0:2] = size
        return tuple(xyzct)

    def get_nchannels(self):
        return self.sizes_xyzct[0][3]

    def get_pixel_size(self) -> list:
        return self.target_pixel_size

    def get_pixel_size_micrometer(self):
        return get_value_units_micrometer(self.get_pixel_size())

    def get_position(self) -> list:
        return self.position

    def get_position_micrometer(self):
        return get_value_units_micrometer(self.get_position())

    def get_rotation(self) -> float:
        return self.rotation

    def get_shape(self, dimension_order: str = None, xyzct: tuple = None) -> tuple:
        shape = []
        if dimension_order is None:
            dimension_order = self.get_dimension_order()
        if xyzct is None:
            xyzct = self.get_size_xyzct()
        for dimension in dimension_order:
            index = 'xyzct'.index(dimension)
            shape.append(xyzct[index])
        return tuple(shape)

    def get_thumbnail(self, target_size: tuple, precise: bool = False) -> np.ndarray:
        size, index = get_best_size(self.sizes, target_size)
        scale = np.divide(target_size, self.sizes[index])
        new_dimension_order = 'yxc'
        image = redimension_data(self._asarray_level(index), self.get_dimension_order(), new_dimension_order, t=0, z=0)
        if precise:
            thumbnail = precise_resize(image, scale)
        else:
            thumbnail = image_resize(image, target_size)
        thumbnail_rgb = self.render(thumbnail, new_dimension_order)
        return thumbnail_rgb

    def get_channel_window(self, channeli):
        min_quantile = 0.001
        max_quantile = 0.999

        if channeli < len(self.channels) and 'window' in self.channels[channeli]:
            return self.channels[channeli].get('window')

        dtype = self.get_pixel_type()
        if dtype.kind == 'f':
            # info = np.finfo(dtype)
            start, end = 0, 1
        else:
            info = np.iinfo(dtype)
            start, end = info.min, info.max

        nsizes = len(self.sizes)
        if nsizes > 1:
            image = self._asarray_level(nsizes - 1)
            image = np.asarray(image[:, channeli:channeli+1, ...])
            min, max = get_image_quantile(image, min_quantile), get_image_quantile(image, max_quantile)
        else:
            # do not query full size image
            min, max = start, end
        return {'start': start, 'end': end, 'min': min, 'max': max}

    def render(self, image: np.ndarray, source_dimension_order: str = None, t: int = 0, z: int = 0, channels: list = []) -> np.ndarray:
        if source_dimension_order is None:
            source_dimension_order = self.get_dimension_order()
        image = redimension_data(image, source_dimension_order, 'yxc', t=t, z=z)
        total_image = None
        n = len(self.channels)
        is_rgb = (self.get_nchannels() in (3, 4) and (n <= 1 or n == 3))
        needs_normalisation = (image.dtype.itemsize == 2)

        if not is_rgb:
            tot_alpha = 0
            for channeli, channel in enumerate(self.channels):
                if not channels or channeli in channels:
                    if n == 1:
                        channel_values = image
                    else:
                        channel_values = image[..., channeli]
                    if needs_normalisation:
                        window = self.get_channel_window(channeli)
                        channel_values = normalise_values(channel_values, window['min'], window['max'])
                    else:
                        channel_values = int2float_image(channel_values)
                    new_channel_image = np.atleast_3d(channel_values)
                    color = channel.get('color')
                    if color:
                        rgba = color
                    else:
                        rgba = [1, 1, 1, 1]
                    color = rgba[:3]
                    alpha = rgba[3]
                    if alpha == 0:
                        alpha = 1
                    new_channel_image = new_channel_image * np.multiply(color, alpha).astype(np.float32)
                    if total_image is None:
                        total_image = new_channel_image
                    else:
                        total_image += new_channel_image
                    tot_alpha += alpha
            if tot_alpha != 1:
                total_image /= tot_alpha
            final_image = float2int_image(total_image,
                                          target_dtype=image.dtype)
        elif needs_normalisation:
            window = self.get_channel_window(0)
            final_image = float2int_image(normalise_values(image, window['min'], window['max']),
                                          target_dtype=image.dtype)
        else:
            final_image = image
        return final_image

    def asarray(self, pixel_size: list = [], **slicing) -> np.ndarray:
        # expects x0, x1, y0, y1, ...
        x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
        y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
        # allow custom pixel size
        if pixel_size:
            level, factor, _ = get_best_level_factor(self, pixel_size)
            size0 = np.round(np.multiply(self.sizes[level], factor)).astype(int)
        else:
            level, factor = self.best_level, self.best_factor
            size0 = self.get_size()

        if x1 < 0 or y1 < 0:
            x1, y1 = size0
        if np.mean(factor) != 1:
            slicing['x0'], slicing['y0'] = np.round(np.divide([x0, y0], factor)).astype(int)
            slicing['x1'], slicing['y1'] = np.round(np.divide([x1, y1], factor)).astype(int)
        image0 = self._asarray_level(level=level, **slicing)
        if np.mean(factor) != 1:
            size1 = x1 - x0, y1 - y0
            image = image_resize(image0, size1, dimension_order=self.get_dimension_order())
        else:
            image = image0
        return image

    def asarray_um(self, **slicing):
        pixel_size = self.get_pixel_size_micrometer()[:2]
        slicing['x0'], slicing['y0'] = np.divide([slicing.get('x0'), slicing.get('y0')], pixel_size)
        slicing['x1'], slicing['y1'] = np.divide([slicing.get('x1'), slicing.get('y1')], pixel_size)
        return self.asarray(**slicing)

    def asdask(self, chunk_size: tuple) -> da.Array:
        chunk_shape = list(np.flip(chunk_size))
        while len(chunk_shape) < 3:
            chunk_shape = [1] + chunk_shape
        chunk_shape = [self.get_nchannels()] + chunk_shape
        while len(chunk_shape) < 5:
            chunk_shape = [1] + chunk_shape
        chunks = np.ceil(np.flip(self.get_size_xyzct()) / chunk_shape).astype(int)
        w, h = self.get_size()

        delayed_reader = dask.delayed(self.asarray)
        dtype = self.get_pixel_type()

        dask_times = []
        for ti in range(chunks[0]):
            dask_planes = []
            for zi in range(chunks[2]):
                dask_rows = []
                for yi in range(chunks[3]):
                    dask_row = []
                    for xi in range(chunks[4]):
                        shape = list(chunk_shape).copy()
                        x0, x1 = xi * shape[4], (xi + 1) * shape[4]
                        y0, y1 = yi * shape[3], (yi + 1) * shape[3]
                        if x1 > w:
                            x1 = w
                            shape[4] = w - x0
                        if y1 > h:
                            y1 = h
                            shape[3] = h - y0
                        z = zi * shape[2]
                        t = ti * shape[0]
                        dask_tile = da.from_delayed(delayed_reader(x0=x0, x1=x1, y0=y0, y1=y1, z=z, t=t),
                                                    shape=shape, dtype=dtype)
                        dask_row.append(dask_tile)
                    dask_rows.append(da.concatenate(dask_row, axis=4))
                dask_planes.append(da.concatenate(dask_rows, axis=3))
            dask_times.append(da.concatenate(dask_planes, axis=2))
        dask_data = da.concatenate(dask_times, axis=0)
        return dask_data

    def clone_empty(self) -> np.ndarray:
        return np.zeros(self.get_shape(), dtype=self.get_pixel_type())

    def produce_chunks(self, chunk_size: tuple) -> tuple:
        w, h = self.get_size()
        ny = int(np.ceil(h / chunk_size[1]))
        nx = int(np.ceil(w / chunk_size[0]))
        for chunky in range(ny):
            for chunkx in range(nx):
                x0, y0 = chunkx * chunk_size[0], chunky * chunk_size[1]
                x1, y1 = min((chunkx + 1) * chunk_size[0], w), min((chunky + 1) * chunk_size[1], h)
                indices = 0, 0, 0, y0, x0
                yield indices, self.asarray(x0=x0, x1=x1, y0=y0, y1=y1)

    def get_metadata(self) -> dict:
        return self.metadata

    def create_xml_metadata(self, output_filename: str, combine_rgb: bool = True, pyramid_sizes_add: list = None) -> str:
        return create_ome_metadata(self, output_filename, combine_rgb=combine_rgb, pyramid_sizes_add=pyramid_sizes_add)

    def _find_metadata(self):
        raise NotImplementedError('Implement method in subclass')

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        raise NotImplementedError('Implement method in subclass')

    def close(self):
        pass

channels = [] instance-attribute

channel information for all image channels

default_physical_unit = 'µm' class-attribute instance-attribute

default_properties_order = 'xyzct' class-attribute instance-attribute

dimension_order = '' instance-attribute

source dimension order

has_ome_metadata = False instance-attribute

has ome metadata

metadata = {} instance-attribute

metadata dictionary

output_dimension_order = '' instance-attribute

data dimension order

pixel_nbits = [] instance-attribute

bits for all pages

pixel_types = [] instance-attribute

pixel types for all pages

position instance-attribute

source position information

rotation instance-attribute

source rotation information

sizes = [] instance-attribute

x/y size pairs for all pages

sizes_xyzct = [] instance-attribute

xyzct size for all pages

source_pixel_size = [] instance-attribute

original source pixel size

target_pixel_size = [] instance-attribute

target pixel size

__init__()

Source code in OmeSliCC\OmeSource.py
46
47
48
49
50
51
52
53
54
55
56
57
def __init__(self):
    self.metadata = {}
    self.has_ome_metadata = False
    self.dimension_order = ''
    self.output_dimension_order = ''
    self.source_pixel_size = []
    self.target_pixel_size = []
    self.sizes = []
    self.sizes_xyzct = []
    self.pixel_types = []
    self.pixel_nbits = []
    self.channels = []

_asarray_level(level, **slicing)

Source code in OmeSliCC\OmeSource.py
457
458
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    raise NotImplementedError('Implement method in subclass')

_find_metadata()

Source code in OmeSliCC\OmeSource.py
454
455
def _find_metadata(self):
    raise NotImplementedError('Implement method in subclass')

_get_ome_metadata()

Source code in OmeSliCC\OmeSource.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def _get_ome_metadata(self):
    images = ensure_list(self.metadata.get('Image', {}))[0]
    pixels = images.get('Pixels', {})
    self.source_pixel_size = []
    size = float(pixels.get('PhysicalSizeX', 0))
    if size > 0:
        self.source_pixel_size.append((size, pixels.get('PhysicalSizeXUnit', self.default_physical_unit)))
    size = float(pixels.get('PhysicalSizeY', 0))
    if size > 0:
        self.source_pixel_size.append((size, pixels.get('PhysicalSizeYUnit', self.default_physical_unit)))
    size = float(pixels.get('PhysicalSizeZ', 0))
    if size > 0:
        self.source_pixel_size.append((size, pixels.get('PhysicalSizeZUnit', self.default_physical_unit)))

    position = []
    for plane in ensure_list(pixels.get('Plane', [])):
        position = []
        if 'PositionX' in plane:
            position.append((float(plane.get('PositionX')), plane.get('PositionXUnit', self.default_physical_unit)))
        if 'PositionY' in plane:
            position.append((float(plane.get('PositionY')), plane.get('PositionYUnit', self.default_physical_unit)))
        if 'PositionZ' in plane:
            position.append((float(plane.get('PositionZ')), plane.get('PositionZUnit', self.default_physical_unit)))
        #c, z, t = plane.get('TheC'), plane.get('TheZ'), plane.get('TheT')
        break
    self.position = position

    rotation = None
    annotations = self.metadata.get('StructuredAnnotations')
    if annotations is not None:
        if not isinstance(annotations, (list, tuple)):
            annotations = [annotations]
        for annotation_item in annotations:
            for annotations2 in annotation_item.values():
                if not isinstance(annotations2, (list, tuple)):
                    annotations2 = [annotations2]
                for annotation in annotations2:
                    value = annotation.get('Value')
                    unit = None
                    if isinstance(value, dict) and 'Modulo' in value:
                        modulo = value.get('Modulo', {}).get('ModuloAlongZ', {})
                        unit = modulo.get('Unit')
                        value = modulo.get('Label')
                    elif isinstance(value, str) and value.lower().startswith('angle'):
                        if ':' in value:
                            value = value.split(':')[1].split()
                        elif '=' in value:
                            value = value.split('=')[1].split()
                        else:
                            value = value.split()[1:]
                        if len(value) >= 2:
                            unit = value[1]
                        value = value[0]
                    else:
                        value = None
                    if value is not None:
                        rotation = float(value)
                        if 'rad' in unit.lower():
                            rotation = np.rad2deg(rotation)
    self.rotation = rotation

    self.source_mag = 0
    objective_id = images.get('ObjectiveSettings', {}).get('ID', '')
    for objective in ensure_list(self.metadata.get('Instrument', {}).get('Objective', [])):
        if objective.get('ID', '') == objective_id:
            self.source_mag = float(objective.get('NominalMagnification', 0))
    nchannels = self.get_nchannels()
    channels = []
    for channel0 in ensure_list(pixels.get('Channel', [])):
        channel = {'label': channel0.get('Name', '')}
        color = channel0.get('Color')
        if color:
            channel['color'] = int_to_rgba(int(color))
        channels.append(channel)
    if len(channels) == 0:
        if nchannels == 3:
            channels = [{'label': ''}]
        else:
            channels = [{'label': str(channeli)} for channeli in range(nchannels)]
    self.channels = channels

_init_metadata(source_reference, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\OmeSource.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def _init_metadata(self,
                   source_reference: str,
                   source_pixel_size: list = None,
                   target_pixel_size: list = None,
                   source_info_required: bool = False):

    self.source_reference = source_reference
    self.target_pixel_size = target_pixel_size
    self._find_metadata()
    if (len(self.source_pixel_size) == 0 or self.source_pixel_size[0][0] == 0
            or self.source_pixel_size[0][1] == '' or self.source_pixel_size[0][1] == 'inch') \
            and source_pixel_size is not None:
        # if pixel size is not set, or default/unspecified value
        self.source_pixel_size = source_pixel_size
    if len(self.source_pixel_size) == 0 or self.source_pixel_size[0][0] == 0:
        msg = f'{source_reference}: No source pixel size in metadata or provided'
        if source_info_required:
            raise ValueError(msg)
        else:
            logging.warning(msg)
    self._init_sizes()

_init_sizes()

Source code in OmeSliCC\OmeSource.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def _init_sizes(self):
    # normalise source pixel sizes
    standard_units = {'nano': 'nm', 'micro': 'µm', 'milli': 'mm', 'centi': 'cm'}
    pixel_size = []
    for pixel_size0 in self.source_pixel_size:
        pixel_size1 = check_round_significants(pixel_size0[0], 6)
        unit1 = pixel_size0[1]
        for standard_unit in standard_units:
            if unit1.lower().startswith(standard_unit):
                unit1 = standard_units[standard_unit]
        pixel_size.append((pixel_size1, unit1))
    if 0 < len(pixel_size) < 2:
        pixel_size.append(pixel_size[0])
    self.source_pixel_size = pixel_size

    if self.target_pixel_size is None:
        self.target_pixel_size = self.source_pixel_size

    if 0 < len(self.target_pixel_size) < 2:
        self.target_pixel_size.append(self.target_pixel_size[0])

    # set source mags
    self.source_mags = [self.source_mag]
    for i, size in enumerate(self.sizes):
        if i > 0:
            mag = self.source_mag * np.mean(np.divide(size, self.sizes[0]))
            self.source_mags.append(check_round_significants(mag, 3))

    if self.target_pixel_size:
        self.best_level, self.best_factor, self.full_factor = get_best_level_factor(self, self.target_pixel_size)
    else:
        self.best_level = 0
        self.best_factor = 1

    if self.dimension_order == '':
        x, y, z, c, t = self.get_size_xyzct()
        if t > 1:
            self.dimension_order += 't'
        if c > 1:
            self.dimension_order += 'c'
        if z > 1:
            self.dimension_order += 'z'
        self.dimension_order += 'yx'

    self.output_dimension_order = 'tczyx'

asarray(pixel_size=[], **slicing)

Source code in OmeSliCC\OmeSource.py
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
def asarray(self, pixel_size: list = [], **slicing) -> np.ndarray:
    # expects x0, x1, y0, y1, ...
    x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
    y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
    # allow custom pixel size
    if pixel_size:
        level, factor, _ = get_best_level_factor(self, pixel_size)
        size0 = np.round(np.multiply(self.sizes[level], factor)).astype(int)
    else:
        level, factor = self.best_level, self.best_factor
        size0 = self.get_size()

    if x1 < 0 or y1 < 0:
        x1, y1 = size0
    if np.mean(factor) != 1:
        slicing['x0'], slicing['y0'] = np.round(np.divide([x0, y0], factor)).astype(int)
        slicing['x1'], slicing['y1'] = np.round(np.divide([x1, y1], factor)).astype(int)
    image0 = self._asarray_level(level=level, **slicing)
    if np.mean(factor) != 1:
        size1 = x1 - x0, y1 - y0
        image = image_resize(image0, size1, dimension_order=self.get_dimension_order())
    else:
        image = image0
    return image

asarray_um(**slicing)

Source code in OmeSliCC\OmeSource.py
387
388
389
390
391
def asarray_um(self, **slicing):
    pixel_size = self.get_pixel_size_micrometer()[:2]
    slicing['x0'], slicing['y0'] = np.divide([slicing.get('x0'), slicing.get('y0')], pixel_size)
    slicing['x1'], slicing['y1'] = np.divide([slicing.get('x1'), slicing.get('y1')], pixel_size)
    return self.asarray(**slicing)

asdask(chunk_size)

Source code in OmeSliCC\OmeSource.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
def asdask(self, chunk_size: tuple) -> da.Array:
    chunk_shape = list(np.flip(chunk_size))
    while len(chunk_shape) < 3:
        chunk_shape = [1] + chunk_shape
    chunk_shape = [self.get_nchannels()] + chunk_shape
    while len(chunk_shape) < 5:
        chunk_shape = [1] + chunk_shape
    chunks = np.ceil(np.flip(self.get_size_xyzct()) / chunk_shape).astype(int)
    w, h = self.get_size()

    delayed_reader = dask.delayed(self.asarray)
    dtype = self.get_pixel_type()

    dask_times = []
    for ti in range(chunks[0]):
        dask_planes = []
        for zi in range(chunks[2]):
            dask_rows = []
            for yi in range(chunks[3]):
                dask_row = []
                for xi in range(chunks[4]):
                    shape = list(chunk_shape).copy()
                    x0, x1 = xi * shape[4], (xi + 1) * shape[4]
                    y0, y1 = yi * shape[3], (yi + 1) * shape[3]
                    if x1 > w:
                        x1 = w
                        shape[4] = w - x0
                    if y1 > h:
                        y1 = h
                        shape[3] = h - y0
                    z = zi * shape[2]
                    t = ti * shape[0]
                    dask_tile = da.from_delayed(delayed_reader(x0=x0, x1=x1, y0=y0, y1=y1, z=z, t=t),
                                                shape=shape, dtype=dtype)
                    dask_row.append(dask_tile)
                dask_rows.append(da.concatenate(dask_row, axis=4))
            dask_planes.append(da.concatenate(dask_rows, axis=3))
        dask_times.append(da.concatenate(dask_planes, axis=2))
    dask_data = da.concatenate(dask_times, axis=0)
    return dask_data

clone_empty()

Source code in OmeSliCC\OmeSource.py
434
435
def clone_empty(self) -> np.ndarray:
    return np.zeros(self.get_shape(), dtype=self.get_pixel_type())

close()

Source code in OmeSliCC\OmeSource.py
460
461
def close(self):
    pass

create_xml_metadata(output_filename, combine_rgb=True, pyramid_sizes_add=None)

Source code in OmeSliCC\OmeSource.py
451
452
def create_xml_metadata(self, output_filename: str, combine_rgb: bool = True, pyramid_sizes_add: list = None) -> str:
    return create_ome_metadata(self, output_filename, combine_rgb=combine_rgb, pyramid_sizes_add=pyramid_sizes_add)

get_channel_window(channeli)

Source code in OmeSliCC\OmeSource.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def get_channel_window(self, channeli):
    min_quantile = 0.001
    max_quantile = 0.999

    if channeli < len(self.channels) and 'window' in self.channels[channeli]:
        return self.channels[channeli].get('window')

    dtype = self.get_pixel_type()
    if dtype.kind == 'f':
        # info = np.finfo(dtype)
        start, end = 0, 1
    else:
        info = np.iinfo(dtype)
        start, end = info.min, info.max

    nsizes = len(self.sizes)
    if nsizes > 1:
        image = self._asarray_level(nsizes - 1)
        image = np.asarray(image[:, channeli:channeli+1, ...])
        min, max = get_image_quantile(image, min_quantile), get_image_quantile(image, max_quantile)
    else:
        # do not query full size image
        min, max = start, end
    return {'start': start, 'end': end, 'min': min, 'max': max}

get_channels()

Source code in OmeSliCC\OmeSource.py
233
234
def get_channels(self) -> list:
    return self.channels

get_dimension_order()

Source code in OmeSliCC\OmeSource.py
218
219
def get_dimension_order(self) -> str:
    return self.output_dimension_order

get_mag()

Source code in OmeSliCC\OmeSource.py
211
212
213
214
215
216
def get_mag(self) -> float:
    mag = self.source_mag
    # get effective mag at target pixel size
    if self.target_pixel_size:
        mag *= np.mean(self.full_factor)
    return check_round_significants(mag, 3)

get_metadata()

Source code in OmeSliCC\OmeSource.py
448
449
def get_metadata(self) -> dict:
    return self.metadata

get_nchannels()

Source code in OmeSliCC\OmeSource.py
246
247
def get_nchannels(self):
    return self.sizes_xyzct[0][3]

get_physical_size()

Source code in OmeSliCC\OmeSource.py
221
222
223
224
225
def get_physical_size(self) -> tuple:
    physical_size = []
    for size, pixel_size in zip(self.get_size_xyzct(), self.get_pixel_size()):
        physical_size.append((np.multiply(size, pixel_size[0]), pixel_size[1]))
    return tuple(physical_size)

get_pixel_nbytes(level=0)

Source code in OmeSliCC\OmeSource.py
230
231
def get_pixel_nbytes(self, level: int = 0) -> int:
    return self.pixel_nbits[level] // 8

get_pixel_size()

Source code in OmeSliCC\OmeSource.py
249
250
def get_pixel_size(self) -> list:
    return self.target_pixel_size

get_pixel_size_micrometer()

Source code in OmeSliCC\OmeSource.py
252
253
def get_pixel_size_micrometer(self):
    return get_value_units_micrometer(self.get_pixel_size())

get_pixel_type(level=0)

Source code in OmeSliCC\OmeSource.py
227
228
def get_pixel_type(self, level: int = 0) -> np.dtype:
    return self.pixel_types[level]

get_position()

Source code in OmeSliCC\OmeSource.py
255
256
def get_position(self) -> list:
    return self.position

get_position_micrometer()

Source code in OmeSliCC\OmeSource.py
258
259
def get_position_micrometer(self):
    return get_value_units_micrometer(self.get_position())

get_rotation()

Source code in OmeSliCC\OmeSource.py
261
262
def get_rotation(self) -> float:
    return self.rotation

get_shape(dimension_order=None, xyzct=None)

Source code in OmeSliCC\OmeSource.py
264
265
266
267
268
269
270
271
272
273
def get_shape(self, dimension_order: str = None, xyzct: tuple = None) -> tuple:
    shape = []
    if dimension_order is None:
        dimension_order = self.get_dimension_order()
    if xyzct is None:
        xyzct = self.get_size_xyzct()
    for dimension in dimension_order:
        index = 'xyzct'.index(dimension)
        shape.append(xyzct[index])
    return tuple(shape)

get_size()

Source code in OmeSliCC\OmeSource.py
236
237
238
def get_size(self) -> tuple:
    # size at target pixel size
    return tuple(np.round(np.multiply(self.sizes[self.best_level], self.best_factor)).astype(int))

get_size_xyzct()

Source code in OmeSliCC\OmeSource.py
240
241
242
243
244
def get_size_xyzct(self) -> tuple:
    xyzct = list(self.sizes_xyzct[self.best_level]).copy()
    size = self.get_size()
    xyzct[0:2] = size
    return tuple(xyzct)

get_source_dask()

Source code in OmeSliCC\OmeSource.py
208
209
def get_source_dask(self):
    raise NotImplementedError('Implement method in subclass')

get_thumbnail(target_size, precise=False)

Source code in OmeSliCC\OmeSource.py
275
276
277
278
279
280
281
282
283
284
285
def get_thumbnail(self, target_size: tuple, precise: bool = False) -> np.ndarray:
    size, index = get_best_size(self.sizes, target_size)
    scale = np.divide(target_size, self.sizes[index])
    new_dimension_order = 'yxc'
    image = redimension_data(self._asarray_level(index), self.get_dimension_order(), new_dimension_order, t=0, z=0)
    if precise:
        thumbnail = precise_resize(image, scale)
    else:
        thumbnail = image_resize(image, target_size)
    thumbnail_rgb = self.render(thumbnail, new_dimension_order)
    return thumbnail_rgb

produce_chunks(chunk_size)

Source code in OmeSliCC\OmeSource.py
437
438
439
440
441
442
443
444
445
446
def produce_chunks(self, chunk_size: tuple) -> tuple:
    w, h = self.get_size()
    ny = int(np.ceil(h / chunk_size[1]))
    nx = int(np.ceil(w / chunk_size[0]))
    for chunky in range(ny):
        for chunkx in range(nx):
            x0, y0 = chunkx * chunk_size[0], chunky * chunk_size[1]
            x1, y1 = min((chunkx + 1) * chunk_size[0], w), min((chunky + 1) * chunk_size[1], h)
            indices = 0, 0, 0, y0, x0
            yield indices, self.asarray(x0=x0, x1=x1, y0=y0, y1=y1)

render(image, source_dimension_order=None, t=0, z=0, channels=[])

Source code in OmeSliCC\OmeSource.py
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
def render(self, image: np.ndarray, source_dimension_order: str = None, t: int = 0, z: int = 0, channels: list = []) -> np.ndarray:
    if source_dimension_order is None:
        source_dimension_order = self.get_dimension_order()
    image = redimension_data(image, source_dimension_order, 'yxc', t=t, z=z)
    total_image = None
    n = len(self.channels)
    is_rgb = (self.get_nchannels() in (3, 4) and (n <= 1 or n == 3))
    needs_normalisation = (image.dtype.itemsize == 2)

    if not is_rgb:
        tot_alpha = 0
        for channeli, channel in enumerate(self.channels):
            if not channels or channeli in channels:
                if n == 1:
                    channel_values = image
                else:
                    channel_values = image[..., channeli]
                if needs_normalisation:
                    window = self.get_channel_window(channeli)
                    channel_values = normalise_values(channel_values, window['min'], window['max'])
                else:
                    channel_values = int2float_image(channel_values)
                new_channel_image = np.atleast_3d(channel_values)
                color = channel.get('color')
                if color:
                    rgba = color
                else:
                    rgba = [1, 1, 1, 1]
                color = rgba[:3]
                alpha = rgba[3]
                if alpha == 0:
                    alpha = 1
                new_channel_image = new_channel_image * np.multiply(color, alpha).astype(np.float32)
                if total_image is None:
                    total_image = new_channel_image
                else:
                    total_image += new_channel_image
                tot_alpha += alpha
        if tot_alpha != 1:
            total_image /= tot_alpha
        final_image = float2int_image(total_image,
                                      target_dtype=image.dtype)
    elif needs_normalisation:
        window = self.get_channel_window(0)
        final_image = float2int_image(normalise_values(image, window['min'], window['max']),
                                      target_dtype=image.dtype)
    else:
        final_image = image
    return final_image

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_best_level_factor(source, target_pixel_size)

Source code in OmeSliCC\OmeSource.py
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
def get_best_level_factor(source: OmeSource, target_pixel_size: list) -> tuple:
    # find best pixel_size level and corresponding factor
    if source.source_pixel_size:
        target_factor = np.divide(get_value_units_micrometer(source.source_pixel_size)[:2],
                                  get_value_units_micrometer(target_pixel_size)[:2])
    else:
        target_factor = 1
    best_level = 0
    best_factor = None
    for level, size in enumerate(source.sizes):
        factor = np.divide(size, source.sizes[0])
        if (np.all(factor > target_factor)
                or np.all(np.isclose(factor, target_factor, rtol=1e-4))
                or best_factor is None):
            best_level = level
            best_factor = factor
    return best_level, target_factor / best_factor, target_factor

get_best_size(sizes, target_size)

Source code in OmeSliCC\OmeSource.py
513
514
515
516
517
518
519
520
521
522
def get_best_size(sizes: list, target_size: tuple) -> tuple:
    # find largest scale but smaller to 1
    best_index = 0
    best_scale = 0
    for index, size in enumerate(sizes):
        scale = np.mean(np.divide(target_size, size))
        if 1 >= scale > best_scale:
            best_index = index
            best_scale = scale
    return sizes[best_index], best_index

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_resolution_from_pixel_size(pixel_size)

Source code in OmeSliCC\OmeSource.py
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
def get_resolution_from_pixel_size(pixel_size: list) -> tuple:
    conversions = {
        'cm': (1, 'centimeter'),
        'mm': (1, 'millimeter'),
        'µm': (1, 'micrometer'),
        'um': (1, 'micrometer'),
        'nm': (1000, 'micrometer'),
        'nanometer': (1000, 'micrometer'),
    }
    resolutions = []
    resolutions_unit = None
    if len(pixel_size) > 0:
        units = []
        for size, unit in pixel_size:
            if size != 0 and size != 1:
                resolution = 1 / size
                resolutions.append(resolution)
                if unit != '':
                    units.append(unit)
        if len(units) > 0:
            resolutions_unit = units[0]
            if resolutions_unit in conversions:
                conversion = conversions[resolutions_unit]
                resolutions = list(np.multiply(resolutions, conversion[0]))
                resolutions_unit = conversion[1]
    if len(resolutions) == 0:
        resolutions = None
    return resolutions, resolutions_unit

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

OmeZarr

OmeZarr

Source code in OmeSliCC\OmeZarr.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class OmeZarr:
    def __init__(self, filename):
        self.filename = filename

    def write(self, sources, tile_size=[], compression=[],
              npyramid_add=0, pyramid_downsample=2, translations=[],
              image_operations=[]):

        compressor, compression_filters = create_compression_filter(compression)
        storage_options = {'dimension_separator': '/', 'chunks': tile_size}
        ome_version = '0.4'
        # Zarr V3 testing
        #storage_options = {'chunks': tile_size}
        if compressor is not None:
            storage_options['compressor'] = compressor
        if compression_filters is not None:
            storage_options['filters'] = compression_filters
        self.storage_options = storage_options

        zarr_root = zarr.open_group(store=parse_url(self.filename, mode="w").store, mode="w", storage_options=storage_options)
        # Zarr V3 testing
        #zarr_root = zarr.open_group(store=parse_url(self.filename, mode="w").store, mode="w", zarr_version=3)
        root_path = ''

        multiple_images = isinstance(sources, list)
        multi_metadata = []
        if not multiple_images:
            sources = [sources]

        for index, source in enumerate(sources):
            data = source.asarray()
            for image_operation in image_operations:
                data = image_operation(data)
            if multiple_images:
                root_path = str(index)
                root = zarr_root.create_group(root_path)
            else:
                root = zarr_root
            if index < len(translations):
                translation_um = translations[index]
            else:
                translation_um = source.get_position_micrometer()
            self.write_dataset(root, data, source, npyramid_add, pyramid_downsample, translation_um)
            if multiple_images:
                meta = root.attrs['multiscales'][0].copy()
                for dataset_meta in meta['datasets']:
                    dataset_meta['path'] = f'{root_path}/{dataset_meta["path"]}'
                multi_metadata.append(meta)
        if multiple_images:
            zarr_root.attrs['multiscales'] = multi_metadata
        zarr_root.attrs['omero'] = create_channel_metadata(sources[0], ome_version)

    def write_dataset(self, zarr_root, data, source,
                      npyramid_add=0, pyramid_downsample=2, translation_um=[]):

        pixel_size_um = source.get_pixel_size_micrometer()
        if len(pixel_size_um) == 0:
            npyramid_add = 0

        dimension_order = source.get_dimension_order()
        if 'c' in dimension_order and dimension_order.index('c') == len(dimension_order) - 1:
            # ome-zarr doesn't support channel after space dimensions (yet)
            data = np.moveaxis(data, -1, 0)
            dimension_order = dimension_order[-1] + dimension_order[:-1]

        axes = create_axes_metadata(dimension_order)

        pixel_size_scales = []
        scale = 1
        for i in range(npyramid_add + 1):
            pixel_size_scales.append(create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um))
            if pyramid_downsample:
                scale /= pyramid_downsample

        write_image(image=data, group=zarr_root, axes=axes, coordinate_transformations=pixel_size_scales,
                    scaler=Scaler(downscale=pyramid_downsample, max_layer=npyramid_add), overwrite=True)

filename = filename instance-attribute

__init__(filename)

Source code in OmeSliCC\OmeZarr.py
13
14
def __init__(self, filename):
    self.filename = filename

write(sources, tile_size=[], compression=[], npyramid_add=0, pyramid_downsample=2, translations=[], image_operations=[])

Source code in OmeSliCC\OmeZarr.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def write(self, sources, tile_size=[], compression=[],
          npyramid_add=0, pyramid_downsample=2, translations=[],
          image_operations=[]):

    compressor, compression_filters = create_compression_filter(compression)
    storage_options = {'dimension_separator': '/', 'chunks': tile_size}
    ome_version = '0.4'
    # Zarr V3 testing
    #storage_options = {'chunks': tile_size}
    if compressor is not None:
        storage_options['compressor'] = compressor
    if compression_filters is not None:
        storage_options['filters'] = compression_filters
    self.storage_options = storage_options

    zarr_root = zarr.open_group(store=parse_url(self.filename, mode="w").store, mode="w", storage_options=storage_options)
    # Zarr V3 testing
    #zarr_root = zarr.open_group(store=parse_url(self.filename, mode="w").store, mode="w", zarr_version=3)
    root_path = ''

    multiple_images = isinstance(sources, list)
    multi_metadata = []
    if not multiple_images:
        sources = [sources]

    for index, source in enumerate(sources):
        data = source.asarray()
        for image_operation in image_operations:
            data = image_operation(data)
        if multiple_images:
            root_path = str(index)
            root = zarr_root.create_group(root_path)
        else:
            root = zarr_root
        if index < len(translations):
            translation_um = translations[index]
        else:
            translation_um = source.get_position_micrometer()
        self.write_dataset(root, data, source, npyramid_add, pyramid_downsample, translation_um)
        if multiple_images:
            meta = root.attrs['multiscales'][0].copy()
            for dataset_meta in meta['datasets']:
                dataset_meta['path'] = f'{root_path}/{dataset_meta["path"]}'
            multi_metadata.append(meta)
    if multiple_images:
        zarr_root.attrs['multiscales'] = multi_metadata
    zarr_root.attrs['omero'] = create_channel_metadata(sources[0], ome_version)

write_dataset(zarr_root, data, source, npyramid_add=0, pyramid_downsample=2, translation_um=[])

Source code in OmeSliCC\OmeZarr.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def write_dataset(self, zarr_root, data, source,
                  npyramid_add=0, pyramid_downsample=2, translation_um=[]):

    pixel_size_um = source.get_pixel_size_micrometer()
    if len(pixel_size_um) == 0:
        npyramid_add = 0

    dimension_order = source.get_dimension_order()
    if 'c' in dimension_order and dimension_order.index('c') == len(dimension_order) - 1:
        # ome-zarr doesn't support channel after space dimensions (yet)
        data = np.moveaxis(data, -1, 0)
        dimension_order = dimension_order[-1] + dimension_order[:-1]

    axes = create_axes_metadata(dimension_order)

    pixel_size_scales = []
    scale = 1
    for i in range(npyramid_add + 1):
        pixel_size_scales.append(create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um))
        if pyramid_downsample:
            scale /= pyramid_downsample

    write_image(image=data, group=zarr_root, axes=axes, coordinate_transformations=pixel_size_scales,
                scaler=Scaler(downscale=pyramid_downsample, max_layer=npyramid_add), overwrite=True)

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_axes_metadata(dimension_order)

Source code in OmeSliCC\ome_zarr_util.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def create_axes_metadata(dimension_order):
    axes = []
    for dimension in dimension_order:
        unit1 = None
        if dimension == 't':
            type1 = 'time'
            unit1 = 'millisecond'
        elif dimension == 'c':
            type1 = 'channel'
        else:
            type1 = 'space'
            unit1 = 'micrometer'
        axis = {'name': dimension, 'type': type1}
        if unit1 is not None and unit1 != '':
            axis['unit'] = unit1
        axes.append(axis)
    return axes

create_channel_metadata(source, ome_version)

Source code in OmeSliCC\ome_zarr_util.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def create_channel_metadata(source, ome_version):
    channels = source.get_channels()
    nchannels = source.get_nchannels()

    if len(channels) < nchannels == 3:
        labels = ['Red', 'Green', 'Blue']
        colors = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)]
        channels = [{'label': label, 'color': color} for label, color in zip(labels, colors)]

    omezarr_channels = []
    for channeli, channel0 in enumerate(channels):
        channel = channel0.copy()
        color = channel.get('color', (1, 1, 1, 1))
        channel['color'] = rgba_to_hexrgb(color)
        if 'window' not in channel:
            channel['window'] = source.get_channel_window(channeli)
        omezarr_channels.append(channel)

    metadata = {
        'version': ome_version,
        'channels': omezarr_channels,
    }
    return metadata

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[])

Source code in OmeSliCC\ome_zarr_util.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[]):
    metadata = []
    pixel_size_scale = []
    translation_scale = []
    for dimension in dimension_order:
        if dimension == 'z' and len(pixel_size_um) > 2:
            pixel_size_scale1 = pixel_size_um[2]
        elif dimension == 'y' and len(pixel_size_um) > 1:
            pixel_size_scale1 = pixel_size_um[1] / scale
        elif dimension == 'x' and len(pixel_size_um) > 0:
            pixel_size_scale1 = pixel_size_um[0] / scale
        else:
            pixel_size_scale1 = 1
        if pixel_size_scale1 == 0:
            pixel_size_scale1 = 1
        pixel_size_scale.append(pixel_size_scale1)

        if dimension == 'z' and len(translation_um) > 2:
            translation1 = translation_um[2]
        elif dimension == 'y' and len(translation_um) > 1:
            translation1 = translation_um[1] * scale
        elif dimension == 'x' and len(translation_um) > 0:
            translation1 = translation_um[0] * scale
        else:
            translation1 = 0
        translation_scale.append(translation1)

    metadata.append({'type': 'scale', 'scale': pixel_size_scale})
    if not all(v == 0 for v in translation_scale):
        metadata.append({'type': 'translation', 'translation': translation_scale})
    return metadata

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

scale_dimensions_dict(shape0, scale)

Source code in OmeSliCC\ome_zarr_util.py
 92
 93
 94
 95
 96
 97
 98
 99
100
def scale_dimensions_dict(shape0, scale):
    shape = {}
    if scale == 1:
        return shape0
    for dimension, shape1 in shape0.items():
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape[dimension] = shape1
    return shape

scale_dimensions_xy(shape0, dimension_order, scale)

Source code in OmeSliCC\ome_zarr_util.py
81
82
83
84
85
86
87
88
89
def scale_dimensions_xy(shape0, dimension_order, scale):
    shape = []
    if scale == 1:
        return shape0
    for shape1, dimension in zip(shape0, dimension_order):
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape.append(shape1)
    return shape

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

OmeZarrSource

OmeZarrSource

Bases: OmeSource

Zarr-compatible image source

Source code in OmeSliCC\OmeZarrSource.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class OmeZarrSource(OmeSource):
    """Zarr-compatible image source"""

    filename: str
    """original filename / URL"""
    levels: list
    """list of all image arrays for different sizes"""
    level_scales: list
    """list of all image (xy) scales"""
    shapes: list
    """list of image shapes"""
    chunk_shapes: list
    """list of image chunk shapes"""

    def __init__(self, filename: str,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False):

        super().__init__()

        self.levels = []
        self.level_scales = []
        self.shapes = []
        self.chunk_shapes = []
        nchannels = 1
        try:
            location = parse_url(filename)
            if location is None:
                raise FileNotFoundError(f'Error parsing ome-zarr file {filename}')
            reader = Reader(location)
            # nodes may include images, labels etc
            nodes = list(reader())
            # first node will be the image pixel data
            if len(nodes) == 0:
                raise FileNotFoundError(f'No image data found in ome-zarr file {filename}')
            image_node = nodes[0]

            self.metadata = image_node.metadata
            # channel metadata from ome-zarr-py limited; get from root_attrs manually
            self.root_metadata = reader.zarr.root_attrs

            axes = self.metadata.get('axes', [])
            self.dimension_order = ''.join([axis.get('name') for axis in axes])

            for data in image_node.data:
                self.levels.append(data)

                xyzct = [1, 1, 1, 1, 1]
                for i, n in enumerate(data.shape):
                    xyzct_index = self.default_properties_order.index(self.dimension_order[i])
                    xyzct[xyzct_index] = n
                self.sizes_xyzct.append(xyzct)
                self.sizes.append((xyzct[0], xyzct[1]))
                self.pixel_types.append(data.dtype)
                self.pixel_nbits.append(data.dtype.itemsize * 8)
                self.level_scales.append(np.divide(self.sizes_xyzct[0][0], xyzct[0]))
                self.shapes.append(np.flip(reorder(data.shape, self.dimension_order, self.default_properties_order)))
                self.chunk_shapes.append(np.flip(reorder(data.chunksize, self.dimension_order, self.default_properties_order)))
                nchannels = xyzct[3]
        except Exception as e:
            raise FileNotFoundError(f'Read error: {e}')

        self.is_rgb = nchannels in (3, 4)

        self._init_metadata(filename,
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

    def _find_metadata(self):
        pixel_size = []
        position = []
        channels = []
        metadata = self.metadata
        axes = self.dimension_order

        units = [axis.get('unit', '') for axis in metadata.get('axes', [])]

        scale1 = [1] * len(metadata.get('axes'))
        position1 = [0] * len(metadata.get('axes'))
        # get pixelsize using largest/first scale
        transform = self.metadata.get('coordinateTransformations', [])
        if transform:
            for transform1 in transform[0]:
                if transform1['type'] == 'scale':
                    scale1 = transform1['scale']
                if transform1['type'] == 'translation':
                    position1 = transform1['translation']
            for axis in 'xyz':
                if axis in axes:
                    index = axes.index(axis)
                    pixel_size.append((scale1[index], units[index]))
                    position.append((position1[index], units[index]))
                else:
                    pixel_size.append((1, ''))
                    position.append((0, ''))
        nchannels = self.sizes_xyzct[0][3]
        # look for channel metadata
        for data in self.root_metadata.values():
            if isinstance(data, dict) and 'channels' in data:
                channels = data['channels'].copy()
                for channel in channels:
                    color = channel.pop('color', '')
                    if color != '':
                        if isinstance(color, int):
                            color = int_to_rgba(color)
                        else:
                            color = hexrgb_to_rgba(color)
                        channel['color'] = color
        if len(channels) == 0:
            if self.is_rgb:
                channels = [{'label': ''}]
            else:
                channels = [{'label': ''}] * nchannels
        self.source_pixel_size = pixel_size
        self.channels = channels
        self.source_mag = 0
        self.position = position

    def get_source_dask(self):
        return self.levels

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        redim = redimension_data(self.levels[level], self.dimension_order, self.get_dimension_order())
        slices = get_numpy_slicing(self.get_dimension_order(), **slicing)
        out = redim[slices]
        return out

chunk_shapes = [] instance-attribute

list of image chunk shapes

dimension_order = ''.join([axis.get('name') for axis in axes]) instance-attribute

filename instance-attribute

original filename / URL

is_rgb = nchannels in (3, 4) instance-attribute

level_scales = [] instance-attribute

list of all image (xy) scales

levels = [] instance-attribute

list of all image arrays for different sizes

metadata = image_node.metadata instance-attribute

root_metadata = reader.zarr.root_attrs instance-attribute

shapes = [] instance-attribute

list of image shapes

__init__(filename, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\OmeZarrSource.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def __init__(self, filename: str,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False):

    super().__init__()

    self.levels = []
    self.level_scales = []
    self.shapes = []
    self.chunk_shapes = []
    nchannels = 1
    try:
        location = parse_url(filename)
        if location is None:
            raise FileNotFoundError(f'Error parsing ome-zarr file {filename}')
        reader = Reader(location)
        # nodes may include images, labels etc
        nodes = list(reader())
        # first node will be the image pixel data
        if len(nodes) == 0:
            raise FileNotFoundError(f'No image data found in ome-zarr file {filename}')
        image_node = nodes[0]

        self.metadata = image_node.metadata
        # channel metadata from ome-zarr-py limited; get from root_attrs manually
        self.root_metadata = reader.zarr.root_attrs

        axes = self.metadata.get('axes', [])
        self.dimension_order = ''.join([axis.get('name') for axis in axes])

        for data in image_node.data:
            self.levels.append(data)

            xyzct = [1, 1, 1, 1, 1]
            for i, n in enumerate(data.shape):
                xyzct_index = self.default_properties_order.index(self.dimension_order[i])
                xyzct[xyzct_index] = n
            self.sizes_xyzct.append(xyzct)
            self.sizes.append((xyzct[0], xyzct[1]))
            self.pixel_types.append(data.dtype)
            self.pixel_nbits.append(data.dtype.itemsize * 8)
            self.level_scales.append(np.divide(self.sizes_xyzct[0][0], xyzct[0]))
            self.shapes.append(np.flip(reorder(data.shape, self.dimension_order, self.default_properties_order)))
            self.chunk_shapes.append(np.flip(reorder(data.chunksize, self.dimension_order, self.default_properties_order)))
            nchannels = xyzct[3]
    except Exception as e:
        raise FileNotFoundError(f'Read error: {e}')

    self.is_rgb = nchannels in (3, 4)

    self._init_metadata(filename,
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

_asarray_level(level, **slicing)

Source code in OmeSliCC\OmeZarrSource.py
134
135
136
137
138
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    redim = redimension_data(self.levels[level], self.dimension_order, self.get_dimension_order())
    slices = get_numpy_slicing(self.get_dimension_order(), **slicing)
    out = redim[slices]
    return out

_find_metadata()

Source code in OmeSliCC\OmeZarrSource.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def _find_metadata(self):
    pixel_size = []
    position = []
    channels = []
    metadata = self.metadata
    axes = self.dimension_order

    units = [axis.get('unit', '') for axis in metadata.get('axes', [])]

    scale1 = [1] * len(metadata.get('axes'))
    position1 = [0] * len(metadata.get('axes'))
    # get pixelsize using largest/first scale
    transform = self.metadata.get('coordinateTransformations', [])
    if transform:
        for transform1 in transform[0]:
            if transform1['type'] == 'scale':
                scale1 = transform1['scale']
            if transform1['type'] == 'translation':
                position1 = transform1['translation']
        for axis in 'xyz':
            if axis in axes:
                index = axes.index(axis)
                pixel_size.append((scale1[index], units[index]))
                position.append((position1[index], units[index]))
            else:
                pixel_size.append((1, ''))
                position.append((0, ''))
    nchannels = self.sizes_xyzct[0][3]
    # look for channel metadata
    for data in self.root_metadata.values():
        if isinstance(data, dict) and 'channels' in data:
            channels = data['channels'].copy()
            for channel in channels:
                color = channel.pop('color', '')
                if color != '':
                    if isinstance(color, int):
                        color = int_to_rgba(color)
                    else:
                        color = hexrgb_to_rgba(color)
                    channel['color'] = color
    if len(channels) == 0:
        if self.is_rgb:
            channels = [{'label': ''}]
        else:
            channels = [{'label': ''}] * nchannels
    self.source_pixel_size = pixel_size
    self.channels = channels
    self.source_mag = 0
    self.position = position

get_source_dask()

Source code in OmeSliCC\OmeZarrSource.py
131
132
def get_source_dask(self):
    return self.levels

Omero

Omero

Omero image and metadata extraction

Source code in OmeSliCC\Omero.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
class Omero:
    """Omero image and metadata extraction"""

    def __init__(self, params: dict):
        self.params = params
        self.private_key_filename = params['credentials']['private_key']
        self.credentials_filename = params['credentials']['credentials']
        self.connected = False

    def __enter__(self) -> Omero:
        self.init()
        return self

    def __exit__(self, exc_type: type[BaseException], exc_value: BaseException, traceback: TracebackType):
        self.close()

    def init(self):
        self._connect()
        self._switch_user_group()

    def close(self):
        self._disconnect()

    def _connect(self):
        logging.info('Connecting to Omero...')
        usr, pwd = decrypt_credentials(self.private_key_filename, self.credentials_filename)
        self.conn = BlitzGateway(usr, pwd, host=self.params['input']['omero']['host'], secure=True)
        if not self.conn.connect():
            self._disconnect()
            logging.error('Omero connection error')
            raise ConnectionError
        self.conn.c.enableKeepAlive(60)
        self.connected = True
        logging.info(f'Connected as {self.conn.getUser().getName()}')

    def _disconnect(self):
        self.conn.close()
        self.connected = False

    def _switch_user_group(self):
        self.conn.SERVICE_OPTS.setOmeroGroup('-1')

    def _get_project(self, project_id: int) -> omero.gateway.ProjectWrapper:
        project = self.conn.getObject('Project', project_id)
        return project

    def _get_dataset(self, dataset_id: int) -> omero.gateway.DatasetWrapper:
        dataset = self.conn.getObject('Dataset', dataset_id)
        return dataset

    def get_image_object(self, image_id: int) -> omero.gateway.ImageWrapper:
        image_object = self.conn.getObject('Image', image_id)
        return image_object

    def create_pixels_store(self, image_object: omero.gateway.ImageWrapper) -> omero.gateway.ProxyObjectWrapper:
        pixels_store = self.conn.createRawPixelsStore()
        pixels_store.setPixelsId(image_object.getPixelsId(), False, self.conn.SERVICE_OPTS)
        return pixels_store

    def get_annotation_image_ids(self) -> dict:
        images_final = {}
        input_omero = self.params['input'].get('omero', {})
        include_params = input_omero['include']
        include_regex = ensure_list(include_params.get('regex', []))
        exclude_params = input_omero.get('exclude', {})
        exclude_regex = ensure_list(exclude_params.get('regex', []))
        # include
        image_ids = set(ensure_list(include_params.get('image', [])))
        images = {image_id: self.get_image_object(image_id) for image_id in image_ids}
        for dataset_id in ensure_list(include_params.get('dataset', [])):
            images.update(self._get_dataset_images(dataset_id))
        for project_id in ensure_list(include_params.get('project', [])):
            project = self._get_project(project_id)
            for dataset in project.listChildren():
                images.update(self._get_dataset_images(dataset.getId()))
        # exclude
        for image_id in ensure_list(exclude_params.get('image', [])):
            images.pop(image_id, None)
        for dataset_id in ensure_list(exclude_params.get('dataset', [])):
            for image_id in self._get_dataset_images(dataset_id):
                images.pop(image_id, None)
        for project_id in ensure_list(exclude_params.get('project', [])):
            project = self._get_project(project_id)
            for dataset in project.listChildren():
                for image_id in self._get_dataset_images(dataset.getId()):
                    images.pop(image_id, None)

        # regex
        for image_id, image in images.items():
            name = image.getName()
            include = True
            if include_regex:
                include = False
                for pattern in include_regex:
                    if re.search(pattern, name, re.IGNORECASE):
                        include = True
            if exclude_regex:
                for pattern in exclude_regex:
                    if re.search(pattern, name, re.IGNORECASE):
                        include = False
            if include:
                images_final[image_id] = image
        return images_final

    def _get_dataset_images(self, dataset_id: int) -> dict:
        dataset = self._get_dataset(dataset_id)
        return {image.getId(): image for image in dataset.listChildren()}

    def get_image_annotation(self, image_id: int, target_labels: list) -> tuple:
        image_object = self.get_image_object(image_id)
        name = image_object.getName()
        annotations = self._get_image_annotations(image_object, target_labels)
        return name, annotations

    def _get_image_annotations(self, image_object: omero.gateway.ImageWrapper, annotation_keys: list) -> dict:
        annotations = {}
        for omero_annotation in image_object.listAnnotations():
            if omero_annotation.OMERO_TYPE == omero.model.MapAnnotationI:
                for annotation_key in annotation_keys:
                    for annotation in omero_annotation.getMapValue():
                        if annotation.name.lower() == annotation_key.lower():
                            annotations[annotation_key] = annotation.value
        return annotations

    def print_projects(self):
        projects = self.conn.listProjects()      # may include other users' data
        for project in projects:
            print_omero_object(project)

connected = False instance-attribute

credentials_filename = params['credentials']['credentials'] instance-attribute

params = params instance-attribute

private_key_filename = params['credentials']['private_key'] instance-attribute

__enter__()

Source code in OmeSliCC\Omero.py
22
23
24
def __enter__(self) -> Omero:
    self.init()
    return self

__exit__(exc_type, exc_value, traceback)

Source code in OmeSliCC\Omero.py
26
27
def __exit__(self, exc_type: type[BaseException], exc_value: BaseException, traceback: TracebackType):
    self.close()

__init__(params)

Source code in OmeSliCC\Omero.py
16
17
18
19
20
def __init__(self, params: dict):
    self.params = params
    self.private_key_filename = params['credentials']['private_key']
    self.credentials_filename = params['credentials']['credentials']
    self.connected = False

_connect()

Source code in OmeSliCC\Omero.py
36
37
38
39
40
41
42
43
44
45
46
def _connect(self):
    logging.info('Connecting to Omero...')
    usr, pwd = decrypt_credentials(self.private_key_filename, self.credentials_filename)
    self.conn = BlitzGateway(usr, pwd, host=self.params['input']['omero']['host'], secure=True)
    if not self.conn.connect():
        self._disconnect()
        logging.error('Omero connection error')
        raise ConnectionError
    self.conn.c.enableKeepAlive(60)
    self.connected = True
    logging.info(f'Connected as {self.conn.getUser().getName()}')

_disconnect()

Source code in OmeSliCC\Omero.py
48
49
50
def _disconnect(self):
    self.conn.close()
    self.connected = False

_get_dataset(dataset_id)

Source code in OmeSliCC\Omero.py
59
60
61
def _get_dataset(self, dataset_id: int) -> omero.gateway.DatasetWrapper:
    dataset = self.conn.getObject('Dataset', dataset_id)
    return dataset

_get_dataset_images(dataset_id)

Source code in OmeSliCC\Omero.py
117
118
119
def _get_dataset_images(self, dataset_id: int) -> dict:
    dataset = self._get_dataset(dataset_id)
    return {image.getId(): image for image in dataset.listChildren()}

_get_image_annotations(image_object, annotation_keys)

Source code in OmeSliCC\Omero.py
127
128
129
130
131
132
133
134
135
def _get_image_annotations(self, image_object: omero.gateway.ImageWrapper, annotation_keys: list) -> dict:
    annotations = {}
    for omero_annotation in image_object.listAnnotations():
        if omero_annotation.OMERO_TYPE == omero.model.MapAnnotationI:
            for annotation_key in annotation_keys:
                for annotation in omero_annotation.getMapValue():
                    if annotation.name.lower() == annotation_key.lower():
                        annotations[annotation_key] = annotation.value
    return annotations

_get_project(project_id)

Source code in OmeSliCC\Omero.py
55
56
57
def _get_project(self, project_id: int) -> omero.gateway.ProjectWrapper:
    project = self.conn.getObject('Project', project_id)
    return project

_switch_user_group()

Source code in OmeSliCC\Omero.py
52
53
def _switch_user_group(self):
    self.conn.SERVICE_OPTS.setOmeroGroup('-1')

close()

Source code in OmeSliCC\Omero.py
33
34
def close(self):
    self._disconnect()

create_pixels_store(image_object)

Source code in OmeSliCC\Omero.py
67
68
69
70
def create_pixels_store(self, image_object: omero.gateway.ImageWrapper) -> omero.gateway.ProxyObjectWrapper:
    pixels_store = self.conn.createRawPixelsStore()
    pixels_store.setPixelsId(image_object.getPixelsId(), False, self.conn.SERVICE_OPTS)
    return pixels_store

get_annotation_image_ids()

Source code in OmeSliCC\Omero.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def get_annotation_image_ids(self) -> dict:
    images_final = {}
    input_omero = self.params['input'].get('omero', {})
    include_params = input_omero['include']
    include_regex = ensure_list(include_params.get('regex', []))
    exclude_params = input_omero.get('exclude', {})
    exclude_regex = ensure_list(exclude_params.get('regex', []))
    # include
    image_ids = set(ensure_list(include_params.get('image', [])))
    images = {image_id: self.get_image_object(image_id) for image_id in image_ids}
    for dataset_id in ensure_list(include_params.get('dataset', [])):
        images.update(self._get_dataset_images(dataset_id))
    for project_id in ensure_list(include_params.get('project', [])):
        project = self._get_project(project_id)
        for dataset in project.listChildren():
            images.update(self._get_dataset_images(dataset.getId()))
    # exclude
    for image_id in ensure_list(exclude_params.get('image', [])):
        images.pop(image_id, None)
    for dataset_id in ensure_list(exclude_params.get('dataset', [])):
        for image_id in self._get_dataset_images(dataset_id):
            images.pop(image_id, None)
    for project_id in ensure_list(exclude_params.get('project', [])):
        project = self._get_project(project_id)
        for dataset in project.listChildren():
            for image_id in self._get_dataset_images(dataset.getId()):
                images.pop(image_id, None)

    # regex
    for image_id, image in images.items():
        name = image.getName()
        include = True
        if include_regex:
            include = False
            for pattern in include_regex:
                if re.search(pattern, name, re.IGNORECASE):
                    include = True
        if exclude_regex:
            for pattern in exclude_regex:
                if re.search(pattern, name, re.IGNORECASE):
                    include = False
        if include:
            images_final[image_id] = image
    return images_final

get_image_annotation(image_id, target_labels)

Source code in OmeSliCC\Omero.py
121
122
123
124
125
def get_image_annotation(self, image_id: int, target_labels: list) -> tuple:
    image_object = self.get_image_object(image_id)
    name = image_object.getName()
    annotations = self._get_image_annotations(image_object, target_labels)
    return name, annotations

get_image_object(image_id)

Source code in OmeSliCC\Omero.py
63
64
65
def get_image_object(self, image_id: int) -> omero.gateway.ImageWrapper:
    image_object = self.conn.getObject('Image', image_id)
    return image_object

init()

Source code in OmeSliCC\Omero.py
29
30
31
def init(self):
    self._connect()
    self._switch_user_group()

print_projects()

Source code in OmeSliCC\Omero.py
137
138
139
140
def print_projects(self):
    projects = self.conn.listProjects()      # may include other users' data
    for project in projects:
        print_omero_object(project)

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

print_omero_object(omero_object, indent=0)

Source code in OmeSliCC\Omero.py
143
144
145
146
147
148
149
150
151
152
def print_omero_object(omero_object: omero.gateway.BlitzObjectWrapper, indent: int = 0):
    logging.info("""%s%s:%s  Name:"%s" (owner=%s)""" % (
        " " * indent,
        omero_object.OMERO_CLASS,
        omero_object.getId(),
        omero_object.getName(),
        omero_object.getOwnerOmeName()))

    for child in omero_object.listChildren():
        logging.info('\t', child.getName())

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

OmeroLabelReader

OmeroLabelReader

Omero metadata extraction to label file

Source code in OmeSliCC\OmeroLabelReader.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class OmeroLabelReader:
    """Omero metadata extraction to label file"""

    params: dict
    """input parameters"""
    omero: Omero
    """Omero instance"""
    manage_omero: bool
    """If responsible for managing Omero instance"""

    def __init__(self, params: dict, omero: Omero = None):
        self.params = params
        self.manage_omero = (omero is None)
        if self.manage_omero:
            self.omero = Omero(params)
        else:
            self.omero = omero

    def __enter__(self) -> OmeroLabelReader:
        if self.manage_omero:
            self.omero.init()
        return self

    def __exit__(self, exc_type: type[BaseException], exc_value: BaseException, traceback: TracebackType):
        if self.manage_omero:
            self.omero.close()

    def create_label_csv(self, image_ids):
        image_names = []
        image_annotations = []
        input_params = self.params['input']
        output_params = self.params['output']
        input_labels = input_params.get('omero', {}).get('labels', [])
        logging.info(f'Matching images: {len(image_ids)}')
        for image_id in image_ids:
            name, annotations = self.omero.get_image_annotation(image_id, input_labels)
            image_names.append(name)
            image_annotations.append(annotations)
        df = pd.DataFrame(index=image_ids, data=image_annotations)
        df.index.name = 'omero_id'
        for input_label in input_labels:
            if input_label in df:
                logging.info(f'Label {input_label}:\n' + df[input_label].value_counts().to_string())
        df.insert(0, 'omero_name', image_names)
        df['path'] = [image_name + '.' + output_params['format'] for image_name in image_names]
        log_path = os.path.dirname(output_params['csv'])
        if not os.path.exists(log_path):
            os.makedirs(log_path)
        df.to_csv(output_params['csv'])

manage_omero = omero is None instance-attribute

If responsible for managing Omero instance

omero instance-attribute

Omero instance

params = params instance-attribute

input parameters

__enter__()

Source code in OmeSliCC\OmeroLabelReader.py
28
29
30
31
def __enter__(self) -> OmeroLabelReader:
    if self.manage_omero:
        self.omero.init()
    return self

__exit__(exc_type, exc_value, traceback)

Source code in OmeSliCC\OmeroLabelReader.py
33
34
35
def __exit__(self, exc_type: type[BaseException], exc_value: BaseException, traceback: TracebackType):
    if self.manage_omero:
        self.omero.close()

__init__(params, omero=None)

Source code in OmeSliCC\OmeroLabelReader.py
20
21
22
23
24
25
26
def __init__(self, params: dict, omero: Omero = None):
    self.params = params
    self.manage_omero = (omero is None)
    if self.manage_omero:
        self.omero = Omero(params)
    else:
        self.omero = omero

create_label_csv(image_ids)

Source code in OmeSliCC\OmeroLabelReader.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def create_label_csv(self, image_ids):
    image_names = []
    image_annotations = []
    input_params = self.params['input']
    output_params = self.params['output']
    input_labels = input_params.get('omero', {}).get('labels', [])
    logging.info(f'Matching images: {len(image_ids)}')
    for image_id in image_ids:
        name, annotations = self.omero.get_image_annotation(image_id, input_labels)
        image_names.append(name)
        image_annotations.append(annotations)
    df = pd.DataFrame(index=image_ids, data=image_annotations)
    df.index.name = 'omero_id'
    for input_label in input_labels:
        if input_label in df:
            logging.info(f'Label {input_label}:\n' + df[input_label].value_counts().to_string())
    df.insert(0, 'omero_name', image_names)
    df['path'] = [image_name + '.' + output_params['format'] for image_name in image_names]
    log_path = os.path.dirname(output_params['csv'])
    if not os.path.exists(log_path):
        os.makedirs(log_path)
    df.to_csv(output_params['csv'])

OmeroSource

OmeroSource

Bases: OmeSource

Omero image source

Source code in OmeSliCC\OmeroSource.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class OmeroSource(OmeSource):
    """Omero image source"""

    omero: Omero
    """Omero instance"""
    image_id: int
    """Omero image id"""
    image_object: omero.gateway.ImageWrapper
    """Omero image object"""
    pixels_store: omero.gateway.ProxyObjectWrapper
    """Raw pixels store object"""
    pixels_store_pyramid_order: list
    """Raw pixels store pyramid sizes order (pixel store level order not guaranteed) """

    def __init__(self,
                 omero: Omero,
                 image_id: int,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False):

        super().__init__()
        self.omero = omero
        self.image_id = image_id
        image_object = self.omero.get_image_object(image_id)
        self.image_object = image_object

        zsize = get_default(image_object.getSizeZ(), 1)
        nchannels = np.sum([channel.getLogicalChannel().getSamplesPerPixel() for channel in image_object.getChannels()])
        pixel_type = np.dtype(image_object.getPixelsType())

        self.pixels_store = self.omero.create_pixels_store(image_object)
        for resolution in self.pixels_store.getResolutionDescriptions():
            self.sizes.append((resolution.sizeX, resolution.sizeY))
            self.sizes_xyzct.append((resolution.sizeX, resolution.sizeY, zsize, nchannels, 1))
            self.pixel_types.append(pixel_type)
            self.pixel_nbits.append(pixel_type.itemsize * 8)

        if not self.sizes:
            xsize, ysize = image_object.getSizeX(), image_object.getSizeY()
            self.sizes.append((xsize, ysize))
            self.sizes_xyzct.append((xsize, ysize, zsize, nchannels, 1))
            self.pixel_types.append(pixel_type)
            self.pixel_nbits.append(pixel_type.itemsize * 8)

        # Omero API issue: pixel store level order not guaranteed
        default_level = self.pixels_store.getResolutionLevel()
        nlevels = self.pixels_store.getResolutionLevels()
        if default_level != 0:
            # reverse order
            self.pixels_store_pyramid_order = list(reversed(range(nlevels)))
        else:
            # default order
            self.pixels_store_pyramid_order = list(range(nlevels))

        self.is_rgb = nchannels in (3, 4)

        self._init_metadata(image_object.getName(),
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

        # currently only support/output yxc
        self.dimension_order = 'yxc'

    def _find_metadata(self):
        image_object = self.image_object
        self.source_pixel_size = [(get_default(image_object.getPixelSizeX(), 0), self.default_physical_unit),
                                  (get_default(image_object.getPixelSizeY(), 0), self.default_physical_unit),
                                  (get_default(image_object.getPixelSizeZ(), 0), self.default_physical_unit)]
        objective_settings = image_object.getObjectiveSettings()
        if objective_settings:
            self.source_mag = objective_settings.getObjective().getNominalMagnification()
        else:
            self.source_mag = 0
        self.channels = []
        for channeli, channel0 in enumerate(image_object.getChannels()):
            channel = {'label': get_default(channel0.getName(), str(channeli)),
                       'color': int_to_rgba(channel0.getColor().getInt())}
            self.channels.append(channel)

    def create_xml_metadata(self, output_filename: str, combine_rgb: bool = True, pyramid_sizes_add: list = None) -> str:
        return create_ome_metadata_from_omero(self, self.image_object, output_filename, combine_rgb=combine_rgb,
                                              pyramid_sizes_add=pyramid_sizes_add)

    def get_thumbnail(self, target_size: tuple, precise: bool = False) -> np.ndarray:
        image_bytes = self.image_object.getThumbnail(target_size)
        image_stream = io.BytesIO(image_bytes)
        image = np.array(PIL.Image.open(image_stream))
        return image

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
        y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
        c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
        if x1 < 0 or y1 < 0:
            x1, y1 = self.sizes[level]
        if t is None:
            t = 0
        if z is None:
            z = 0

        w, h = x1 - x0, y1 - y0
        if c is not None:
            channels = [c]
        else:
            channels = range(self.get_nchannels())
        shape = h, w, len(channels)
        image = np.zeros(shape, dtype=self.pixel_types[level])
        pixels_store = self.pixels_store
        pixels_store_level = self.pixels_store_pyramid_order[level]
        if pixels_store.getResolutionLevel() != pixels_store_level:
            pixels_store.setResolutionLevel(pixels_store_level)
        for c in channels:
            tile0 = pixels_store.getTile(z, c, t, x0, y0, w, h)
            tile = np.frombuffer(tile0, dtype=image.dtype).reshape(h, w)
            image[..., c] = tile

        out = redimension_data(image, self.dimension_order, self.get_dimension_order())
        return out

    def close(self):
        self.pixels_store.close()

dimension_order = 'yxc' instance-attribute

image_id = image_id instance-attribute

Omero image id

image_object = image_object instance-attribute

Omero image object

is_rgb = nchannels in (3, 4) instance-attribute

omero = omero instance-attribute

Omero instance

pixels_store = self.omero.create_pixels_store(image_object) instance-attribute

Raw pixels store object

pixels_store_pyramid_order instance-attribute

Raw pixels store pyramid sizes order (pixel store level order not guaranteed)

__init__(omero, image_id, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\OmeroSource.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def __init__(self,
             omero: Omero,
             image_id: int,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False):

    super().__init__()
    self.omero = omero
    self.image_id = image_id
    image_object = self.omero.get_image_object(image_id)
    self.image_object = image_object

    zsize = get_default(image_object.getSizeZ(), 1)
    nchannels = np.sum([channel.getLogicalChannel().getSamplesPerPixel() for channel in image_object.getChannels()])
    pixel_type = np.dtype(image_object.getPixelsType())

    self.pixels_store = self.omero.create_pixels_store(image_object)
    for resolution in self.pixels_store.getResolutionDescriptions():
        self.sizes.append((resolution.sizeX, resolution.sizeY))
        self.sizes_xyzct.append((resolution.sizeX, resolution.sizeY, zsize, nchannels, 1))
        self.pixel_types.append(pixel_type)
        self.pixel_nbits.append(pixel_type.itemsize * 8)

    if not self.sizes:
        xsize, ysize = image_object.getSizeX(), image_object.getSizeY()
        self.sizes.append((xsize, ysize))
        self.sizes_xyzct.append((xsize, ysize, zsize, nchannels, 1))
        self.pixel_types.append(pixel_type)
        self.pixel_nbits.append(pixel_type.itemsize * 8)

    # Omero API issue: pixel store level order not guaranteed
    default_level = self.pixels_store.getResolutionLevel()
    nlevels = self.pixels_store.getResolutionLevels()
    if default_level != 0:
        # reverse order
        self.pixels_store_pyramid_order = list(reversed(range(nlevels)))
    else:
        # default order
        self.pixels_store_pyramid_order = list(range(nlevels))

    self.is_rgb = nchannels in (3, 4)

    self._init_metadata(image_object.getName(),
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

    # currently only support/output yxc
    self.dimension_order = 'yxc'

_asarray_level(level, **slicing)

Source code in OmeSliCC\OmeroSource.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
    y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
    c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
    if x1 < 0 or y1 < 0:
        x1, y1 = self.sizes[level]
    if t is None:
        t = 0
    if z is None:
        z = 0

    w, h = x1 - x0, y1 - y0
    if c is not None:
        channels = [c]
    else:
        channels = range(self.get_nchannels())
    shape = h, w, len(channels)
    image = np.zeros(shape, dtype=self.pixel_types[level])
    pixels_store = self.pixels_store
    pixels_store_level = self.pixels_store_pyramid_order[level]
    if pixels_store.getResolutionLevel() != pixels_store_level:
        pixels_store.setResolutionLevel(pixels_store_level)
    for c in channels:
        tile0 = pixels_store.getTile(z, c, t, x0, y0, w, h)
        tile = np.frombuffer(tile0, dtype=image.dtype).reshape(h, w)
        image[..., c] = tile

    out = redimension_data(image, self.dimension_order, self.get_dimension_order())
    return out

_find_metadata()

Source code in OmeSliCC\OmeroSource.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def _find_metadata(self):
    image_object = self.image_object
    self.source_pixel_size = [(get_default(image_object.getPixelSizeX(), 0), self.default_physical_unit),
                              (get_default(image_object.getPixelSizeY(), 0), self.default_physical_unit),
                              (get_default(image_object.getPixelSizeZ(), 0), self.default_physical_unit)]
    objective_settings = image_object.getObjectiveSettings()
    if objective_settings:
        self.source_mag = objective_settings.getObjective().getNominalMagnification()
    else:
        self.source_mag = 0
    self.channels = []
    for channeli, channel0 in enumerate(image_object.getChannels()):
        channel = {'label': get_default(channel0.getName(), str(channeli)),
                   'color': int_to_rgba(channel0.getColor().getInt())}
        self.channels.append(channel)

close()

Source code in OmeSliCC\OmeroSource.py
135
136
def close(self):
    self.pixels_store.close()

create_xml_metadata(output_filename, combine_rgb=True, pyramid_sizes_add=None)

Source code in OmeSliCC\OmeroSource.py
95
96
97
def create_xml_metadata(self, output_filename: str, combine_rgb: bool = True, pyramid_sizes_add: list = None) -> str:
    return create_ome_metadata_from_omero(self, self.image_object, output_filename, combine_rgb=combine_rgb,
                                          pyramid_sizes_add=pyramid_sizes_add)

get_thumbnail(target_size, precise=False)

Source code in OmeSliCC\OmeroSource.py
 99
100
101
102
103
def get_thumbnail(self, target_size: tuple, precise: bool = False) -> np.ndarray:
    image_bytes = self.image_object.getThumbnail(target_size)
    image_stream = io.BytesIO(image_bytes)
    image = np.array(PIL.Image.open(image_stream))
    return image

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

PlainImageSource

PlainImageSource

Bases: OmeSource

Plain common format image source

Source code in OmeSliCC\PlainImageSource.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
class PlainImageSource(OmeSource):
    """Plain common format image source"""

    filename: str
    """original filename"""
    loaded: bool
    """if image data is loaded"""
    arrays: list
    """list of all image arrays for different sizes"""

    def __init__(self,
                 filename: str,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False):

        super().__init__()
        self.loaded = False
        self.arrays = []

        self.image = Image.open(filename)
        self.metadata = get_pil_metadata(self.image)
        size = (self.image.width, self.image.height)
        self.sizes = [size]
        nchannels = len(self.image.getbands())
        size_xyzct = (self.image.width, self.image.height, self.image.n_frames, nchannels, 1)
        self.sizes_xyzct = [size_xyzct]
        pixelinfo = pilmode_to_pixelinfo(self.image.mode)
        self.pixel_types = [pixelinfo[0]]
        self.pixel_nbits = [pixelinfo[1]]

        dimension_order = 'yx'
        if self.image.n_frames > 1:
            dimension_order = 'z' + dimension_order
        if nchannels > 1:
            dimension_order += 'c'
        self.dimension_order = dimension_order

        self.is_rgb = nchannels in (3, 4)

        self._init_metadata(filename,
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

    def _find_metadata(self):
        self.source_pixel_size = []
        pixel_size_unit = None
        pixel_size_z = None

        description = self.metadata.get('ImageDescription', '')
        if description != '':
            metadata = desc_to_dict(description)
            if 'spacing' in metadata:
                pixel_size_unit = metadata.get('unit', '')
                if not isinstance(pixel_size_unit, str):
                    pixel_size_unit = 'micrometer'
                pixel_size_z = metadata['spacing']
        if not pixel_size_unit:
            pixel_size_unit = self.metadata.get('ResolutionUnit')
            if pixel_size_unit is not None:
                pixel_size_unit = str(RESUNIT(pixel_size_unit).name).lower()
                if pixel_size_unit == 'none':
                    pixel_size_unit = ''
        res0 = self.metadata.get('XResolution')
        if res0 is not None:
            self.source_pixel_size.append((1 / float(res0), pixel_size_unit))
        res0 = self.metadata.get('YResolution')
        if res0 is not None:
            self.source_pixel_size.append((1 / float(res0), pixel_size_unit))
        if pixel_size_z is not None:
            self.source_pixel_size.append((pixel_size_z, pixel_size_unit))
        self.source_mag = self.metadata.get('Mag', 0)
        self.channels = [{'label': ''}]

    def load(self):
        self.unload()
        for level in range(len(self.sizes)):
            self.arrays.append(self._asarray_level(level))
        self.loaded = True

    def unload(self):
        for array in self.arrays:
            del array
        self.arrays = []
        self.loaded = False

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        nframes = self.image.n_frames
        if self.loaded:
            image = self.arrays[level]
        elif nframes > 1:
            shape = [nframes] + list(np.array(self.image).shape)
            image = np.zeros(shape, dtype=self.pixel_types[level])
            for framei in range(nframes):
                self.image.seek(framei)
                image[framei] = np.array(self.image)
        else:
            image = np.array(self.image)

        redim = redimension_data(image, self.dimension_order, self.get_dimension_order())
        slicing = get_numpy_slicing(self.get_dimension_order(), **slicing)
        out = redim[slicing]
        return out

arrays = [] instance-attribute

list of all image arrays for different sizes

dimension_order = dimension_order instance-attribute

filename instance-attribute

original filename

image = Image.open(filename) instance-attribute

is_rgb = nchannels in (3, 4) instance-attribute

loaded = False instance-attribute

if image data is loaded

metadata = get_pil_metadata(self.image) instance-attribute

pixel_nbits = [pixelinfo[1]] instance-attribute

pixel_types = [pixelinfo[0]] instance-attribute

sizes = [size] instance-attribute

sizes_xyzct = [size_xyzct] instance-attribute

__init__(filename, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\PlainImageSource.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def __init__(self,
             filename: str,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False):

    super().__init__()
    self.loaded = False
    self.arrays = []

    self.image = Image.open(filename)
    self.metadata = get_pil_metadata(self.image)
    size = (self.image.width, self.image.height)
    self.sizes = [size]
    nchannels = len(self.image.getbands())
    size_xyzct = (self.image.width, self.image.height, self.image.n_frames, nchannels, 1)
    self.sizes_xyzct = [size_xyzct]
    pixelinfo = pilmode_to_pixelinfo(self.image.mode)
    self.pixel_types = [pixelinfo[0]]
    self.pixel_nbits = [pixelinfo[1]]

    dimension_order = 'yx'
    if self.image.n_frames > 1:
        dimension_order = 'z' + dimension_order
    if nchannels > 1:
        dimension_order += 'c'
    self.dimension_order = dimension_order

    self.is_rgb = nchannels in (3, 4)

    self._init_metadata(filename,
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

_asarray_level(level, **slicing)

Source code in OmeSliCC\PlainImageSource.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    nframes = self.image.n_frames
    if self.loaded:
        image = self.arrays[level]
    elif nframes > 1:
        shape = [nframes] + list(np.array(self.image).shape)
        image = np.zeros(shape, dtype=self.pixel_types[level])
        for framei in range(nframes):
            self.image.seek(framei)
            image[framei] = np.array(self.image)
    else:
        image = np.array(self.image)

    redim = redimension_data(image, self.dimension_order, self.get_dimension_order())
    slicing = get_numpy_slicing(self.get_dimension_order(), **slicing)
    out = redim[slicing]
    return out

_find_metadata()

Source code in OmeSliCC\PlainImageSource.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def _find_metadata(self):
    self.source_pixel_size = []
    pixel_size_unit = None
    pixel_size_z = None

    description = self.metadata.get('ImageDescription', '')
    if description != '':
        metadata = desc_to_dict(description)
        if 'spacing' in metadata:
            pixel_size_unit = metadata.get('unit', '')
            if not isinstance(pixel_size_unit, str):
                pixel_size_unit = 'micrometer'
            pixel_size_z = metadata['spacing']
    if not pixel_size_unit:
        pixel_size_unit = self.metadata.get('ResolutionUnit')
        if pixel_size_unit is not None:
            pixel_size_unit = str(RESUNIT(pixel_size_unit).name).lower()
            if pixel_size_unit == 'none':
                pixel_size_unit = ''
    res0 = self.metadata.get('XResolution')
    if res0 is not None:
        self.source_pixel_size.append((1 / float(res0), pixel_size_unit))
    res0 = self.metadata.get('YResolution')
    if res0 is not None:
        self.source_pixel_size.append((1 / float(res0), pixel_size_unit))
    if pixel_size_z is not None:
        self.source_pixel_size.append((pixel_size_z, pixel_size_unit))
    self.source_mag = self.metadata.get('Mag', 0)
    self.channels = [{'label': ''}]

load()

Source code in OmeSliCC\PlainImageSource.py
86
87
88
89
90
def load(self):
    self.unload()
    for level in range(len(self.sizes)):
        self.arrays.append(self._asarray_level(level))
    self.loaded = True

unload()

Source code in OmeSliCC\PlainImageSource.py
92
93
94
95
96
def unload(self):
    for array in self.arrays:
        del array
    self.arrays = []
    self.loaded = False

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

TiffSource

TiffSource

Bases: OmeSource

Tiff-compatible image source

Source code in OmeSliCC\TiffSource.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
class TiffSource(OmeSource):
    """Tiff-compatible image source"""

    filename: str
    """original filename"""
    compressed: bool
    """if image data is loaded compressed"""
    decompressed: bool
    """if image data is loaded decompressed"""
    pages: list
    """list of all relevant TiffPages"""
    data: bytes
    """raw un-decoded image byte data"""
    arrays: list
    """list of all image arrays for different sizes"""

    def __init__(self,
                 filename: str,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False,
                 executor: ThreadPoolExecutor = None):

        super().__init__()
        self.compressed = False
        self.decompressed = False
        self.executor = executor
        self.data = bytes()
        self.arrays = []
        photometric = None
        nchannels = 1

        tiff = TiffFile(filename)
        self.tiff = tiff
        self.first_page = tiff.pages.first
        if tiff.is_ome and tiff.ome_metadata is not None:
            xml_metadata = tiff.ome_metadata
            self.metadata = XmlDict.xml2dict(xml_metadata)
            if 'OME' in self.metadata:
                self.metadata = self.metadata['OME']
                self.has_ome_metadata = True
            if 'BinaryOnly' in self.metadata:
                # binary image only; get metadata from metadata file instead
                self.has_ome_metadata = False
                metdata_filename = os.path.join(os.path.dirname(filename),
                                                self.metadata['BinaryOnly'].get('MetadataFile'))
                if os.path.isfile(metdata_filename):
                    metdata_tiff = TiffFile(metdata_filename)
                    if metdata_tiff.is_ome and metdata_tiff.ome_metadata is not None:
                        xml_metadata = metdata_tiff.ome_metadata
                        self.metadata = XmlDict.xml2dict(xml_metadata)
                        if 'OME' in self.metadata:
                            self.metadata = self.metadata['OME']
                            images = self.metadata.get('Image')
                            if isinstance(images, list):
                                for image in images:
                                    if image.get('Name', '').lower() == get_filetitle(filename).lower():
                                        self.metadata['Image'] = image
                                        break
                            self.has_ome_metadata = True

        if self.has_ome_metadata:
            pass
        elif tiff.is_imagej:
            self.metadata = tiff.imagej_metadata
        elif self.first_page.description:
            self.metadata = desc_to_dict(self.first_page.description)
        self.tags = tags_to_dict(self.first_page.tags)
        if 'FEI_TITAN' in self.tags:
            metadata = tifffile.xml2dict(self.tags.pop('FEI_TITAN'))
            if 'FeiImage' in metadata:
                metadata = metadata['FeiImage']
            self.metadata.update(metadata)

        if tiff.series:
            series0 = tiff.series[0]
            self.dimension_order = series0.axes
            photometric = series0.keyframe.photometric
        self.pages = get_tiff_pages(tiff)
        for page0 in self.pages:
            if isinstance(page0, list):
                page = page0[0]
                npages = len(page0)
            else:
                page = page0
                npages = 1
            self.npages = npages
            if not self.dimension_order:
                self.dimension_order = page.axes
                photometric = page.photometric
            shape = page.shape
            nchannels = shape[2] if len(shape) > 2 else 1
            nt = 1
            if isinstance(page, TiffPage):
                width = page.imagewidth
                height = page.imagelength
                self.depth = page.imagedepth
                depth = self.depth * npages
                bitspersample = page.bitspersample
            else:
                width = shape[1]
                height = shape[0]
                depth = npages
                if len(shape) > 2:
                    self.depth = shape[2]
                    depth *= self.depth
                bitspersample = page.dtype.itemsize * 8
            if self.has_ome_metadata:
                pixels = ensure_list(self.metadata.get('Image', {}))[0].get('Pixels', {})
                depth = int(pixels.get('SizeZ', depth))
                nchannels = int(pixels.get('SizeC', nchannels))
                nt = int(pixels.get('SizeT', nt))
            self.sizes.append((width, height))
            self.sizes_xyzct.append((width, height, depth, nchannels, nt))
            self.pixel_types.append(page.dtype)
            self.pixel_nbits.append(bitspersample)

        self.fh = tiff.filehandle
        self.dimension_order = self.dimension_order.lower().replace('s', 'c').replace('r', '')

        self.is_rgb = (photometric in (PHOTOMETRIC.RGB, PHOTOMETRIC.PALETTE) and nchannels in (3, 4))

        self._init_metadata(filename,
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

    def _find_metadata(self):
        pixel_size = []
        # from OME metadata
        if self.has_ome_metadata:
            self._get_ome_metadata()
            return

        # from imageJ metadata
        pixel_size_z = None
        pixel_size_unit = self.metadata.get('unit', '').encode().decode('unicode_escape')
        if pixel_size_unit == 'micron':
            pixel_size_unit = self.default_physical_unit
        if 'scales' in self.metadata:
            for scale in self.metadata['scales'].split(','):
                scale = scale.strip()
                if scale != '':
                    pixel_size.append((float(scale), pixel_size_unit))
        if len(pixel_size) == 0 and self.metadata is not None and 'spacing' in self.metadata:
            pixel_size_z = (self.metadata['spacing'], pixel_size_unit)
        # from description
        if len(pixel_size) < 2 and 'pixelWidth' in self.metadata:
            pixel_info = self.metadata['pixelWidth']
            pixel_size.append((pixel_info['value'], pixel_info['unit']))
            pixel_info = self.metadata['pixelHeight']
            pixel_size.append((pixel_info['value'], pixel_info['unit']))
        if len(pixel_size) < 2 and 'MPP' in self.metadata:
            pixel_size.append((self.metadata['MPP'], self.default_physical_unit))
            pixel_size.append((self.metadata['MPP'], self.default_physical_unit))
        # from page TAGS
        if len(pixel_size) < 2:
            pixel_size_unit = self.tags.get('ResolutionUnit', '')
            if isinstance(pixel_size_unit, Enum):
                pixel_size_unit = pixel_size_unit.name
            pixel_size_unit = pixel_size_unit.lower()
            if pixel_size_unit == 'none':
                pixel_size_unit = ''
            res0 = convert_rational_value(self.tags.get('XResolution'))
            if res0 is not None and res0 != 0:
                pixel_size.append((1 / res0, pixel_size_unit))
            res0 = convert_rational_value(self.tags.get('YResolution'))
            if res0 is not None and res0 != 0:
                pixel_size.append((1 / res0, pixel_size_unit))

        position = []
        xpos = convert_rational_value(self.tags.get('XPosition'))
        ypos = convert_rational_value(self.tags.get('YPosition'))
        if xpos is not None and ypos is not None:
            position = [(xpos, pixel_size_unit), (ypos, pixel_size_unit)]

        if pixel_size_z is not None and len(pixel_size) == 2:
            pixel_size.append(pixel_size_z)

        mag = self.metadata.get('Mag', self.metadata.get('AppMag', 0))

        nchannels = self.get_nchannels()
        photometric = str(self.metadata.get('PhotometricInterpretation', '')).lower().split('.')[-1]
        if nchannels == 3:
            channels = [{'label': photometric}]
        else:
            channels = [{'label': photometric}] * nchannels

        self.source_pixel_size = pixel_size
        self.source_mag = mag
        self.channels = channels
        self.position = position

    def get_source_dask(self):
        return self._load_as_dask()

    def _load_as_dask(self):
        if len(self.arrays) == 0:
            for level in range(len(self.sizes)):
                if self.tiff.is_mmstack:
                    page = self.pages[level]
                    if isinstance(page, list):
                        page = page[0]
                    data = da.from_zarr(page.aszarr())
                else:
                    data = da.from_zarr(self.tiff.aszarr(level=level))
                if data.chunksize == data.shape:
                    data = data.rechunk()
                self.arrays.append(data)
        return self.arrays

    def _load_as_zarr(self):
        if len(self.arrays) == 0:
            import zarr
            store = self.tiff.aszarr(multiscales=True)
            group = zarr.group(store=store)
            self.arrays = [arr for _, arr in group.arrays()]  # read-only zarr arrays
        return self.arrays

    def load(self, decompress: bool = False):
        if decompress:
            self.decompress()
            self.decompressed = True
        else:
            self.fh.seek(0)
            self.data = self.fh.read()
            self.compressed = True

    def unload(self):
        del self.data
        self.clear_arrays()
        self.compressed = False
        self.decompressed = False

    def decompress(self):
        self.clear_arrays()
        for page in self.pages:
            if isinstance(page, list):
                array = []
                for page1 in page:
                    data = page1.asarray()
                    if len(page) > 1:
                        array.append(data)
                    else:
                        array = data
                array = np.asarray(array)
            else:
                array = page.asarray()
            self.arrays.append(array)

    def clear_arrays(self):
        for array in self.arrays:
            del array
        self.arrays = []

    def _asarray_level(self, level: int, **slicing) -> np.ndarray:
        if self.compressed and not self.decompressed:
            out = self._decompress(level, **slicing)
        else:
            self._load_as_dask()
            redim = redimension_data(self.arrays[level], self.dimension_order, self.get_dimension_order())
            slices = get_numpy_slicing(self.get_dimension_order(), **slicing)
            out = redim[slices]
        return out

    def _decompress(self, level: int, **slicing) -> np.ndarray:
        # based on tiffile asarray

        if self.executor is None:
            max_workers = (os.cpu_count() or 1) + 4
            self.executor = ThreadPoolExecutor(max_workers)

        x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
        y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
        c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
        if x1 < 0 or y1 < 0:
            x1, y1 = self.sizes[level]

        dw = x1 - x0
        dh = y1 - y0
        xyzct = list(self.sizes_xyzct[level]).copy()
        nz = xyzct[2]
        nc = xyzct[3]

        pages = self.pages[level]
        if nz == self.npages and z is not None:
            pages = [pages[z]]
        elif t is not None:
            pages = [pages[t]]
        page = pages[0] if isinstance(pages, list) else pages
        tile_height, tile_width = page.chunks[:2]
        tile_y0, tile_x0 = y0 // tile_height, x0 // tile_width
        tile_y1, tile_x1 = np.ceil([y1 / tile_height, x1 / tile_width]).astype(int)
        w = (tile_x1 - tile_x0) * tile_width
        h = (tile_y1 - tile_y0) * tile_height
        niter_channels = nc if page.dims[0] == 'sample' else 1
        tile_per_line = int(np.ceil(page.imagewidth / tile_width))
        tile_per_channel = tile_per_line * int(np.ceil(page.imagelength / tile_height))
        xyzct[0] = w
        xyzct[1] = h

        # Match internal Tiff page dimensions [separate sample, depth, length, width, contig sample]
        n = self.npages
        d = self.depth
        s = nc
        if self.npages == s > 1:
            # in case channels represented as pages
            s = 1
        shape = n, d, h, w, s
        out = np.zeros(shape, page.dtype)

        dataoffsets = []
        databytecounts = []
        tile_locations = []
        for pagei, page in enumerate(pages):
            for channeli in range(niter_channels):
                for y in range(tile_y0, tile_y1):
                    for x in range(tile_x0, tile_x1):
                        index = channeli * tile_per_channel + y * tile_per_line + x
                        if index < len(page.databytecounts):
                            offset = page.dataoffsets[index]
                            count = page.databytecounts[index]
                            if count > 0:
                                dataoffsets.append(offset)
                                databytecounts.append(count)
                                target_y = (y - tile_y0) * tile_height
                                target_x = (x - tile_x0) * tile_width
                                tile_locations.append((pagei, 0, target_y, target_x, channeli))

            self._decode(page, dataoffsets, databytecounts, tile_locations, out)

        target_y0 = y0 - tile_y0 * tile_height
        target_x0 = x0 - tile_x0 * tile_width
        image = out[:, :, target_y0: target_y0 + dh, target_x0: target_x0 + dw, :]
        # 'ndyxs' -> 'tzyxc'
        if image.shape[0] == nc > 1:
            image = np.swapaxes(image, 0, -1)
        elif image.shape[0] == nz > 1:
            image = np.swapaxes(image, 0, 1)
        # 'tzyxc' -> 'tczyx'
        image = np.moveaxis(image, -1, 1)
        return image

    def _decode(self, page: TiffPage, dataoffsets: list, databytecounts: list, tile_locations: list, out: np.ndarray):
        def process_decoded(decoded, index, out=out):
            segment, indices, shape = decoded
            s = tile_locations[index]
            e = np.array(s) + ([1] + list(shape))
            # Note: numpy is not thread-safe
            out[s[0]: e[0],
                s[1]: e[1],
                s[2]: e[2],
                s[3]: e[3],
                s[4]: e[4]] = segment

        for _ in self._segments(
                process_function=process_decoded,
                page=page,
                dataoffsets=dataoffsets,
                databytecounts=databytecounts
        ):
            pass

    def _segments(self, process_function: callable, page: TiffPage, dataoffsets: list, databytecounts: list) -> tuple:
        # based on tiffile segments
        def decode(args, page=page, process_function=process_function):
            decoded = page.decode(*args, jpegtables=page.jpegtables)
            return process_function(decoded, args[1])

        tile_segments = []
        for index in range(len(dataoffsets)):
            offset = dataoffsets[index]
            bytecount = databytecounts[index]
            if self.compressed:
                segment = self.data[offset:offset + bytecount]
            else:
                fh = page.parent.filehandle
                fh.seek(offset)
                segment = fh.read(bytecount)
            tile_segment = (segment, index)
            tile_segments.append(tile_segment)
            # yield decode(tile_segment)
        yield from self.executor.map(decode, tile_segments, timeout=10)

    def close(self):
        self.tiff.close()
        self.fh.close()

arrays = [] instance-attribute

list of all image arrays for different sizes

compressed = False instance-attribute

if image data is loaded compressed

data = bytes() instance-attribute

raw un-decoded image byte data

decompressed = False instance-attribute

if image data is loaded decompressed

depth = page.imagedepth instance-attribute

dimension_order = self.dimension_order.lower().replace('s', 'c').replace('r', '') instance-attribute

executor = executor instance-attribute

fh = tiff.filehandle instance-attribute

filename instance-attribute

original filename

first_page = tiff.pages.first instance-attribute

has_ome_metadata = True instance-attribute

is_rgb = photometric in (PHOTOMETRIC.RGB, PHOTOMETRIC.PALETTE) and nchannels in (3, 4) instance-attribute

metadata = XmlDict.xml2dict(xml_metadata) instance-attribute

npages = npages instance-attribute

pages = get_tiff_pages(tiff) instance-attribute

list of all relevant TiffPages

tags = tags_to_dict(self.first_page.tags) instance-attribute

tiff = tiff instance-attribute

__init__(filename, source_pixel_size=None, target_pixel_size=None, source_info_required=False, executor=None)

Source code in OmeSliCC\TiffSource.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def __init__(self,
             filename: str,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False,
             executor: ThreadPoolExecutor = None):

    super().__init__()
    self.compressed = False
    self.decompressed = False
    self.executor = executor
    self.data = bytes()
    self.arrays = []
    photometric = None
    nchannels = 1

    tiff = TiffFile(filename)
    self.tiff = tiff
    self.first_page = tiff.pages.first
    if tiff.is_ome and tiff.ome_metadata is not None:
        xml_metadata = tiff.ome_metadata
        self.metadata = XmlDict.xml2dict(xml_metadata)
        if 'OME' in self.metadata:
            self.metadata = self.metadata['OME']
            self.has_ome_metadata = True
        if 'BinaryOnly' in self.metadata:
            # binary image only; get metadata from metadata file instead
            self.has_ome_metadata = False
            metdata_filename = os.path.join(os.path.dirname(filename),
                                            self.metadata['BinaryOnly'].get('MetadataFile'))
            if os.path.isfile(metdata_filename):
                metdata_tiff = TiffFile(metdata_filename)
                if metdata_tiff.is_ome and metdata_tiff.ome_metadata is not None:
                    xml_metadata = metdata_tiff.ome_metadata
                    self.metadata = XmlDict.xml2dict(xml_metadata)
                    if 'OME' in self.metadata:
                        self.metadata = self.metadata['OME']
                        images = self.metadata.get('Image')
                        if isinstance(images, list):
                            for image in images:
                                if image.get('Name', '').lower() == get_filetitle(filename).lower():
                                    self.metadata['Image'] = image
                                    break
                        self.has_ome_metadata = True

    if self.has_ome_metadata:
        pass
    elif tiff.is_imagej:
        self.metadata = tiff.imagej_metadata
    elif self.first_page.description:
        self.metadata = desc_to_dict(self.first_page.description)
    self.tags = tags_to_dict(self.first_page.tags)
    if 'FEI_TITAN' in self.tags:
        metadata = tifffile.xml2dict(self.tags.pop('FEI_TITAN'))
        if 'FeiImage' in metadata:
            metadata = metadata['FeiImage']
        self.metadata.update(metadata)

    if tiff.series:
        series0 = tiff.series[0]
        self.dimension_order = series0.axes
        photometric = series0.keyframe.photometric
    self.pages = get_tiff_pages(tiff)
    for page0 in self.pages:
        if isinstance(page0, list):
            page = page0[0]
            npages = len(page0)
        else:
            page = page0
            npages = 1
        self.npages = npages
        if not self.dimension_order:
            self.dimension_order = page.axes
            photometric = page.photometric
        shape = page.shape
        nchannels = shape[2] if len(shape) > 2 else 1
        nt = 1
        if isinstance(page, TiffPage):
            width = page.imagewidth
            height = page.imagelength
            self.depth = page.imagedepth
            depth = self.depth * npages
            bitspersample = page.bitspersample
        else:
            width = shape[1]
            height = shape[0]
            depth = npages
            if len(shape) > 2:
                self.depth = shape[2]
                depth *= self.depth
            bitspersample = page.dtype.itemsize * 8
        if self.has_ome_metadata:
            pixels = ensure_list(self.metadata.get('Image', {}))[0].get('Pixels', {})
            depth = int(pixels.get('SizeZ', depth))
            nchannels = int(pixels.get('SizeC', nchannels))
            nt = int(pixels.get('SizeT', nt))
        self.sizes.append((width, height))
        self.sizes_xyzct.append((width, height, depth, nchannels, nt))
        self.pixel_types.append(page.dtype)
        self.pixel_nbits.append(bitspersample)

    self.fh = tiff.filehandle
    self.dimension_order = self.dimension_order.lower().replace('s', 'c').replace('r', '')

    self.is_rgb = (photometric in (PHOTOMETRIC.RGB, PHOTOMETRIC.PALETTE) and nchannels in (3, 4))

    self._init_metadata(filename,
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

_asarray_level(level, **slicing)

Source code in OmeSliCC\TiffSource.py
272
273
274
275
276
277
278
279
280
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
    if self.compressed and not self.decompressed:
        out = self._decompress(level, **slicing)
    else:
        self._load_as_dask()
        redim = redimension_data(self.arrays[level], self.dimension_order, self.get_dimension_order())
        slices = get_numpy_slicing(self.get_dimension_order(), **slicing)
        out = redim[slices]
    return out

_decode(page, dataoffsets, databytecounts, tile_locations, out)

Source code in OmeSliCC\TiffSource.py
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
def _decode(self, page: TiffPage, dataoffsets: list, databytecounts: list, tile_locations: list, out: np.ndarray):
    def process_decoded(decoded, index, out=out):
        segment, indices, shape = decoded
        s = tile_locations[index]
        e = np.array(s) + ([1] + list(shape))
        # Note: numpy is not thread-safe
        out[s[0]: e[0],
            s[1]: e[1],
            s[2]: e[2],
            s[3]: e[3],
            s[4]: e[4]] = segment

    for _ in self._segments(
            process_function=process_decoded,
            page=page,
            dataoffsets=dataoffsets,
            databytecounts=databytecounts
    ):
        pass

_decompress(level, **slicing)

Source code in OmeSliCC\TiffSource.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
def _decompress(self, level: int, **slicing) -> np.ndarray:
    # based on tiffile asarray

    if self.executor is None:
        max_workers = (os.cpu_count() or 1) + 4
        self.executor = ThreadPoolExecutor(max_workers)

    x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
    y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
    c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
    if x1 < 0 or y1 < 0:
        x1, y1 = self.sizes[level]

    dw = x1 - x0
    dh = y1 - y0
    xyzct = list(self.sizes_xyzct[level]).copy()
    nz = xyzct[2]
    nc = xyzct[3]

    pages = self.pages[level]
    if nz == self.npages and z is not None:
        pages = [pages[z]]
    elif t is not None:
        pages = [pages[t]]
    page = pages[0] if isinstance(pages, list) else pages
    tile_height, tile_width = page.chunks[:2]
    tile_y0, tile_x0 = y0 // tile_height, x0 // tile_width
    tile_y1, tile_x1 = np.ceil([y1 / tile_height, x1 / tile_width]).astype(int)
    w = (tile_x1 - tile_x0) * tile_width
    h = (tile_y1 - tile_y0) * tile_height
    niter_channels = nc if page.dims[0] == 'sample' else 1
    tile_per_line = int(np.ceil(page.imagewidth / tile_width))
    tile_per_channel = tile_per_line * int(np.ceil(page.imagelength / tile_height))
    xyzct[0] = w
    xyzct[1] = h

    # Match internal Tiff page dimensions [separate sample, depth, length, width, contig sample]
    n = self.npages
    d = self.depth
    s = nc
    if self.npages == s > 1:
        # in case channels represented as pages
        s = 1
    shape = n, d, h, w, s
    out = np.zeros(shape, page.dtype)

    dataoffsets = []
    databytecounts = []
    tile_locations = []
    for pagei, page in enumerate(pages):
        for channeli in range(niter_channels):
            for y in range(tile_y0, tile_y1):
                for x in range(tile_x0, tile_x1):
                    index = channeli * tile_per_channel + y * tile_per_line + x
                    if index < len(page.databytecounts):
                        offset = page.dataoffsets[index]
                        count = page.databytecounts[index]
                        if count > 0:
                            dataoffsets.append(offset)
                            databytecounts.append(count)
                            target_y = (y - tile_y0) * tile_height
                            target_x = (x - tile_x0) * tile_width
                            tile_locations.append((pagei, 0, target_y, target_x, channeli))

        self._decode(page, dataoffsets, databytecounts, tile_locations, out)

    target_y0 = y0 - tile_y0 * tile_height
    target_x0 = x0 - tile_x0 * tile_width
    image = out[:, :, target_y0: target_y0 + dh, target_x0: target_x0 + dw, :]
    # 'ndyxs' -> 'tzyxc'
    if image.shape[0] == nc > 1:
        image = np.swapaxes(image, 0, -1)
    elif image.shape[0] == nz > 1:
        image = np.swapaxes(image, 0, 1)
    # 'tzyxc' -> 'tczyx'
    image = np.moveaxis(image, -1, 1)
    return image

_find_metadata()

Source code in OmeSliCC\TiffSource.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def _find_metadata(self):
    pixel_size = []
    # from OME metadata
    if self.has_ome_metadata:
        self._get_ome_metadata()
        return

    # from imageJ metadata
    pixel_size_z = None
    pixel_size_unit = self.metadata.get('unit', '').encode().decode('unicode_escape')
    if pixel_size_unit == 'micron':
        pixel_size_unit = self.default_physical_unit
    if 'scales' in self.metadata:
        for scale in self.metadata['scales'].split(','):
            scale = scale.strip()
            if scale != '':
                pixel_size.append((float(scale), pixel_size_unit))
    if len(pixel_size) == 0 and self.metadata is not None and 'spacing' in self.metadata:
        pixel_size_z = (self.metadata['spacing'], pixel_size_unit)
    # from description
    if len(pixel_size) < 2 and 'pixelWidth' in self.metadata:
        pixel_info = self.metadata['pixelWidth']
        pixel_size.append((pixel_info['value'], pixel_info['unit']))
        pixel_info = self.metadata['pixelHeight']
        pixel_size.append((pixel_info['value'], pixel_info['unit']))
    if len(pixel_size) < 2 and 'MPP' in self.metadata:
        pixel_size.append((self.metadata['MPP'], self.default_physical_unit))
        pixel_size.append((self.metadata['MPP'], self.default_physical_unit))
    # from page TAGS
    if len(pixel_size) < 2:
        pixel_size_unit = self.tags.get('ResolutionUnit', '')
        if isinstance(pixel_size_unit, Enum):
            pixel_size_unit = pixel_size_unit.name
        pixel_size_unit = pixel_size_unit.lower()
        if pixel_size_unit == 'none':
            pixel_size_unit = ''
        res0 = convert_rational_value(self.tags.get('XResolution'))
        if res0 is not None and res0 != 0:
            pixel_size.append((1 / res0, pixel_size_unit))
        res0 = convert_rational_value(self.tags.get('YResolution'))
        if res0 is not None and res0 != 0:
            pixel_size.append((1 / res0, pixel_size_unit))

    position = []
    xpos = convert_rational_value(self.tags.get('XPosition'))
    ypos = convert_rational_value(self.tags.get('YPosition'))
    if xpos is not None and ypos is not None:
        position = [(xpos, pixel_size_unit), (ypos, pixel_size_unit)]

    if pixel_size_z is not None and len(pixel_size) == 2:
        pixel_size.append(pixel_size_z)

    mag = self.metadata.get('Mag', self.metadata.get('AppMag', 0))

    nchannels = self.get_nchannels()
    photometric = str(self.metadata.get('PhotometricInterpretation', '')).lower().split('.')[-1]
    if nchannels == 3:
        channels = [{'label': photometric}]
    else:
        channels = [{'label': photometric}] * nchannels

    self.source_pixel_size = pixel_size
    self.source_mag = mag
    self.channels = channels
    self.position = position

_load_as_dask()

Source code in OmeSliCC\TiffSource.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def _load_as_dask(self):
    if len(self.arrays) == 0:
        for level in range(len(self.sizes)):
            if self.tiff.is_mmstack:
                page = self.pages[level]
                if isinstance(page, list):
                    page = page[0]
                data = da.from_zarr(page.aszarr())
            else:
                data = da.from_zarr(self.tiff.aszarr(level=level))
            if data.chunksize == data.shape:
                data = data.rechunk()
            self.arrays.append(data)
    return self.arrays

_load_as_zarr()

Source code in OmeSliCC\TiffSource.py
228
229
230
231
232
233
234
def _load_as_zarr(self):
    if len(self.arrays) == 0:
        import zarr
        store = self.tiff.aszarr(multiscales=True)
        group = zarr.group(store=store)
        self.arrays = [arr for _, arr in group.arrays()]  # read-only zarr arrays
    return self.arrays

_segments(process_function, page, dataoffsets, databytecounts)

Source code in OmeSliCC\TiffSource.py
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
def _segments(self, process_function: callable, page: TiffPage, dataoffsets: list, databytecounts: list) -> tuple:
    # based on tiffile segments
    def decode(args, page=page, process_function=process_function):
        decoded = page.decode(*args, jpegtables=page.jpegtables)
        return process_function(decoded, args[1])

    tile_segments = []
    for index in range(len(dataoffsets)):
        offset = dataoffsets[index]
        bytecount = databytecounts[index]
        if self.compressed:
            segment = self.data[offset:offset + bytecount]
        else:
            fh = page.parent.filehandle
            fh.seek(offset)
            segment = fh.read(bytecount)
        tile_segment = (segment, index)
        tile_segments.append(tile_segment)
        # yield decode(tile_segment)
    yield from self.executor.map(decode, tile_segments, timeout=10)

clear_arrays()

Source code in OmeSliCC\TiffSource.py
267
268
269
270
def clear_arrays(self):
    for array in self.arrays:
        del array
    self.arrays = []

close()

Source code in OmeSliCC\TiffSource.py
401
402
403
def close(self):
    self.tiff.close()
    self.fh.close()

decompress()

Source code in OmeSliCC\TiffSource.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
def decompress(self):
    self.clear_arrays()
    for page in self.pages:
        if isinstance(page, list):
            array = []
            for page1 in page:
                data = page1.asarray()
                if len(page) > 1:
                    array.append(data)
                else:
                    array = data
            array = np.asarray(array)
        else:
            array = page.asarray()
        self.arrays.append(array)

get_source_dask()

Source code in OmeSliCC\TiffSource.py
210
211
def get_source_dask(self):
    return self._load_as_dask()

load(decompress=False)

Source code in OmeSliCC\TiffSource.py
236
237
238
239
240
241
242
243
def load(self, decompress: bool = False):
    if decompress:
        self.decompress()
        self.decompressed = True
    else:
        self.fh.seek(0)
        self.data = self.fh.read()
        self.compressed = True

unload()

Source code in OmeSliCC\TiffSource.py
245
246
247
248
249
def unload(self):
    del self.data
    self.clear_arrays()
    self.compressed = False
    self.decompressed = False

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

XmlDict

dct = {'@a': 1, 'b': {'@aa': 2, 'bb': 3}} module-attribute

test = XmlDict(dct) module-attribute

xml = xmltodict.unparse({'TEST': test}, short_empty_elements=True, pretty=True) module-attribute

XmlDict

Bases: dict

xmltodict type dictionary providing and propagating access to keys without @ sign

Source code in OmeSliCC\XmlDict.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class XmlDict(dict):
    """xmltodict type dictionary providing and propagating access to keys without @ sign"""

    def __getitem__(self, key: str):
        at_key = '@' + key
        if key not in self and at_key in self:
            key = at_key
        value = dict.__getitem__(self, key)
        if isinstance(value, dict):
            value = XmlDict(value)
        if isinstance(value, list):
            value = XmlList(value)
        return value

    def get(self, key: str, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    def copy(self) -> XmlDict:
        return XmlDict(dict.copy(self))

__getitem__(key)

Source code in OmeSliCC\XmlDict.py
 8
 9
10
11
12
13
14
15
16
17
def __getitem__(self, key: str):
    at_key = '@' + key
    if key not in self and at_key in self:
        key = at_key
    value = dict.__getitem__(self, key)
    if isinstance(value, dict):
        value = XmlDict(value)
    if isinstance(value, list):
        value = XmlList(value)
    return value

copy()

Source code in OmeSliCC\XmlDict.py
25
26
def copy(self) -> XmlDict:
    return XmlDict(dict.copy(self))

get(key, default=None)

Source code in OmeSliCC\XmlDict.py
19
20
21
22
23
def get(self, key: str, default=None):
    try:
        return self[key]
    except KeyError:
        return default

XmlList

Bases: list

xmltodict type list propagating access to keys without @ sign

Source code in OmeSliCC\XmlDict.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class XmlList(list):
    """xmltodict type list propagating access to keys without @ sign"""

    def __getitem__(self, index: int):
        value = list.__getitem__(self, index)
        if isinstance(value, dict):
            value = XmlDict(value)
        if isinstance(value, list):
            value = XmlList(value)
        return value

    def __iter__(self) -> XmlList:
        self.i = 0
        return self

    def __next__(self):
        if self.i < len(self):
            item = self[self.i]
            self.i += 1
            return item
        else:
            raise StopIteration

__getitem__(index)

Source code in OmeSliCC\XmlDict.py
32
33
34
35
36
37
38
def __getitem__(self, index: int):
    value = list.__getitem__(self, index)
    if isinstance(value, dict):
        value = XmlDict(value)
    if isinstance(value, list):
        value = XmlList(value)
    return value

__iter__()

Source code in OmeSliCC\XmlDict.py
40
41
42
def __iter__(self) -> XmlList:
    self.i = 0
    return self

__next__()

Source code in OmeSliCC\XmlDict.py
44
45
46
47
48
49
50
def __next__(self):
    if self.i < len(self):
        item = self[self.i]
        self.i += 1
        return item
    else:
        raise StopIteration

dict2xml(dct)

Source code in OmeSliCC\XmlDict.py
57
58
def dict2xml(dct: dict) -> str:
    return xmltodict.unparse(dct, short_empty_elements=True, pretty=True)

xml2dict(xml_metadata)

Source code in OmeSliCC\XmlDict.py
53
54
def xml2dict(xml_metadata: str) -> dict:
    return XmlDict(xmltodict.parse(xml_metadata))

Zarr

Zarr

Source code in OmeSliCC\Zarr.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class Zarr:
    def __init__(self, filename, ome=None, v3=False):
        self.filename = filename
        if ome is not None:
            self.ome = ome
        else:
            self.ome = ('ome' == self.filename.split('.')[1].lower())
        self.v3 = v3
        self.data = []

    def create(self, source, shapes=[], chunk_shapes=[], level_scales=[],
               tile_size=None, npyramid_add=0, pyramid_downsample=2, compression=[]):
        # create empty dataset
        dimension_order = source.get_dimension_order()
        self.dimension_order = dimension_order
        nlevels = max(1 + npyramid_add, len(shapes))
        self.npyramid_add = npyramid_add
        self.pyramid_downsample = pyramid_downsample
        self.level_scales = level_scales
        if self.v3:
            import zarrita
            store_path = zarrita.store.make_store_path(self.filename)
            if os.path.exists(self.filename):
                shutil.rmtree(self.filename)
            self.zarr_root = zarrita.Group.create(store=store_path, exists_ok=True)
            ome_version = '0.5-dev1'
        else:
            file_url = pathlib.Path(self.filename, mode='w').as_uri()
            self.zarr_root = zarr.open_group(store=file_url, mode='w', storage_options={'dimension_separator': '/'})
            ome_version = '0.4'
        xyzct = source.get_size_xyzct()
        self.scaler = Scaler(downscale=pyramid_downsample, max_layer=npyramid_add)
        shape0 = [xyzct['xyzct'.index(dimension)] for dimension in dimension_order]
        dtype = source.pixel_types[0]
        pixel_size_um = source.get_pixel_size_micrometer()
        translation_um = source.get_position_micrometer()
        scale = 1
        datasets = []
        if tile_size and len(tile_size) < 5:
            if isinstance(tile_size, int):
                tile_size = [tile_size] * 2
            elif len(tile_size) == 1:
                tile_size = tile_size * 2
            tile_size = list(np.flip(tile_size))
            while len(tile_size) < 5:
                tile_size = [1] + tile_size
        for level in range(nlevels):
            if len(shapes) > 0:
                shape = shapes[level]
            else:
                shape = scale_dimensions_xy(shape0, dimension_order, scale)
            if len(chunk_shapes) > 0:
                chunk_shape = chunk_shapes[level]
            else:
                chunk_shape = np.min([tile_size, shape], axis=0)
            if self.v3:
                import zarrita
                shape = np.array(shape).tolist()                # convert to basic int
                chunk_shape = np.array(chunk_shape).tolist()    # convert to basic int
                codecs = create_compression_codecs(compression)
                dataset = self.zarr_root.create_array(str(level), shape=shape, chunk_shape=chunk_shape, dtype=dtype,
                                                      codecs=codecs)
            else:
                chunk_shape = tile_size
                compressor, compression_filters = create_compression_filter(compression)
                dataset = self.zarr_root.create_dataset(str(level), shape=shape, chunks=chunk_shape, dtype=dtype,
                                                        compressor=compressor, filters=compression_filters)
            self.data.append(dataset)
            # used for ome metadata:
            datasets.append({
                'path': str(level),
                'coordinateTransformations': create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um)
            })
            scale /= pyramid_downsample

        if self.ome:
            multiscales = {
                'version': ome_version,
                'axes': create_axes_metadata(dimension_order),
                'name': get_filetitle(source.source_reference),
                'datasets': datasets,
            }
            metadata = {'multiscales': [multiscales], 'omero': create_channel_metadata(source, ome_version)}
            if self.v3:
                self.zarr_root.update_attributes(metadata)
            else:
                self.zarr_root.attrs = metadata

    def get(self, level, **slicing):
        slices = get_numpy_slicing(self.dimension_order, **slicing)
        data = self.data[level][slices]
        return data

    def set(self, data, **slicing):
        scale = 1
        for level, sized_data in enumerate(self.scaler.nearest(data)):
            resized_slicing = scale_dimensions_dict(slicing, scale)
            slices = get_numpy_slicing(self.dimension_order, **resized_slicing)
            self.data[level][slices] = np.asarray(sized_data)
            scale /= self.pyramid_downsample

    def set_level(self, level, data, **slicing):
        resized_slicing = scale_dimensions_dict(slicing, 1 / self.level_scales[level])
        slices = get_numpy_slicing(self.dimension_order, **resized_slicing)
        self.data[level][slices] = np.asarray(data)

data = [] instance-attribute

filename = filename instance-attribute

ome = ome instance-attribute

v3 = v3 instance-attribute

__init__(filename, ome=None, v3=False)

Source code in OmeSliCC\Zarr.py
14
15
16
17
18
19
20
21
def __init__(self, filename, ome=None, v3=False):
    self.filename = filename
    if ome is not None:
        self.ome = ome
    else:
        self.ome = ('ome' == self.filename.split('.')[1].lower())
    self.v3 = v3
    self.data = []

create(source, shapes=[], chunk_shapes=[], level_scales=[], tile_size=None, npyramid_add=0, pyramid_downsample=2, compression=[])

Source code in OmeSliCC\Zarr.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def create(self, source, shapes=[], chunk_shapes=[], level_scales=[],
           tile_size=None, npyramid_add=0, pyramid_downsample=2, compression=[]):
    # create empty dataset
    dimension_order = source.get_dimension_order()
    self.dimension_order = dimension_order
    nlevels = max(1 + npyramid_add, len(shapes))
    self.npyramid_add = npyramid_add
    self.pyramid_downsample = pyramid_downsample
    self.level_scales = level_scales
    if self.v3:
        import zarrita
        store_path = zarrita.store.make_store_path(self.filename)
        if os.path.exists(self.filename):
            shutil.rmtree(self.filename)
        self.zarr_root = zarrita.Group.create(store=store_path, exists_ok=True)
        ome_version = '0.5-dev1'
    else:
        file_url = pathlib.Path(self.filename, mode='w').as_uri()
        self.zarr_root = zarr.open_group(store=file_url, mode='w', storage_options={'dimension_separator': '/'})
        ome_version = '0.4'
    xyzct = source.get_size_xyzct()
    self.scaler = Scaler(downscale=pyramid_downsample, max_layer=npyramid_add)
    shape0 = [xyzct['xyzct'.index(dimension)] for dimension in dimension_order]
    dtype = source.pixel_types[0]
    pixel_size_um = source.get_pixel_size_micrometer()
    translation_um = source.get_position_micrometer()
    scale = 1
    datasets = []
    if tile_size and len(tile_size) < 5:
        if isinstance(tile_size, int):
            tile_size = [tile_size] * 2
        elif len(tile_size) == 1:
            tile_size = tile_size * 2
        tile_size = list(np.flip(tile_size))
        while len(tile_size) < 5:
            tile_size = [1] + tile_size
    for level in range(nlevels):
        if len(shapes) > 0:
            shape = shapes[level]
        else:
            shape = scale_dimensions_xy(shape0, dimension_order, scale)
        if len(chunk_shapes) > 0:
            chunk_shape = chunk_shapes[level]
        else:
            chunk_shape = np.min([tile_size, shape], axis=0)
        if self.v3:
            import zarrita
            shape = np.array(shape).tolist()                # convert to basic int
            chunk_shape = np.array(chunk_shape).tolist()    # convert to basic int
            codecs = create_compression_codecs(compression)
            dataset = self.zarr_root.create_array(str(level), shape=shape, chunk_shape=chunk_shape, dtype=dtype,
                                                  codecs=codecs)
        else:
            chunk_shape = tile_size
            compressor, compression_filters = create_compression_filter(compression)
            dataset = self.zarr_root.create_dataset(str(level), shape=shape, chunks=chunk_shape, dtype=dtype,
                                                    compressor=compressor, filters=compression_filters)
        self.data.append(dataset)
        # used for ome metadata:
        datasets.append({
            'path': str(level),
            'coordinateTransformations': create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um)
        })
        scale /= pyramid_downsample

    if self.ome:
        multiscales = {
            'version': ome_version,
            'axes': create_axes_metadata(dimension_order),
            'name': get_filetitle(source.source_reference),
            'datasets': datasets,
        }
        metadata = {'multiscales': [multiscales], 'omero': create_channel_metadata(source, ome_version)}
        if self.v3:
            self.zarr_root.update_attributes(metadata)
        else:
            self.zarr_root.attrs = metadata

get(level, **slicing)

Source code in OmeSliCC\Zarr.py
101
102
103
104
def get(self, level, **slicing):
    slices = get_numpy_slicing(self.dimension_order, **slicing)
    data = self.data[level][slices]
    return data

set(data, **slicing)

Source code in OmeSliCC\Zarr.py
106
107
108
109
110
111
112
def set(self, data, **slicing):
    scale = 1
    for level, sized_data in enumerate(self.scaler.nearest(data)):
        resized_slicing = scale_dimensions_dict(slicing, scale)
        slices = get_numpy_slicing(self.dimension_order, **resized_slicing)
        self.data[level][slices] = np.asarray(sized_data)
        scale /= self.pyramid_downsample

set_level(level, data, **slicing)

Source code in OmeSliCC\Zarr.py
114
115
116
117
def set_level(self, level, data, **slicing):
    resized_slicing = scale_dimensions_dict(slicing, 1 / self.level_scales[level])
    slices = get_numpy_slicing(self.dimension_order, **resized_slicing)
    self.data[level][slices] = np.asarray(data)

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_axes_metadata(dimension_order)

Source code in OmeSliCC\ome_zarr_util.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def create_axes_metadata(dimension_order):
    axes = []
    for dimension in dimension_order:
        unit1 = None
        if dimension == 't':
            type1 = 'time'
            unit1 = 'millisecond'
        elif dimension == 'c':
            type1 = 'channel'
        else:
            type1 = 'space'
            unit1 = 'micrometer'
        axis = {'name': dimension, 'type': type1}
        if unit1 is not None and unit1 != '':
            axis['unit'] = unit1
        axes.append(axis)
    return axes

create_channel_metadata(source, ome_version)

Source code in OmeSliCC\ome_zarr_util.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def create_channel_metadata(source, ome_version):
    channels = source.get_channels()
    nchannels = source.get_nchannels()

    if len(channels) < nchannels == 3:
        labels = ['Red', 'Green', 'Blue']
        colors = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)]
        channels = [{'label': label, 'color': color} for label, color in zip(labels, colors)]

    omezarr_channels = []
    for channeli, channel0 in enumerate(channels):
        channel = channel0.copy()
        color = channel.get('color', (1, 1, 1, 1))
        channel['color'] = rgba_to_hexrgb(color)
        if 'window' not in channel:
            channel['window'] = source.get_channel_window(channeli)
        omezarr_channels.append(channel)

    metadata = {
        'version': ome_version,
        'channels': omezarr_channels,
    }
    return metadata

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[])

Source code in OmeSliCC\ome_zarr_util.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[]):
    metadata = []
    pixel_size_scale = []
    translation_scale = []
    for dimension in dimension_order:
        if dimension == 'z' and len(pixel_size_um) > 2:
            pixel_size_scale1 = pixel_size_um[2]
        elif dimension == 'y' and len(pixel_size_um) > 1:
            pixel_size_scale1 = pixel_size_um[1] / scale
        elif dimension == 'x' and len(pixel_size_um) > 0:
            pixel_size_scale1 = pixel_size_um[0] / scale
        else:
            pixel_size_scale1 = 1
        if pixel_size_scale1 == 0:
            pixel_size_scale1 = 1
        pixel_size_scale.append(pixel_size_scale1)

        if dimension == 'z' and len(translation_um) > 2:
            translation1 = translation_um[2]
        elif dimension == 'y' and len(translation_um) > 1:
            translation1 = translation_um[1] * scale
        elif dimension == 'x' and len(translation_um) > 0:
            translation1 = translation_um[0] * scale
        else:
            translation1 = 0
        translation_scale.append(translation1)

    metadata.append({'type': 'scale', 'scale': pixel_size_scale})
    if not all(v == 0 for v in translation_scale):
        metadata.append({'type': 'translation', 'translation': translation_scale})
    return metadata

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

scale_dimensions_dict(shape0, scale)

Source code in OmeSliCC\ome_zarr_util.py
 92
 93
 94
 95
 96
 97
 98
 99
100
def scale_dimensions_dict(shape0, scale):
    shape = {}
    if scale == 1:
        return shape0
    for dimension, shape1 in shape0.items():
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape[dimension] = shape1
    return shape

scale_dimensions_xy(shape0, dimension_order, scale)

Source code in OmeSliCC\ome_zarr_util.py
81
82
83
84
85
86
87
88
89
def scale_dimensions_xy(shape0, dimension_order, scale):
    shape = []
    if scale == 1:
        return shape0
    for shape1, dimension in zip(shape0, dimension_order):
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape.append(shape1)
    return shape

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

ZarrSource

ZarrSource

Bases: OmeSource

Zarr-compatible image source

Source code in OmeSliCC\ZarrSource.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
class ZarrSource(OmeSource):
    """Zarr-compatible image source"""

    filename: str
    """original filename / URL"""
    levels: list
    """list of all image arrays for different sizes"""

    def __init__(self, filename: str,
                 source_pixel_size: list = None,
                 target_pixel_size: list = None,
                 source_info_required: bool = False):

        super().__init__()

        try:
            if filename.startswith(("http", "s3")):
                cls = RemoteStore
            else:
                cls = LocalStore
            store_path = cls(filename)
            root = zarr.open_group(store=store_path)
            self.metadata = root.attrs.asdict()

            paths = []
            dimension_order = 'tczyx'
            if 'multiscales' in self.metadata:
                for scale in self.metadata.get('multiscales', []):
                    for index, dataset in enumerate(scale.get('datasets', [])):
                        paths.append(dataset.get('path', str(index)))
                    axes = scale.get('axes', [])
                    if len(axes) > 0:
                        dimension_order = ''.join([axis.get('name') for axis in axes])
            else:
                paths = root.array_keys()
            self.dimension_order = dimension_order

            self.levels = []
            for path in paths:
                data = root.get(path)
                self.levels.append(data)

                xyzct = [1, 1, 1, 1, 1]
                for i, n in enumerate(data.shape):
                    xyzct_index = 'xyzct'.index(dimension_order[i])
                    xyzct[xyzct_index] = n
                self.sizes_xyzct.append(xyzct)
                self.sizes.append((xyzct[0], xyzct[1]))
                self.pixel_types.append(data.dtype)
                self.pixel_nbits.append(data.dtype.itemsize * 8)
        except Exception as e:
            raise FileNotFoundError(f'Read error: {e}')

        self._init_metadata(filename,
                            source_pixel_size=source_pixel_size,
                            target_pixel_size=target_pixel_size,
                            source_info_required=source_info_required)

    def _find_metadata(self):
        pixel_size = []
        channels = []
        for scale in self.metadata.get('multiscales', []):
            axes = ''.join([axis.get('name', '') for axis in scale.get('axes', [])])
            units = [axis.get('unit', '') for axis in scale.get('axes', [])]
            scale1 = [0, 0, 0, 0, 0]
            datasets = scale.get('datasets')
            if datasets is not None:
                coordinateTransformations = datasets[0].get('coordinateTransformations')
                if coordinateTransformations is not None:
                    scale1 = coordinateTransformations[0].get('scale', scale1)
            if 'z' in axes:
                pixel_size = [
                    (scale1[axes.index('x')], units[axes.index('x')]),
                    (scale1[axes.index('y')], units[axes.index('y')]),
                    (scale1[axes.index('z')], units[axes.index('z')])]
            else:
                pixel_size = [(0, ''), (0, ''), (0, '')]
        nchannels = self.sizes_xyzct[0][3]
        for data in self.metadata.values():
            if isinstance(data, dict):
                n = len(data.get('channels', []))
                for channel0 in data.get('channels', []):
                    channel = XmlDict({'@Name': channel0.get('label'), '@SamplesPerPixel': nchannels // n})
                    if 'color' in channel0:
                        channel['@Color'] = channel0['color']
                    channels.append(channel)
        if len(channels) == 0:
            if nchannels == 3:
                channels = [XmlDict({'@Name': '', '@SamplesPerPixel': nchannels})]
            else:
                channels = [XmlDict({'@Name': '', '@SamplesPerPixel': 1})] * nchannels
        self.source_pixel_size = pixel_size
        self.channels = channels
        self.source_mag = 0

    def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1) -> np.ndarray:
        size_xyzct = self.sizes_xyzct[level]
        if x1 < 0 or y1 < 0:
            x1, y1, _, _, _ = size_xyzct
        if self.dimension_order.endswith('yx'):
            image = self.levels[level][..., y0:y1, x0:x1].squeeze()
            if len(image.shape) > 2:
                image = np.moveaxis(image, 0, -1)  # move axis 0 (channel/z) to end
        else:
            image = self.levels[level][y0:y1, x0:x1].squeeze()
        return image

dimension_order = dimension_order instance-attribute

filename instance-attribute

original filename / URL

levels = [] instance-attribute

list of all image arrays for different sizes

metadata = root.attrs.asdict() instance-attribute

__init__(filename, source_pixel_size=None, target_pixel_size=None, source_info_required=False)

Source code in OmeSliCC\ZarrSource.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def __init__(self, filename: str,
             source_pixel_size: list = None,
             target_pixel_size: list = None,
             source_info_required: bool = False):

    super().__init__()

    try:
        if filename.startswith(("http", "s3")):
            cls = RemoteStore
        else:
            cls = LocalStore
        store_path = cls(filename)
        root = zarr.open_group(store=store_path)
        self.metadata = root.attrs.asdict()

        paths = []
        dimension_order = 'tczyx'
        if 'multiscales' in self.metadata:
            for scale in self.metadata.get('multiscales', []):
                for index, dataset in enumerate(scale.get('datasets', [])):
                    paths.append(dataset.get('path', str(index)))
                axes = scale.get('axes', [])
                if len(axes) > 0:
                    dimension_order = ''.join([axis.get('name') for axis in axes])
        else:
            paths = root.array_keys()
        self.dimension_order = dimension_order

        self.levels = []
        for path in paths:
            data = root.get(path)
            self.levels.append(data)

            xyzct = [1, 1, 1, 1, 1]
            for i, n in enumerate(data.shape):
                xyzct_index = 'xyzct'.index(dimension_order[i])
                xyzct[xyzct_index] = n
            self.sizes_xyzct.append(xyzct)
            self.sizes.append((xyzct[0], xyzct[1]))
            self.pixel_types.append(data.dtype)
            self.pixel_nbits.append(data.dtype.itemsize * 8)
    except Exception as e:
        raise FileNotFoundError(f'Read error: {e}')

    self._init_metadata(filename,
                        source_pixel_size=source_pixel_size,
                        target_pixel_size=target_pixel_size,
                        source_info_required=source_info_required)

_asarray_level(level, x0=0, y0=0, x1=-1, y1=-1)

Source code in OmeSliCC\ZarrSource.py
105
106
107
108
109
110
111
112
113
114
115
def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1) -> np.ndarray:
    size_xyzct = self.sizes_xyzct[level]
    if x1 < 0 or y1 < 0:
        x1, y1, _, _, _ = size_xyzct
    if self.dimension_order.endswith('yx'):
        image = self.levels[level][..., y0:y1, x0:x1].squeeze()
        if len(image.shape) > 2:
            image = np.moveaxis(image, 0, -1)  # move axis 0 (channel/z) to end
    else:
        image = self.levels[level][y0:y1, x0:x1].squeeze()
    return image

_find_metadata()

Source code in OmeSliCC\ZarrSource.py
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def _find_metadata(self):
    pixel_size = []
    channels = []
    for scale in self.metadata.get('multiscales', []):
        axes = ''.join([axis.get('name', '') for axis in scale.get('axes', [])])
        units = [axis.get('unit', '') for axis in scale.get('axes', [])]
        scale1 = [0, 0, 0, 0, 0]
        datasets = scale.get('datasets')
        if datasets is not None:
            coordinateTransformations = datasets[0].get('coordinateTransformations')
            if coordinateTransformations is not None:
                scale1 = coordinateTransformations[0].get('scale', scale1)
        if 'z' in axes:
            pixel_size = [
                (scale1[axes.index('x')], units[axes.index('x')]),
                (scale1[axes.index('y')], units[axes.index('y')]),
                (scale1[axes.index('z')], units[axes.index('z')])]
        else:
            pixel_size = [(0, ''), (0, ''), (0, '')]
    nchannels = self.sizes_xyzct[0][3]
    for data in self.metadata.values():
        if isinstance(data, dict):
            n = len(data.get('channels', []))
            for channel0 in data.get('channels', []):
                channel = XmlDict({'@Name': channel0.get('label'), '@SamplesPerPixel': nchannels // n})
                if 'color' in channel0:
                    channel['@Color'] = channel0['color']
                channels.append(channel)
    if len(channels) == 0:
        if nchannels == 3:
            channels = [XmlDict({'@Name': '', '@SamplesPerPixel': nchannels})]
        else:
            channels = [XmlDict({'@Name': '', '@SamplesPerPixel': 1})] * nchannels
    self.source_pixel_size = pixel_size
    self.channels = channels
    self.source_mag = 0

color_conversion

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

conversion

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_image(source, image, converted_filename)

Source code in OmeSliCC\conversion.py
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
def check_image(source, image, converted_filename):
    error_message = None
    try:
        dummy_params = {'input': {}, 'output': {}}
        converted_source = create_source(converted_filename, dummy_params)
        w, h = converted_source.get_size()
        x1, y1 = min(16, w), min(16, h)
        slicing = {'x0': 0, 'x1': x1, 'y0': 0, 'y1': y1, 'z': 0, 't': 0}
        slices = get_numpy_slicing(source.get_dimension_order(), **slicing)
        patch_original = image[slices]
        patch_converted = converted_source.asarray(**slicing)
        np.testing.assert_allclose(patch_original, patch_converted, verbose=False)
    except Exception as e:
        error_message = str(e)
    if error_message:
        raise ValueError(f'Converted image check\n{error_message}')

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

combine_images(sources, params)

Source code in OmeSliCC\conversion.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
def combine_images(sources: list[OmeSource], params: dict):
    source0 = sources[0]
    source_ref = source0.source_reference
    output_params = params['output']
    output_folder = output_params['folder']
    output_format = output_params['format']
    extra_metadata = output_params.get('extra_metadata', {})

    images = [get_source_image(source) for source in sources]
    image = da.concatenate(images, axis=1)

    # Experimental metadata
    #metadatas = []
    #for source in sources:
    #    image_filename = source.source_reference
    #    filepattern = os.path.splitext(image_filename)[0].rstrip('.ome') + '*'
    #    for metadata_filename in glob.glob(filepattern):
    #        if metadata_filename != image_filename:
    #            metadata = file_to_dict(metadata_filename)
    #            if metadata is not None:
    #                metadatas.append(metadata)

    new_source = OmeSource()
    ome = ('ome' in output_format)
    filetitle = get_filetitle(source_ref).rstrip('.ome')
    output_filename = str(os.path.join(output_folder, filetitle + '_combined.' + output_format))
    new_source.source_reference = output_filename
    new_source.target_pixel_size = source0.get_pixel_size()
    new_source.position = source0.get_position()
    new_source.rotation = source0.get_rotation()

    channels = extra_metadata.get('channels', [])
    if not channels:
        channels = []
        for source in sources:
            for channeli, channel in enumerate(source.get_channels()):
                label = channel.get('label', '')
                if label == '' or label in [standard_type.name.lower() for standard_type in TIFF.PHOTOMETRIC]:
                    channel = channel.copy()
                    label = get_filetitle(source.source_reference).rstrip('.ome')
                    if len(source.get_channels()) > 1:
                        label += f'#{channeli}'
                    channel['label'] = label
                channels.append(channel)
    nchannels = len(channels)

    if image.shape[1] != nchannels:
        logging.warning('#Combined image channels does not match #data channels')

    new_source.channels = channels

    new_source.sizes = [source0.get_size()]
    sizes_xyzc = list(source0.get_size_xyzct())
    sizes_xyzc[3] = nchannels
    new_source.sizes_xyzct = [tuple(sizes_xyzc)]
    new_source.pixel_types = source0.pixel_types
    new_source.pixel_nbits = source0.pixel_nbits
    new_source.best_level, new_source.best_factor, new_source.full_factor = 0, 1, 1
    new_source.source_mag = source0.source_mag
    new_source.output_dimension_order = source0.output_dimension_order

    if 'zar' in output_format:
        if 'ome.' in output_format:
            save_image_as_ome_zarr(new_source, image, output_filename, output_params)
        else:
            save_image_as_zarr(new_source, image, output_filename, output_params)
    elif 'tif' in output_format:
        save_image_as_tiff(new_source, image, output_filename, output_params, ome=ome)
    else:
        save_image(image, output_filename, output_params)

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image(source, params, load_chunked=False)

Source code in OmeSliCC\conversion.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def convert_image(source: OmeSource, params: dict, load_chunked: bool = False):
    source_ref = source.source_reference
    output_params = params['output']
    output_folder = output_params['folder']
    output_format = output_params['format']
    ome = ('ome' in output_format)
    overwrite = output_params.get('overwrite', True)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    filetitle = get_filetitle(source_ref).rstrip('.ome')
    output_filename = str(os.path.join(output_folder, filetitle + '.' + output_format))
    if overwrite or not os.path.exists(output_filename):
        if load_chunked:
            image = get_source_image_chunked(source)
            #image = get_source_image_dask(source)
        else:
            image = get_source_image(source)
        if 'ome.zarr' in output_format:
            save_image_as_ome_zarr(source, image, output_filename, output_params)
        elif 'zarr' in output_format:
            save_image_as_zarr(source, image, output_filename, output_params)
        elif 'tif' in output_format:
            save_image_as_tiff(source, image, output_filename, output_params, ome=ome)
        else:
            save_image(image, output_filename, output_params)
        check_image(source, image, output_filename)

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

create_source(source_ref, params, omero=None)

Source code in OmeSliCC\conversion.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def create_source(source_ref: str, params: dict, omero=None) -> OmeSource:
    source_pixel_size = split_value_unit_list(params.get('input', {}).get('pixel_size'))
    target_pixel_size = split_value_unit_list(params.get('output', {}).get('pixel_size'))
    ext = os.path.splitext(source_ref)[1].lower()
    if omero is not None:
        from OmeSliCC.OmeroSource import OmeroSource
        source = OmeroSource(omero, int(source_ref), source_pixel_size=source_pixel_size, target_pixel_size=target_pixel_size)
    elif ext == '.zarr':
        source = OmeZarrSource(source_ref, source_pixel_size=source_pixel_size, target_pixel_size=target_pixel_size)
    elif ext.lstrip('.') in TIFF.FILE_EXTENSIONS:
        source = TiffSource(source_ref, source_pixel_size=source_pixel_size, target_pixel_size=target_pixel_size)
    elif ext in Image.registered_extensions().keys():
        source = PlainImageSource(source_ref, source_pixel_size=source_pixel_size, target_pixel_size=target_pixel_size)
    else:
        try:
            from OmeSliCC.BioSource import BioSource
            source = BioSource(source_ref, source_pixel_size=source_pixel_size, target_pixel_size=target_pixel_size)
        except ImportError:
            raise NotImplementedError('Unsupported: Bioformats not installed')
    return source

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

extract_thumbnail(source, params)

Source code in OmeSliCC\conversion.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def extract_thumbnail(source: OmeSource, params: dict):
    source_ref = source.source_reference
    output_params = params['output']
    output_folder = output_params['folder']
    target_size = output_params.get('thumbnail_size', 1000)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    output_filename = os.path.join(output_folder, f'{get_filetitle(source_ref)}_thumb.tiff')
    size = source.get_size()
    nchannels = source.get_size_xyzct()[3]

    if target_size < 1:
        factor = target_size
    else:
        factor = np.max(np.divide(size, target_size))
    thumb_size = np.round(np.divide(size, factor)).astype(int)
    thumb = source.get_thumbnail(thumb_size)

    if nchannels not in [1, 3]:
        for channeli in range(nchannels):
            output_filename = os.path.join(output_folder, f'{get_filetitle(source_ref)}_channel{channeli}_thumb.tiff')
            save_tiff(output_filename, thumb[..., channeli], dimension_order='yx')
    else:
        save_tiff(output_filename, thumb, dimension_order='yxc')

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_info(source)

Source code in OmeSliCC\conversion.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def get_image_info(source: OmeSource) -> str:
    sizes_xyzct = source.sizes_xyzct
    pixel_nbytes = source.get_pixel_nbytes()
    pixel_type = source.get_pixel_type()
    channels = source.get_channels()
    image_info = os.path.basename(source.source_reference)
    image_info += ' ' + get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)
    sizes = source.get_physical_size()
    if len(sizes) > 0:
        image_info += ' Physical size:'
        infos = []
        for size in sizes:
            if size[0] > 0:
                infos.append(f' {size[0]:.2f} {size[1]}')
        image_info += ' x'.join(infos)
    return image_info

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_source_image(source)

Source code in OmeSliCC\conversion.py
211
212
213
214
215
216
def get_source_image(source: OmeSource):
    image = source.asarray()
    image_size = image.size * image.itemsize
    if image_size < psutil.virtual_memory().total:
        image = np.asarray(image)   # pre-computing is way faster than dask saving/scaling
    return image

get_source_image_chunked(source, chunk_size=(10240, 10240))

Source code in OmeSliCC\conversion.py
224
225
226
227
228
229
230
231
232
233
234
def get_source_image_chunked(source: OmeSource, chunk_size=(10240, 10240)):
    image = source.clone_empty()
    for indices, chunk in source.produce_chunks(chunk_size):
        s = indices
        e = np.array(s) + chunk.shape
        image[s[0]:e[0],
              s[1]:e[1],
              s[2]:e[2],
              s[3]:e[3],
              s[4]:e[4]] = chunk
    return image

get_source_image_dask(source, chunk_size=(10240, 10240))

Source code in OmeSliCC\conversion.py
219
220
221
def get_source_image_dask(source: OmeSource, chunk_size=(10240, 10240)):
    image = source.asdask(chunk_size)
    return image

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

save_image_as_ome_zarr(source, data, output_filename, output_params)

Source code in OmeSliCC\conversion.py
237
238
239
240
241
242
243
244
245
246
def save_image_as_ome_zarr(source: OmeSource, data: np.ndarray, output_filename: str, output_params: dict):
    # ome-zarr: https://ngff.openmicroscopy.org/latest/
    tile_size = output_params.get('tile_size')
    compression = output_params.get('compression')
    npyramid_add = output_params.get('npyramid_add', 0)
    pyramid_downsample = output_params.get('pyramid_downsample')

    zarr = OmeZarr(output_filename)
    zarr.write(source, tile_size=tile_size, compression=compression,
               npyramid_add=npyramid_add, pyramid_downsample=pyramid_downsample)

save_image_as_tiff(source, image, output_filename, output_params, ome=False)

Source code in OmeSliCC\conversion.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
def save_image_as_tiff(source: OmeSource, image: np.ndarray, output_filename: str, output_params: dict, ome: bool = False):
    tile_size = output_params.get('tile_size')
    compression = output_params.get('compression')
    combine_rgb = output_params.get('combine_rgb', True)

    npyramid_add = output_params.get('npyramid_add', 0)
    pyramid_downsample = output_params.get('pyramid_downsample')
    if npyramid_add > 0:
        pyramid_sizes_add = calc_pyramid(source.get_size_xyzct(), npyramid_add, pyramid_downsample)
    else:
        pyramid_sizes_add = []

    if ome:
        metadata = None
        xml_metadata = source.create_xml_metadata(output_filename, combine_rgb=combine_rgb,
                                                  pyramid_sizes_add=pyramid_sizes_add)
    else:
        metadata = source.get_metadata()
        xml_metadata = None
    resolution, resolution_unit = get_resolution_from_pixel_size(source.get_pixel_size())

    save_tiff(output_filename, image, metadata=metadata, xml_metadata=xml_metadata,
              dimension_order=source.get_dimension_order(),
              resolution=resolution, resolution_unit=resolution_unit, tile_size=tile_size,
              compression=compression, combine_rgb=combine_rgb, pyramid_sizes_add=pyramid_sizes_add)

save_image_as_zarr(source, data, output_filename, output_params, ome=None, v3=False)

Source code in OmeSliCC\conversion.py
249
250
251
252
253
254
255
256
257
258
259
260
def save_image_as_zarr(source: OmeSource, data: np.ndarray, output_filename: str, output_params: dict,
                       ome: bool = None, v3: bool = False):
    # ome-zarr: https://ngff.openmicroscopy.org/latest/
    tile_size = output_params.get('tile_size')
    compression = output_params.get('compression')
    npyramid_add = output_params.get('npyramid_add', 0)
    pyramid_downsample = output_params.get('pyramid_downsample')

    zarr = Zarr(output_filename, ome=ome, v3=v3)
    zarr.create(source, tile_size=tile_size, npyramid_add=npyramid_add, pyramid_downsample=pyramid_downsample,
                compression=compression)
    zarr.set(data)

save_tiff(filename, image, metadata=None, xml_metadata=None, dimension_order='yxc', resolution=None, resolution_unit=None, tile_size=None, compression=None, combine_rgb=True, pyramid_sizes_add=[])

Source code in OmeSliCC\conversion.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
def save_tiff(filename: str, image: np.ndarray, metadata: dict = None, xml_metadata: str = None,
              dimension_order: str = 'yxc',
              resolution: tuple = None, resolution_unit: str = None, tile_size: tuple = None, compression: [] = None,
              combine_rgb=True, pyramid_sizes_add: list = []):
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    size = image.shape[x_index], image.shape[y_index]

    nchannels = 1
    if 'c' in dimension_order:
        c_index = dimension_order.index('c')
        nchannels = image.shape[c_index]
    else:
        c_index = -1

    if tile_size is not None and isinstance(tile_size, int):
        tile_size = [tile_size] * 2

    split_channels = not (combine_rgb and nchannels == 3)
    if nchannels == 3 and not split_channels:
        photometric = PHOTOMETRIC.RGB
        image = np.moveaxis(image, c_index, -1)
        dimension_order = dimension_order.replace('c', '') + 'c'
    else:
        photometric = PHOTOMETRIC.MINISBLACK

    if resolution is not None:
        # tifffile only supports x/y pyramid resolution
        resolution = tuple(resolution[0:2])

    # maximum size (w/o compression)
    max_size = image.size * image.itemsize
    base_size = np.divide(max_size, np.prod(size))
    for new_size in pyramid_sizes_add:
        max_size += np.prod(new_size) * base_size
    bigtiff = (max_size > 2 ** 32)

    #scaler = Scaler(downscale=..., max_layer=len(pyramid_sizes_add))   # use ome-zarr-py dask scaling

    if xml_metadata is not None:
        # set ome=False to provide custom OME xml in description
        xml_metadata_bytes = xml_metadata.encode()
        is_ome = False
    else:
        xml_metadata_bytes = None
        is_ome = None
    with TiffWriter(filename, ome=is_ome, bigtiff=bigtiff) as writer:
        writer.write(image, photometric=photometric, subifds=len(pyramid_sizes_add),
                     resolution=resolution, resolutionunit=resolution_unit, tile=tile_size, compression=compression,
                     metadata=metadata, description=xml_metadata_bytes)
        for new_size in pyramid_sizes_add:
            image = image_resize(image, new_size, dimension_order=dimension_order)
            #image = scaler.resize_image(image)    # significantly slower
            writer.write(image, photometric=photometric, subfiletype=1,
                         resolution=resolution, resolutionunit=resolution_unit, tile=tile_size, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

store_tiles(sources, output_filename, params, composition_metadata=[], image_operations=[])

Source code in OmeSliCC\conversion.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def store_tiles(sources: list[OmeSource], output_filename: str, params: dict,
                composition_metadata: list = [], image_operations: list = []):
    output_params = params['output']
    tile_size = output_params.get('tile_size')
    compression = output_params.get('compression')
    npyramid_add = output_params.get('npyramid_add', 0)
    pyramid_downsample = output_params.get('pyramid_downsample')

    translations = []
    pixel_size = sources[0].get_pixel_size_micrometer()
    for meta in composition_metadata:
        bounds = meta['Bounds']
        translation = bounds['StartX'], bounds['StartY']
        translation_um = np.multiply(translation, pixel_size[:2])
        translations.append(translation_um)

    zarr = OmeZarr(output_filename)
    zarr.write(sources, tile_size=tile_size, compression=compression,
               npyramid_add=npyramid_add, pyramid_downsample=pyramid_downsample,
               translations=translations, image_operations=image_operations)

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

image_util

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

ome_metadata

OME_SCHEMA_LOC = f'{OME_URI} {OME_URI}/ome.xsd' module-attribute

OME_URI = 'http://www.openmicroscopy.org/Schemas/OME/2016-06' module-attribute

OME_XSI = 'http://www.w3.org/2001/XMLSchema-instance' module-attribute

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

create_ome_metadata(source, output_filename, combine_rgb=True, pyramid_sizes_add=[])

Source code in OmeSliCC\ome_metadata.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def create_ome_metadata(source: OmeSource,
                        output_filename: str,
                        combine_rgb: bool = True,
                        pyramid_sizes_add: list = []) -> str:

    file_name = os.path.basename(output_filename)
    file_title = get_filetitle(file_name)
    uuid = f'urn:uuid:{uuid4()}'
    software_name = toml.load("pyproject.toml")["project"]["name"]
    software_version = toml.load("pyproject.toml")["project"]["version"]

    ome = source.get_metadata().copy() if source.has_ome_metadata else XmlDict()
    ome['@xmlns'] = OME_URI
    ome['@xmlns:xsi'] = OME_XSI
    ome['@xsi:schemaLocation'] = OME_SCHEMA_LOC
    ome['@UUID'] = uuid
    ome['@Creator'] = f'{software_name} {software_version}'

    experimenter = ome.get('Experimenter')

    mag = source.get_mag()
    instrument = ome.get('Instrument')
    objective = ome.get('Instrument', {}).get('Objective')
    if isinstance(objective, list):
        objective = objective[0]
    if mag != 0:
        if instrument is None:
            instrument = {'@ID': 'Instrument:0'}
        if objective is None:
            objective = {'@ID': 'Objective:0'}
            instrument['Objective'] = objective
        objective['@NominalMagnification'] = mag
        ome['Instrument'] = instrument

    # currently only supporting single image
    nimages = 1

    images = []
    for imagei in range(nimages):
        images0 = ensure_list(ome.get('Image', []))
        if len(images0) > 0:
            image0 = images0[imagei]
        else:
            image0 = {}
        pixels0 = image0.get('Pixels', {})

        nchannels = source.get_nchannels()
        channels0 = source.get_channels()
        n = len(channels0)
        samples_per_pixel = nchannels // n

        channels = []
        for channeli, channel0 in enumerate(channels0):
            channel = XmlDict({'@Name': channel0.get('label', ''),
                               '@SamplesPerPixel': samples_per_pixel})
            color = channel0.get('color')
            if color:
                channel['@Color'] = rgba_to_int(color)
            channels.append(channel)

        if combine_rgb and len(channels) == 3:
            channel = channels[0].copy()
            channel['@SamplesPerPixel'] = nchannels
            channel.pop('@Color', None)
            channels = [channel]
        elif not combine_rgb and len(channels) < nchannels:
            channel = channels[0].copy()
            if '@Color' in channel:
                channel['@Color'] = rgba_to_int(channel['@Color'])
            channel['@SamplesPerPixel'] = 1
            channels = [channel] * nchannels

        for channeli, channel in enumerate(channels):
            channel['@ID'] = f'Channel:{imagei}:{channeli}'

        image = {
            '@ID': f'Image:{imagei}',
            '@Name': file_title,
        }

        xyzct = source.get_size_xyzct()
        pixels = {
            '@ID': f'Pixels:{imagei}',
            '@SizeX': xyzct[0],
            '@SizeY': xyzct[1],
            '@SizeZ': xyzct[2],
            '@SizeC': nchannels,
            '@SizeT': xyzct[4],
            '@Type': str(ensure_unsigned_type(source.get_pixel_type())),
            '@DimensionOrder': 'XYZCT',
            'Channel': channels,
            'TiffData': {'UUID': {'@FileName': file_name, '#text': uuid}},
        }

        pixel_size = source.get_pixel_size()
        if len(pixel_size) > 0 and pixel_size[0][0] != 0:
            pixels['@PhysicalSizeX'] = pixel_size[0][0]
        if len(pixel_size) > 1 and pixel_size[1][0] != 0:
            pixels['@PhysicalSizeY'] = pixel_size[1][0]
        if len(pixel_size) > 2 and pixel_size[2][0] != 0:
            pixels['@PhysicalSizeZ'] = pixel_size[2][0]
        if len(pixel_size) > 0 and pixel_size[0][1] != '':
            pixels['@PhysicalSizeXUnit'] = pixel_size[0][1]
        if len(pixel_size) > 1 and pixel_size[1][1] != '':
            pixels['@PhysicalSizeYUnit'] = pixel_size[1][1]
        if len(pixel_size) > 2 and pixel_size[2][1] != '':
            pixels['@PhysicalSizeZUnit'] = pixel_size[2][1]

        # TODO: create plane metadata if not exists
        planes = ensure_list(pixels0.get('Plane', []))
        if len(planes) > 0:
            pixels['Plane'] = planes

        if 'AcquisitionDate' in image0:
            image['AcquisitionDate'] = image0['AcquisitionDate']
        if 'Description' in image0:
            image['Description'] = image0['Description']
        # Set image refs
        if experimenter:
            image['ExperimenterRef'] = {'@ID': experimenter['@ID']}
        if instrument:
            image['InstrumentRef'] = {'@ID': instrument['@ID']}
        if objective:
            image['ObjectiveSettings'] = {'@ID': objective['@ID']}
        # (end image refs)
        if 'StageLabel' in image0:
            image['StageLabel'] = image0['StageLabel']
        image['Pixels'] = pixels
        images.append(image)

    ome['Image'] = images

    if 'StructuredAnnotations' not in ome:
        ome['StructuredAnnotations'] = {}

    # filter source pyramid sizes
    map_annotations0 = ensure_list(ome['StructuredAnnotations'].get('MapAnnotation', []))
    map_annotations = [annotation for annotation in map_annotations0
                       if 'resolution' not in annotation.get('ID', '').lower()]
    # add pyramid sizes
    if pyramid_sizes_add:
        key_value_map = {'M': [{'@K': i + 1, '#text': f'{" ".join([str(size) for size in pyramid_size])}'}
                               for i, pyramid_size in enumerate(pyramid_sizes_add)]}
        map_annotations.insert(0, {
            '@ID': 'Annotation:Resolution:0',
            '@Namespace': 'openmicroscopy.org/PyramidResolution',
            'Value': key_value_map
        })
    ome['StructuredAnnotations']['MapAnnotation'] = map_annotations

    # filter original metadata elements
    xml_annotations0 = ensure_list(ome.get('StructuredAnnotations', {}).get('XMLAnnotation', []))
    xml_annotations = [annotation for annotation in xml_annotations0
                       if 'originalmetadata' not in annotation.get('Namespace', '').lower()]
    ome['StructuredAnnotations']['XMLAnnotation'] = xml_annotations

    return dict2xml({'OME': ome})

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

ome_zarr_util

create_axes_metadata(dimension_order)

Source code in OmeSliCC\ome_zarr_util.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def create_axes_metadata(dimension_order):
    axes = []
    for dimension in dimension_order:
        unit1 = None
        if dimension == 't':
            type1 = 'time'
            unit1 = 'millisecond'
        elif dimension == 'c':
            type1 = 'channel'
        else:
            type1 = 'space'
            unit1 = 'micrometer'
        axis = {'name': dimension, 'type': type1}
        if unit1 is not None and unit1 != '':
            axis['unit'] = unit1
        axes.append(axis)
    return axes

create_channel_metadata(source, ome_version)

Source code in OmeSliCC\ome_zarr_util.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def create_channel_metadata(source, ome_version):
    channels = source.get_channels()
    nchannels = source.get_nchannels()

    if len(channels) < nchannels == 3:
        labels = ['Red', 'Green', 'Blue']
        colors = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)]
        channels = [{'label': label, 'color': color} for label, color in zip(labels, colors)]

    omezarr_channels = []
    for channeli, channel0 in enumerate(channels):
        channel = channel0.copy()
        color = channel.get('color', (1, 1, 1, 1))
        channel['color'] = rgba_to_hexrgb(color)
        if 'window' not in channel:
            channel['window'] = source.get_channel_window(channeli)
        omezarr_channels.append(channel)

    metadata = {
        'version': ome_version,
        'channels': omezarr_channels,
    }
    return metadata

create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[])

Source code in OmeSliCC\ome_zarr_util.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def create_transformation_metadata(dimension_order, pixel_size_um, scale, translation_um=[]):
    metadata = []
    pixel_size_scale = []
    translation_scale = []
    for dimension in dimension_order:
        if dimension == 'z' and len(pixel_size_um) > 2:
            pixel_size_scale1 = pixel_size_um[2]
        elif dimension == 'y' and len(pixel_size_um) > 1:
            pixel_size_scale1 = pixel_size_um[1] / scale
        elif dimension == 'x' and len(pixel_size_um) > 0:
            pixel_size_scale1 = pixel_size_um[0] / scale
        else:
            pixel_size_scale1 = 1
        if pixel_size_scale1 == 0:
            pixel_size_scale1 = 1
        pixel_size_scale.append(pixel_size_scale1)

        if dimension == 'z' and len(translation_um) > 2:
            translation1 = translation_um[2]
        elif dimension == 'y' and len(translation_um) > 1:
            translation1 = translation_um[1] * scale
        elif dimension == 'x' and len(translation_um) > 0:
            translation1 = translation_um[0] * scale
        else:
            translation1 = 0
        translation_scale.append(translation1)

    metadata.append({'type': 'scale', 'scale': pixel_size_scale})
    if not all(v == 0 for v in translation_scale):
        metadata.append({'type': 'translation', 'translation': translation_scale})
    return metadata

hexrgb_to_rgba(hexrgb)

Source code in OmeSliCC\color_conversion.py
21
22
23
def hexrgb_to_rgba(hexrgb: str) -> list:
    rgba = int_to_rgba(eval('0x' + hexrgb + 'FF'))
    return rgba

int_to_rgba(intrgba)

Source code in OmeSliCC\color_conversion.py
3
4
5
6
7
8
def int_to_rgba(intrgba: int) -> list:
    signed = (intrgba < 0)
    rgba = [x / 255 for x in intrgba.to_bytes(4, signed=signed, byteorder="big")]
    if rgba[-1] == 0:
        rgba[-1] = 1
    return rgba

rgba_to_hexrgb(rgba)

Source code in OmeSliCC\color_conversion.py
16
17
18
def rgba_to_hexrgb(rgba: list) -> str:
    hexrgb = ''.join([hex(int(x * 255))[2:].upper().zfill(2) for x in rgba[:3]])
    return hexrgb

rgba_to_int(rgba)

Source code in OmeSliCC\color_conversion.py
11
12
13
def rgba_to_int(rgba: list) -> int:
    intrgba = int.from_bytes([int(x * 255) for x in rgba], signed=True, byteorder="big")
    return intrgba

scale_dimensions_dict(shape0, scale)

Source code in OmeSliCC\ome_zarr_util.py
 92
 93
 94
 95
 96
 97
 98
 99
100
def scale_dimensions_dict(shape0, scale):
    shape = {}
    if scale == 1:
        return shape0
    for dimension, shape1 in shape0.items():
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape[dimension] = shape1
    return shape

scale_dimensions_xy(shape0, dimension_order, scale)

Source code in OmeSliCC\ome_zarr_util.py
81
82
83
84
85
86
87
88
89
def scale_dimensions_xy(shape0, dimension_order, scale):
    shape = []
    if scale == 1:
        return shape0
    for shape1, dimension in zip(shape0, dimension_order):
        if dimension[0] in ['x', 'y']:
            shape1 = int(shape1 * scale)
        shape.append(shape1)
    return shape

omero_credentials

create_credentials_file(public_key_filename, credentials_filename)

Source code in OmeSliCC\omero_credentials.py
12
13
14
15
16
17
18
def create_credentials_file(public_key_filename: str, credentials_filename: str):
    with open(public_key_filename, 'r') as key_file, open(credentials_filename, 'wb') as enc_file:
        keydata = key_file.read().encode()
        pub_key = rsa.PublicKey.load_pkcs1(keydata)
        enc_cred = rsa.encrypt((input('Username: ') + '\t' + getpass('Password: ')).encode(), pub_key)
        enc_file.write(enc_cred)
        print('credentials file generated')

decrypt_credentials(private_key_filename, credentials_filename)

Source code in OmeSliCC\omero_credentials.py
21
22
23
24
25
26
27
28
29
def decrypt_credentials(private_key_filename: str, credentials_filename: str) -> tuple:
    with open(credentials_filename, 'rb') as cred_f, open(private_key_filename, 'rb') as key_file:
        keydata = key_file.read()
        pri_key = rsa.PrivateKey.load_pkcs1(keydata)
        cred = cred_f.read()
        dec_cred = rsa.decrypt(cred, pri_key).decode().split()
        usr = dec_cred[0]
        pwd = dec_cred[1]
    return usr, pwd

generate_asymmetric_keys(private_key_filename, public_key_filename)

Source code in OmeSliCC\omero_credentials.py
5
6
7
8
9
def generate_asymmetric_keys(private_key_filename: str, public_key_filename: str):
    pub_key, pri_key = rsa.newkeys(2048)
    with open(public_key_filename, 'w') as pub_file, open(private_key_filename, 'w') as pri_file:
        pub_file.write(pub_key.save_pkcs1().decode())
        pri_file.write(pri_key.save_pkcs1().decode())

omero_metadata

OME_SCHEMA_LOC = f'{OME_URI} {OME_URI}/ome.xsd' module-attribute

OME_URI = 'http://www.openmicroscopy.org/Schemas/OME/2016-06' module-attribute

OME_XSI = 'http://www.w3.org/2001/XMLSchema-instance' module-attribute

blur_image(image, sigma)

Source code in OmeSliCC\image_util.py
470
471
472
473
474
475
476
477
478
def blur_image(image, sigma):
    nchannels = image.shape[2] if image.ndim == 3 else 1
    if nchannels not in [1, 3]:
        new_image = np.zeros_like(image)
        for channeli in range(nchannels):
            new_image[..., channeli] = blur_image_single(image[..., channeli], sigma)
    else:
        new_image = blur_image_single(image, sigma)
    return new_image

blur_image_single(image, sigma)

Source code in OmeSliCC\image_util.py
466
467
def blur_image_single(image, sigma):
    return gaussian_filter(image, sigma)

calc_fraction_used(image, threshold=0.1)

Source code in OmeSliCC\image_util.py
451
452
453
454
455
456
457
458
459
460
461
462
463
def calc_fraction_used(image: np.ndarray, threshold: float = 0.1) -> float:
    low = int(round(threshold * 255))
    high = int(round((1 - threshold) * 255))
    shape = image.shape
    total = shape[0] * shape[1]
    good = 0
    for y in range(shape[0]):
        for x in range(shape[1]):
            pixel = image[y, x]
            if low <= pixel[0] < high and low <= pixel[1] < high and low <= pixel[2] < high:
                good += 1
    fraction = good / total
    return fraction

calc_pyramid(xyzct, npyramid_add=0, pyramid_downsample=2, volumetric_resize=False)

Source code in OmeSliCC\image_util.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def calc_pyramid(xyzct: tuple, npyramid_add: int = 0, pyramid_downsample: float = 2,
                 volumetric_resize: bool = False) -> list:
    x, y, z, c, t = xyzct
    if volumetric_resize and z > 1:
        size = (x, y, z)
    else:
        size = (x, y)
    sizes_add = []
    scale = 1
    for _ in range(npyramid_add):
        scale /= pyramid_downsample
        scaled_size = np.maximum(np.round(np.multiply(size, scale)).astype(int), 1)
        sizes_add.append(scaled_size)
    return sizes_add

calc_tiles_median(images)

Source code in OmeSliCC\image_util.py
481
482
483
484
def calc_tiles_median(images):
    out_image = np.zeros_like(images[0])
    median_image = np.median(images, 0, out_image)
    return median_image

calc_tiles_quantiles(images, quantiles)

Source code in OmeSliCC\image_util.py
487
488
489
490
491
492
493
494
def calc_tiles_quantiles(images, quantiles):
    out_quantiles = []
    quantile_images = np.quantile(images, quantiles, 0)
    for quantile_image in quantile_images:
        maxval = 2 ** (8 * images[0].dtype.itemsize) - 1
        image = (quantile_image / maxval).astype(np.float32)
        out_quantiles.append(image)
    return out_quantiles

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

check_versions()

Source code in OmeSliCC\image_util.py
29
30
31
def check_versions():
    print(f'tifffile {tifffile.__version__}')
    print(imagecodecs.version())

compare_image(image0, image1, show=False)

Source code in OmeSliCC\image_util.py
413
414
415
416
417
418
419
def compare_image(image0, image1, show=False) -> float:
    dif, dif_max, dif_mean, psnr = compare_image_dist(image0, image1)
    print(f'rgb dist max: {dif_max:.1f} mean: {dif_mean:.1f} PSNR: {psnr:.1f}')
    if show:
        show_image(dif)
        show_image((dif * 10).astype(np.uint8))
    return dif

compare_image_dist(image0, image1)

Source code in OmeSliCC\image_util.py
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def compare_image_dist(image0: np.ndarray, image1: np.ndarray) -> tuple:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    if dif.size > 1000000000:
        # split very large array
        rgb_maxs = []
        rgb_means = []
        for dif1 in np.array_split(dif, 16):
            rgb_dif = np.linalg.norm(dif1, axis=2)
            rgb_maxs.append(np.max(rgb_dif))
            rgb_means.append(np.mean(rgb_dif))
        rgb_max = np.max(rgb_maxs)
        rgb_mean = np.mean(rgb_means)
    else:
        rgb_dif = np.linalg.norm(dif, axis=2)
        rgb_max = np.max(rgb_dif)
        rgb_mean = np.mean(rgb_dif)
    return dif, rgb_max, rgb_mean, psnr

compare_image_dist_simple(image0, image1)

Source code in OmeSliCC\image_util.py
442
443
444
445
446
447
448
def compare_image_dist_simple(image0: np.ndarray, image1: np.ndarray) -> dict:
    dif = cv.absdiff(image0, image1)
    psnr = cv.PSNR(image0, image1)
    rgb_dif = np.linalg.norm(dif, axis=2)
    dif_max = np.max(rgb_dif)
    dif_mean = np.mean(rgb_dif)
    return {'dif_max': dif_max, 'dif_mean': dif_mean, 'psnr': psnr}

convert_image_sign_type(image, target_dtype)

Source code in OmeSliCC\image_util.py
81
82
83
84
85
86
87
88
89
90
91
def convert_image_sign_type(image: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
    source_dtype = image.dtype
    if source_dtype.kind == target_dtype.kind:
        new_image = image
    elif source_dtype.kind == 'i':
        new_image = ensure_unsigned_image(image)
    else:
        # conversion without overhead
        offset = 2 ** (8 * target_dtype.itemsize - 1)
        new_image = (image - offset).astype(target_dtype)
    return new_image

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

create_compression_codecs(compression)

Source code in OmeSliCC\image_util.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def create_compression_codecs(compression: list) -> list:
    codecs = None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            codecs = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            codecs = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            codecs = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            codecs = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            codecs = [Jpegxl(level=level)]
        else:
            codecs = [compression]
    return codecs

create_compression_filter(compression)

Source code in OmeSliCC\image_util.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def create_compression_filter(compression: list) -> tuple:
    compressor, compression_filters = None, None
    compression = ensure_list(compression)
    if compression is not None and len(compression) > 0:
        compression_type = compression[0].lower()
        if len(compression) > 1:
            level = int(compression[1])
        else:
            level = None
        if 'lzw' in compression_type:
            from imagecodecs.numcodecs import Lzw
            compression_filters = [Lzw()]
        elif '2k' in compression_type or '2000' in compression_type:
            from imagecodecs.numcodecs import Jpeg2k
            compression_filters = [Jpeg2k(level=level)]
        elif 'jpegls' in compression_type:
            from imagecodecs.numcodecs import Jpegls
            compression_filters = [Jpegls(level=level)]
        elif 'jpegxr' in compression_type:
            from imagecodecs.numcodecs import Jpegxr
            compression_filters = [Jpegxr(level=level)]
        elif 'jpegxl' in compression_type:
            from imagecodecs.numcodecs import Jpegxl
            compression_filters = [Jpegxl(level=level)]
        else:
            compressor = compression
    return compressor, compression_filters

create_ome_metadata_from_omero(source, image_object, output_filename, combine_rgb=True, pyramid_sizes_add=None)

Source code in OmeSliCC\omero_metadata.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def create_ome_metadata_from_omero(source: OmeSource,
                                   image_object: omero.gateway.ImageWrapper,
                                   output_filename: str,
                                   combine_rgb: bool = True,
                                   pyramid_sizes_add: list = None) -> str:

    file_name = os.path.basename(output_filename)
    file_title = get_filetitle(file_name)
    uuid = f'urn:uuid:{uuid4()}'
    software_name = toml.load("pyproject.toml")["project"]["name"]
    software_version = toml.load("pyproject.toml")["project"]["version"]

    ome = XmlDict()
    ome['@xmlns'] = OME_URI
    ome['@xmlns:xsi'] = OME_XSI
    ome['@xsi:schemaLocation'] = OME_SCHEMA_LOC
    ome['@UUID'] = uuid
    ome['@Creator'] = f'{software_name} {software_version}'

    instrument = None
    objective = None
    instrument0 = image_object.getInstrument()
    if instrument0 is not None:
        instrument = {'@ID': 'Instrument:0'}
        objectives0 = instrument0.getObjectives()
        if objectives0 is not None:
            objective0 = objectives0[0]
            wd = objective0.getWorkingDistance()
            if wd is not None:
                working_distance = wd.getValue()
                working_distance_unit = wd.getSymbol()
            else:
                working_distance = None
                working_distance_unit = None
            objective = {
                '@ID': 'Objective:0',
                '@Manufacturer': objective0.getManufacturer(),
                '@Model': objective0.getModel(),
                '@LotNumber': objective0.getLotNumber(),
                '@SerialNumber': objective0.getSerialNumber(),
                '@NominalMagnification': source.get_mag(),
                '@CalibratedMagnification': source.get_mag(),
                '@LensNA': objective0.getLensNA(),
                '@WorkingDistance': working_distance,
                '@WorkingDistanceUnit': working_distance_unit,
                '@Iris': objective0.getIris(),
            }
            correction = objective0.getCorrection().getValue()
            if correction is not None and correction.lower() not in ['', 'unknown']:
                objective['@Correction'] = correction
            immersion = objective0.getImmersion().getValue()
            if immersion is not None and immersion.lower() not in ['', 'unknown']:
                objective['@Immersion'] = immersion
            instrument['Objective'] = objective
        ome['Instrument'] = instrument

    # currently only supporting single image
    nimages = 1

    images = []
    for imagei in range(nimages):
        channels = []
        planes = []

        pixels0 = image_object.getPrimaryPixels()

        stage0 = image_object.getStageLabel()
        if stage0:
            for plane0 in pixels0.copyPlaneInfo():
                planes.append({
                    '@TheC': plane0.getTheC(),
                    '@TheT': plane0.getTheT(),
                    '@TheZ': plane0.getTheZ(),
                    'DeltaT': plane0.getDeltaT(),
                    'ExposureTime': plane0.getExposureTime(),
                    'PositionX': stage0.getPositionX().getValue(),
                    'PositionY': stage0.getPositionY().getValue(),
                    'PositionZ': stage0.getPositionZ().getValue(),
                    'PositionXUnit': stage0.getPositionX().getSymbol(),
                    'PositionYUnit': stage0.getPositionY().getSymbol(),
                    'PositionZUnit': stage0.getPositionZ().getSymbol(),
                })

        channelso = image_object.getChannels()
        for channeli, channelo in enumerate(channelso):
            channell = channelo.getLogicalChannel()
            light_path = channell.getLightPath()
            if light_path is None:
                light_path = {}
            channel = {
                '@ID': f'Channel:{imagei}:{channeli}',
                '@Name': channelo.getName(),
                '@Color': channelo.getColor().getInt(),
                '@EmissionWave': channelo.getEmissionWave(),
                '@ExcitationWave': channelo.getExcitationWave(),
                '@PockelCellSetting': channell.getPockelCellSetting(),
                '@Fluor': channell.getFluor(),
                '@ContrastMethod': channell.getContrastMethod(),
                '@PinHoleSize': channell.getPinHoleSize(),
                '@SamplesPerPixel': channell.getSamplesPerPixel(),
                '@NDFilter': channell.getNdFilter(),
                'LightPath': light_path,
            }
            channels.append(channel)

        nchannels = source.get_nchannels()
        if combine_rgb and len(channels) == 3:
            channel = channels[0].copy()
            channel['@SamplesPerPixel'] = nchannels
            channel.pop('@Color', None)
            channels = [channel]
        elif not combine_rgb and len(channels) < nchannels:
            channel = channels[0].copy()
            if '@Color' in channel:
                channel['@Color'] = rgba_to_int(channel['@Color'])
            channel['@SamplesPerPixel'] = 1
            channels = [channel] * nchannels

        image = {
            '@ID': f'Image:{imagei}',
            '@Name': file_title,
            'AcquisitionDate': image_object.getAcquisitionDate().isoformat(),
            'Description': image_object.getDescription(),
        }

        xyzct = source.get_size_xyzct()
        pixels = {
            '@ID': f'Pixels:{imagei}',
            '@SizeX': xyzct[0],
            '@SizeY': xyzct[1],
            '@SizeZ': xyzct[2],
            '@SizeC': nchannels,
            '@SizeT': xyzct[4],
            '@Type': str(ensure_unsigned_type(source.get_pixel_type())),
            '@DimensionOrder': 'XYZCT',
            'Channel': channels,
            'TiffData': {'UUID': {'@FileName': file_name, '#text': uuid}},
        }
        pixel_size = source.get_pixel_size()
        if len(pixel_size) > 0 and pixel_size[0][0] != 0:
            pixels['@PhysicalSizeX'] = pixel_size[0][0]
        if len(pixel_size) > 1 and pixel_size[1][0] != 0:
            pixels['@PhysicalSizeY'] = pixel_size[1][0]
        if len(pixel_size) > 2 and pixel_size[2][0] != 0:
            pixels['@PhysicalSizeZ'] = pixel_size[2][0]
        if len(pixel_size) > 0 and pixel_size[0][1] != '':
            pixels['@PhysicalSizeXUnit'] = pixel_size[0][1]
        if len(pixel_size) > 1 and pixel_size[1][1] != '':
            pixels['@PhysicalSizeYUnit'] = pixel_size[1][1]
        if len(pixel_size) > 2 and pixel_size[2][1] != '':
            pixels['@PhysicalSizeZUnit'] = pixel_size[2][1]

        if len(planes) > 0:
            pixels['Plane'] = planes

        # Set image refs
        if instrument:
            image['InstrumentRef'] = {'@ID': instrument['@ID']}
        if objective:
            image['ObjectiveSettings'] = {'@ID': objective['@ID']}
        # (end image refs)
        if stage0:
            image['StageLabel'] = {'@Name': stage0.getName()}
        image['Pixels'] = pixels
        images.append(image)

    ome['Image'] = images

    annotations = {}
    for annotations0 in image_object.listAnnotations():
        annotation_type = annotations0.OMERO_TYPE.__name__
        if annotation_type.endswith('I'):
            annotation_type = annotation_type[:-1]
        if annotation_type not in annotations:
            annotations[annotation_type] = []
        value = annotations0.getValue()
        if annotation_type == 'MapAnnotation':
            value = {'M': [{'@K': k, '#text': v} for k, v in value]}
        annotations[annotation_type].append({
            '@ID': f'Annotation:{len(annotations[annotation_type])}',
            '@Namespace': annotations0.getNs(),
            'Value': value
        })
    # add pyramid sizes
    if pyramid_sizes_add:
        if 'MapAnnotation' not in annotations:
            annotations['MapAnnotation'] = []
        key_value_map = {'M': [{'@K': i + 1, '#text': f'{" ".join([str(size) for size in pyramid_size])}'}
                               for i, pyramid_size in enumerate(pyramid_sizes_add)]}
        annotations['MapAnnotation'].insert(0, {
            '@ID': 'Annotation:Resolution:0',
            '@Namespace': 'openmicroscopy.org/PyramidResolution',
            'Value': key_value_map
        })

    ome['StructuredAnnotations'] = annotations

    return dict2xml({'OME': filter_dict(ome)})

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

ensure_unsigned_image(image)

Source code in OmeSliCC\image_util.py
69
70
71
72
73
74
75
76
77
78
def ensure_unsigned_image(image: np.ndarray) -> np.ndarray:
    source_dtype = image.dtype
    dtype = ensure_unsigned_type(source_dtype)
    if dtype != source_dtype:
        # conversion without overhead
        offset = 2 ** (8 * dtype.itemsize - 1)
        new_image = image.astype(dtype) + offset
    else:
        new_image = image
    return new_image

ensure_unsigned_type(dtype)

Source code in OmeSliCC\image_util.py
62
63
64
65
66
def ensure_unsigned_type(dtype: np.dtype) -> np.dtype:
    new_dtype = dtype
    if dtype.kind == 'i' or dtype.byteorder == '>' or dtype.byteorder == '<':
        new_dtype = np.dtype(f'u{dtype.itemsize}')
    return new_dtype

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

float2int_image(image, target_dtype=np.dtype(np.uint8))

Source code in OmeSliCC\image_util.py
53
54
55
56
57
58
59
def float2int_image(image, target_dtype=np.dtype(np.uint8)):
    source_dtype = image.dtype
    if source_dtype.kind not in ('i', 'u') and not target_dtype.kind == 'f':
        maxval = 2 ** (8 * target_dtype.itemsize) - 1
        return (image * maxval).astype(target_dtype)
    else:
        return image

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_image_quantile(image, quantile, axis=None)

Source code in OmeSliCC\image_util.py
136
137
138
def get_image_quantile(image: np.ndarray, quantile: float, axis=None) -> float:
    value = np.quantile(image, quantile, axis=axis).astype(image.dtype)
    return value

get_image_size_info(sizes_xyzct, pixel_nbytes, pixel_type, channels)

Source code in OmeSliCC\image_util.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_image_size_info(sizes_xyzct: list, pixel_nbytes: int, pixel_type: np.dtype, channels: list) -> str:
    image_size_info = 'XYZCT:'
    size = 0
    for i, size_xyzct in enumerate(sizes_xyzct):
        w, h, zs, cs, ts = size_xyzct
        size += np.int64(pixel_nbytes) * w * h * zs * cs * ts
        if i > 0:
            image_size_info += ','
        image_size_info += f' {w} {h} {zs} {cs} {ts}'
    image_size_info += f' Pixel type: {pixel_type} Uncompressed: {print_hbytes(size)}'
    if sizes_xyzct[0][3] == 3:
        channel_info = 'rgb'
    else:
        channel_info = ','.join([channel.get('Name', '') for channel in channels])
    if channel_info != '':
        image_size_info += f' Channels: {channel_info}'
    return image_size_info

get_numpy_slicing(dimension_order, **slicing)

Source code in OmeSliCC\image_util.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_numpy_slicing(dimension_order, **slicing):
    slices = []
    for axis in dimension_order:
        index = slicing.get(axis)
        index0 = slicing.get(axis + '0')
        index1 = slicing.get(axis + '1')
        if index0 is not None and index1 is not None:
            slice1 = slice(int(index0), int(index1))
        elif index is not None:
            slice1 = int(index)
        else:
            slice1 = slice(None)
        slices.append(slice1)
    return tuple(slices)

get_pil_metadata(image)

Source code in OmeSliCC\image_util.py
399
400
401
402
403
404
405
406
407
408
409
410
def get_pil_metadata(image: PIL.Image) -> dict:
    metadata = {}
    exifdata = image.getexif()
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        if isinstance(data, bytes):
            data = data.decode()
        metadata[tag] = data
    if metadata == {}:
        metadata = image.info
    return metadata

get_tiff_pages(tiff)

Source code in OmeSliCC\image_util.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_tiff_pages(tiff: TiffFile) -> list:
    # TODO: review so this works for multi-level ome-tiff, tiff-stack, and z pages tiff, then later check for mmstack
    pages = []
    found = False
    if tiff.series and not tiff.is_mmstack:
        # has series
        baseline = tiff.series[0]
        for level in baseline.levels:
            # has levels
            level_pages = []
            for page in level.pages:
                found = True
                level_pages.append(page)
            if level_pages:
                pages.append(level_pages)

    if not found:
        for page in tiff.pages:
            pages.append(page)
    return pages

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

image_reshape(image, target_size)

Source code in OmeSliCC\image_util.py
202
203
204
205
206
207
208
209
210
211
212
213
214
def image_reshape(image: np.ndarray, target_size: tuple) -> np.ndarray:
    tw, th = target_size
    sh, sw = image.shape[0:2]
    if sw < tw or sh < th:
        dw = max(tw - sw, 0)
        dh = max(th - sh, 0)
        padding = [(0, dh), (0, dw)]
        if len(image.shape) == 3:
            padding += [(0, 0)]
        image = np.pad(image, padding, 'edge')
    if tw < sw or th < sh:
        image = image[0:th, 0:tw]
    return image

image_resize(image, target_size0, dimension_order='yxc')

Source code in OmeSliCC\image_util.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def image_resize(image: np.ndarray, target_size0: tuple, dimension_order: str = 'yxc') -> np.ndarray:
    shape = image.shape
    x_index = dimension_order.index('x')
    y_index = dimension_order.index('y')
    c_is_at_end = ('c' in dimension_order and dimension_order.endswith('c'))
    size = shape[x_index], shape[y_index]
    if np.mean(np.divide(size, target_size0)) < 1:
        interpolation = cv.INTER_CUBIC
    else:
        interpolation = cv.INTER_AREA
    dtype0 = image.dtype
    image = ensure_unsigned_image(image)
    target_size = tuple(np.maximum(np.round(target_size0).astype(int), 1))
    if dimension_order in ['yxc', 'yx']:
        new_image = cv.resize(np.asarray(image), target_size, interpolation=interpolation)
    elif dimension_order == 'cyx':
        new_image = np.moveaxis(image, 0, -1)
        new_image = cv.resize(np.asarray(new_image), target_size, interpolation=interpolation)
        new_image = np.moveaxis(new_image, -1, 0)
    else:
        ts = image.shape[dimension_order.index('t')] if 't' in dimension_order else 1
        zs = image.shape[dimension_order.index('z')] if 'z' in dimension_order else 1
        target_shape = list(image.shape).copy()
        target_shape[x_index] = target_size[0]
        target_shape[y_index] = target_size[1]
        new_image = np.zeros(target_shape, dtype=image.dtype)
        for t in range(ts):
            for z in range(zs):
                slices = get_numpy_slicing(dimension_order, z=z, t=t)
                image1 = image[slices]
                if not c_is_at_end:
                    image1 = np.moveaxis(image1, 0, -1)
                new_image1 = np.atleast_3d(cv.resize(np.asarray(image1), target_size, interpolation=interpolation))
                if not c_is_at_end:
                    new_image1 = np.moveaxis(new_image1, -1, 0)
                new_image[slices] = new_image1
    new_image = convert_image_sign_type(new_image, dtype0)
    return new_image

int2float_image(image)

Source code in OmeSliCC\image_util.py
44
45
46
47
48
49
50
def int2float_image(image):
    source_dtype = image.dtype
    if not source_dtype.kind == 'f':
        maxval = 2 ** (8 * source_dtype.itemsize) - 1
        return image / np.float32(maxval)
    else:
        return image

normalise_values(image, min_value, max_value)

Source code in OmeSliCC\image_util.py
141
142
def normalise_values(image: np.ndarray, min_value: float, max_value: float) -> np.ndarray:
    return np.clip((image.astype(np.float32) - min_value) / (max_value - min_value), 0, 1)

pilmode_to_pixelinfo(mode)

Source code in OmeSliCC\image_util.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def pilmode_to_pixelinfo(mode: str) -> tuple:
    pixelinfo = (np.uint8, 8, 1)
    mode_types = {
        'I': (np.uint32, 32, 1),
        'F': (np.float32, 32, 1),
        'RGB': (np.uint8, 24, 3),
        'RGBA': (np.uint8, 32, 4),
        'CMYK': (np.uint8, 32, 4),
        'YCbCr': (np.uint8, 24, 3),
        'LAB': (np.uint8, 24, 3),
        'HSV': (np.uint8, 24, 3),
    }
    if '16' in mode:
        pixelinfo = (np.uint16, 16, 1)
    elif '32' in mode:
        pixelinfo = (np.uint32, 32, 1)
    elif mode in mode_types:
        pixelinfo = mode_types[mode]
    pixelinfo = (np.dtype(pixelinfo[0]), pixelinfo[1])
    return pixelinfo

precise_resize(image, factors)

Source code in OmeSliCC\image_util.py
257
258
259
260
261
def precise_resize(image: np.ndarray, factors) -> np.ndarray:
    if image.ndim > len(factors):
        factors = list(factors) + [1]
    new_image = downscale_local_mean(np.asarray(image), tuple(factors)).astype(image.dtype)
    return new_image

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

redimension_data(data, old_order, new_order, **indices)

Source code in OmeSliCC\image_util.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def redimension_data(data, old_order, new_order, **indices):
    # able to provide optional dimension values e.g. t=0, z=0
    if new_order == old_order:
        return data

    new_data = data
    order = old_order
    # remove
    for o in old_order:
        if o not in new_order:
            index = order.index(o)
            dim_value = indices.get(o, 0)
            new_data = np.take(new_data, indices=dim_value, axis=index)
            order = order[:index] + order[index + 1:]
    # add
    for o in new_order:
        if o not in order:
            new_data = np.expand_dims(new_data, 0)
            order = o + order
    # move
    old_indices = [order.index(o) for o in new_order]
    new_indices = list(range(len(new_order)))
    new_data = np.moveaxis(new_data, old_indices, new_indices)
    return new_data

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

save_image(image, filename, output_params={})

Source code in OmeSliCC\image_util.py
497
498
499
def save_image(image: np.ndarray, filename: str, output_params: dict = {}):
    compression = output_params.get('compression')
    PIL.Image.fromarray(image).save(filename, compression=compression)

show_image(image)

Source code in OmeSliCC\image_util.py
34
35
36
def show_image(image: np.ndarray):
    plt.imshow(image)
    plt.show()

show_image_gray(image)

Source code in OmeSliCC\image_util.py
39
40
41
def show_image_gray(image: np.ndarray):
    plt.imshow(image, cmap='gray')
    plt.show()

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units

tags_to_dict(tags)

Source code in OmeSliCC\image_util.py
344
345
346
347
348
def tags_to_dict(tags: tifffile.TiffTags) -> dict:
    tag_dict = {}
    for tag in tags.values():
        tag_dict[tag.name] = tag.value
    return tag_dict

tiff_info(filename)

Source code in OmeSliCC\image_util.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def tiff_info(filename: str) -> str:
    s = ''
    nom_size = 0
    tiff = TiffFile(filename)
    real_size = tiff.fstat.st_size
    s += str(tiff) + '\n'
    if tiff.ome_metadata:
        print(tiff.ome_metadata)
        s += f'OME: {print_dict(tifffile.xml2dict(tiff.ome_metadata))}\n'
    if tiff.metaseries_metadata:
        s += f'Series: {tiff.metaseries_metadata}\n'
    if tiff.imagej_metadata:
        s += f'ImageJ: {tiff.imagej_metadata}\n'

    for page0 in get_tiff_pages(tiff):
        page = page0[0] if isinstance(page0, list) else page0
        s += str(page) + '\n'
        s += f'Size: {np.flip(page.shape)} ({print_hbytes(page.size)})\n'
        if page.is_tiled:
            s += f'Tiling: {page.tilewidth} {page.tilelength} {page.tiledepth}\n'
        s += f'Compression: {str(page.compression)} jpegtables: {page.jpegtables is not None}\n'
        tag_dict = tags_to_dict(page.tags)
        if 'TileOffsets' in tag_dict:
            tag_dict.pop('TileOffsets')
        if 'TileByteCounts' in tag_dict:
            tag_dict.pop('TileByteCounts')
        if 'ImageDescription' in tag_dict and tag_dict['ImageDescription'].startswith('<?xml'):
            # redundant
            tag_dict.pop('ImageDescription')
        s += print_dict(tag_dict) + '\n\n'
        nom_size += page.size

    s += f'Overall compression: 1:{nom_size / real_size:.1f}'
    return s

tiff_info_short(filename)

Source code in OmeSliCC\image_util.py
387
388
389
390
391
392
393
394
395
396
def tiff_info_short(filename: str) -> str:
    nom_size = 0
    tiff = TiffFile(filename)
    s = str(filename)
    real_size = tiff.fstat.st_size
    for page in tiff.pages:
        s += ' ' + str(page)
        nom_size += page.size
    s += f' Image size:{nom_size} File size:{real_size} Overall compression: 1:{nom_size / real_size:.1f}'
    return s

parameters

IMAGE_DIR = RESOURCE_DIR + 'images/' module-attribute

PARAMETER_FILE = RESOURCE_DIR + 'params.yml' module-attribute

RESOURCE_DIR = 'resources/' module-attribute

ChannelOperation

Bases: Enum

Source code in OmeSliCC\parameters.py
4
5
6
7
class ChannelOperation(Enum):
    NONE = 0
    COMBINE = 1
    SPLIT = 2

COMBINE = 1 class-attribute instance-attribute

NONE = 0 class-attribute instance-attribute

SPLIT = 2 class-attribute instance-attribute

util

check_round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
117
118
119
120
121
122
123
124
125
def check_round_significants(a: float, significant_digits: int) -> float:
    rounded = round_significants(a, significant_digits)
    if a != 0:
        dif = 1 - rounded / a
    else:
        dif = rounded - a
    if abs(dif) < 10 ** -significant_digits:
        return rounded
    return a

convert_rational_value(value)

Source code in OmeSliCC\util.py
213
214
215
216
def convert_rational_value(value) -> float:
    if value is not None and isinstance(value, tuple):
        value = value[0] / value[1]
    return value

desc_to_dict(desc)

Source code in OmeSliCC\util.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def desc_to_dict(desc: str) -> dict:
    desc_dict = {}
    if desc.startswith('{'):
        try:
            metadata = ast.literal_eval(desc)
            return metadata
        except:
            pass
    for item in re.split(r'[\r\n\t|]', desc):
        item_sep = '='
        if ':' in item:
            item_sep = ':'
        if item_sep in item:
            items = item.split(item_sep)
            key = items[0].strip()
            value = items[1].strip()
            for dtype in (int, float, bool):
                try:
                    value = dtype(value)
                    break
                except:
                    pass
            desc_dict[key] = value
    return desc_dict

ensure_list(x)

Source code in OmeSliCC\util.py
14
15
16
17
18
19
20
def ensure_list(x) -> list:
    if x is None:
        return []
    elif isinstance(x, list):
        return x
    else:
        return [x]

file_to_dict(filename)

Source code in OmeSliCC\util.py
34
35
36
37
38
39
40
41
42
43
def file_to_dict(filename: str) -> dict:
    ext = os.path.splitext(filename)[1]
    content = open(filename, 'r').read()
    if ext == '.xml':
        data = xmltodict.parse(content)
    elif ext in ['.yml', '.yaml']:
        data = yaml.safe_load(content)
    else:   # assume json
        data = json.loads(content)
    return data

filter_dict(dict0)

Source code in OmeSliCC\util.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def filter_dict(dict0: dict) -> dict:
    new_dict = {}
    for key, value0 in dict0.items():
        if value0 is not None:
            values = []
            for value in ensure_list(value0):
                if isinstance(value, dict):
                    value = filter_dict(value)
                values.append(value)
            if len(values) == 1:
                values = values[0]
            new_dict[key] = values
    return new_dict

get_default(x, default)

Source code in OmeSliCC\util.py
10
11
def get_default(x, default):
    return default if x is None else x

get_filetitle(filename)

Source code in OmeSliCC\util.py
135
136
137
138
def get_filetitle(filename: str) -> str:
    filebase = os.path.basename(filename)
    title = os.path.splitext(filebase)[0].rstrip('.ome')
    return title

get_value_units_micrometer(value_units0)

Source code in OmeSliCC\util.py
198
199
200
201
202
203
204
205
206
207
208
209
210
def get_value_units_micrometer(value_units0: list) -> list:
    conversions = {'nm': 1e-3, 'µm': 1, 'um': 1, 'micrometer': 1, 'mm': 1e3, 'cm': 1e4, 'm': 1e6}
    if value_units0 is None:
        return None

    values_um = []
    for value_unit in value_units0:
        if not (isinstance(value_unit, int) or isinstance(value_unit, float)):
            value_um = value_unit[0] * conversions.get(value_unit[1], 1)
        else:
            value_um = value_unit
        values_um.append(value_um)
    return values_um

print_dict(dct, indent=0)

Source code in OmeSliCC\util.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def print_dict(dct: dict, indent: int = 0) -> str:
    s = ''
    if isinstance(dct, dict):
        for key, value in dct.items():
            s += '\n'
            if not isinstance(value, list):
                s += '\t' * indent + str(key) + ': '
            if isinstance(value, dict):
                s += print_dict(value, indent=indent + 1)
            elif isinstance(value, list):
                for v in value:
                    s += print_dict(v)
            else:
                s += str(value)
    else:
        s += str(dct)
    return s

print_hbytes(nbytes)

Source code in OmeSliCC\util.py
106
107
108
109
110
111
112
113
114
def print_hbytes(nbytes: int) -> str:
    exps = ['', 'K', 'M', 'G', 'T']
    div = 1024
    exp = 0

    while nbytes > div:
        nbytes /= div
        exp += 1
    return f'{nbytes:.1f}{exps[exp]}B'

reorder(items, old_order, new_order, default_value=0)

Source code in OmeSliCC\util.py
23
24
25
26
27
28
29
30
31
def reorder(items: list, old_order: str, new_order: str, default_value: int = 0) -> list:
    new_items = []
    for label in new_order:
        if label in old_order:
            item = items[old_order.index(label)]
        else:
            item = default_value
        new_items.append(item)
    return new_items

round_significants(a, significant_digits)

Source code in OmeSliCC\util.py
128
129
130
131
132
def round_significants(a: float, significant_digits: int) -> float:
    if a != 0:
        round_decimals = significant_digits - int(np.floor(np.log10(abs(a)))) - 1
        return round(a, round_decimals)
    return a

split_num_text(text)

Source code in OmeSliCC\util.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def split_num_text(text: str) -> list:
    num_texts = []
    block = ''
    is_num0 = None
    if text is None:
        return None

    for c in text:
        is_num = (c.isnumeric() or c == '.')
        if is_num0 is not None and is_num != is_num0:
            num_texts.append(block)
            block = ''
        block += c
        is_num0 = is_num
    if block != '':
        num_texts.append(block)

    num_texts2 = []
    for block in num_texts:
        block = block.strip()
        try:
            block = float(block)
        except:
            pass
        if block not in [' ', ',', '|']:
            num_texts2.append(block)
    return num_texts2

split_value_unit_list(text)

Source code in OmeSliCC\util.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def split_value_unit_list(text: str) -> list:
    value_units = []
    if text is None:
        return None

    items = split_num_text(text)
    if isinstance(items[-1], str):
        def_unit = items[-1]
    else:
        def_unit = ''

    i = 0
    while i < len(items):
        value = items[i]
        if i + 1 < len(items):
            unit = items[i + 1]
        else:
            unit = ''
        if not isinstance(value, str):
            if isinstance(unit, str):
                i += 1
            else:
                unit = def_unit
            value_units.append((value, unit))
        i += 1
    return value_units