visor-py 2024.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- visor_py-2024.12.1/LICENSE +29 -0
- visor_py-2024.12.1/PKG-INFO +129 -0
- visor_py-2024.12.1/README.md +111 -0
- visor_py-2024.12.1/pyproject.toml +28 -0
- visor_py-2024.12.1/setup.cfg +4 -0
- visor_py-2024.12.1/visor/__init__.py +0 -0
- visor_py-2024.12.1/visor/image/__init__.py +2 -0
- visor_py-2024.12.1/visor/image/core.py +305 -0
- visor_py-2024.12.1/visor/image/tests/test_core.py +225 -0
- visor_py-2024.12.1/visor/transform/__init__.py +0 -0
- visor_py-2024.12.1/visor_py.egg-info/PKG-INFO +129 -0
- visor_py-2024.12.1/visor_py.egg-info/SOURCES.txt +13 -0
- visor_py-2024.12.1/visor_py.egg-info/dependency_links.txt +1 -0
- visor_py-2024.12.1/visor_py.egg-info/requires.txt +3 -0
- visor_py-2024.12.1/visor_py.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, VISoR Group
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: visor-py
|
|
3
|
+
Version: 2024.12.1
|
|
4
|
+
Summary: A Python Library for VISoR Image.
|
|
5
|
+
Author-email: Zijian Yi <arrowzeke@gmail.com>
|
|
6
|
+
License: BSD
|
|
7
|
+
Project-URL: repository, https://github.com/visor-tech/visor-py
|
|
8
|
+
Keywords: VISoR
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: dask[complete]==2024.9.1
|
|
16
|
+
Requires-Dist: zarr==2.18.3
|
|
17
|
+
Requires-Dist: numcodecs==0.13.0
|
|
18
|
+
|
|
19
|
+
# visor-py
|
|
20
|
+
A Python Library for VISoR Image.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
#### Install Module
|
|
24
|
+
```sh
|
|
25
|
+
pip install visor-py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
#### Import Module
|
|
29
|
+
```py
|
|
30
|
+
import visor.image as vimg
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### Examples
|
|
34
|
+
- Read .vsr
|
|
35
|
+
```py
|
|
36
|
+
import visor.image as vimg
|
|
37
|
+
|
|
38
|
+
# Open .vsr file with read-only ('r') mode
|
|
39
|
+
# img is a visor.Image object
|
|
40
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
41
|
+
print(img.info)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Read raw image
|
|
45
|
+
```py
|
|
46
|
+
import visor.image as vimg
|
|
47
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
48
|
+
|
|
49
|
+
# Read raw image of slice_1_10x from zarr file
|
|
50
|
+
# arr is a visor.Array object
|
|
51
|
+
arr = img.read(img_type='raw',
|
|
52
|
+
zarr_file='slice_1_10x.zarr',
|
|
53
|
+
resolution=0)
|
|
54
|
+
print(arr.info)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Read arrays by named stacks or channels
|
|
58
|
+
```py
|
|
59
|
+
import visor.image as vimg
|
|
60
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
61
|
+
arr = img.read(img_type='raw',
|
|
62
|
+
zarr_file='slice_1_10x.zarr',
|
|
63
|
+
resolution=0)
|
|
64
|
+
|
|
65
|
+
# Read stack by label
|
|
66
|
+
# s1_array is a dask.array, dimensions: c,z,y,x
|
|
67
|
+
s1_array = arr.read(stack='stack_1')
|
|
68
|
+
print(f's1_array shape: {s1_array.shape}, dimensions: c,z,y,x')
|
|
69
|
+
|
|
70
|
+
# Read channel by wavelength
|
|
71
|
+
# c488_array is a dask.array, dimensions: s,z,y,x
|
|
72
|
+
c488_array = arr.read(channel='488')
|
|
73
|
+
print(f'c488_array shape: {c488_array.shape}, dimensions: s,z,y,x')
|
|
74
|
+
|
|
75
|
+
# Read stack and channel
|
|
76
|
+
# s1c488_array is a dask.array, dimensions: z,y,x
|
|
77
|
+
s1c488_array = arr.read(stack='stack_1', channel='488')
|
|
78
|
+
print(f's1c488_array shape: {s1c488_array.shape}, dimensions: z,y,x')
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- Read array by index
|
|
82
|
+
```py
|
|
83
|
+
import visor.image as vimg
|
|
84
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
85
|
+
arr = img.read(img_type='raw',
|
|
86
|
+
zarr_file='slice_1_10x.zarr',
|
|
87
|
+
resolution=0)
|
|
88
|
+
|
|
89
|
+
# Read by index
|
|
90
|
+
# the_array is a dask.array
|
|
91
|
+
the_array = arr.array
|
|
92
|
+
print(f'the_array shape: {the_array.shape}, dimensions: s,c,z,y,x')
|
|
93
|
+
# subarr is a dask.array
|
|
94
|
+
subarr = the_array[0,0,:,:,:]
|
|
95
|
+
print(f'subarr shape: {subarr.shape}, dimensions: z,y,x')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- Write .vsr
|
|
99
|
+
```py
|
|
100
|
+
import visor.image as vimg
|
|
101
|
+
import numpy as np
|
|
102
|
+
import dask.array as da
|
|
103
|
+
|
|
104
|
+
# Open .vsr file with write ('w') mode
|
|
105
|
+
# img is a visor.Image object
|
|
106
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'w')
|
|
107
|
+
|
|
108
|
+
# Generate a random array
|
|
109
|
+
# new_arr is a dask array
|
|
110
|
+
new_arr = da.random.random(size=(2, 2, 4, 4, 4), chunks=(1, 1, 2, 2, 2))
|
|
111
|
+
|
|
112
|
+
# Metadata
|
|
113
|
+
# follow https://visor-tech.github.io/visor-data-schema/
|
|
114
|
+
img_info = {} # info.json
|
|
115
|
+
arr_info = {} # .zattrs
|
|
116
|
+
selected = {} # selected.json
|
|
117
|
+
|
|
118
|
+
# Write array to .vsr
|
|
119
|
+
img.write(arr=new_arr,
|
|
120
|
+
img_type='raw',
|
|
121
|
+
file='slice_1_10x',
|
|
122
|
+
resolution=0,
|
|
123
|
+
img_info=img_info,
|
|
124
|
+
arr_info=arr_info,
|
|
125
|
+
selected=selected)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## References
|
|
129
|
+
[VISoR Image Schema](https://visor-tech.github.io/visor-data-schema)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# visor-py
|
|
2
|
+
A Python Library for VISoR Image.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
#### Install Module
|
|
6
|
+
```sh
|
|
7
|
+
pip install visor-py
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
#### Import Module
|
|
11
|
+
```py
|
|
12
|
+
import visor.image as vimg
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
#### Examples
|
|
16
|
+
- Read .vsr
|
|
17
|
+
```py
|
|
18
|
+
import visor.image as vimg
|
|
19
|
+
|
|
20
|
+
# Open .vsr file with read-only ('r') mode
|
|
21
|
+
# img is a visor.Image object
|
|
22
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
23
|
+
print(img.info)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- Read raw image
|
|
27
|
+
```py
|
|
28
|
+
import visor.image as vimg
|
|
29
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
30
|
+
|
|
31
|
+
# Read raw image of slice_1_10x from zarr file
|
|
32
|
+
# arr is a visor.Array object
|
|
33
|
+
arr = img.read(img_type='raw',
|
|
34
|
+
zarr_file='slice_1_10x.zarr',
|
|
35
|
+
resolution=0)
|
|
36
|
+
print(arr.info)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Read arrays by named stacks or channels
|
|
40
|
+
```py
|
|
41
|
+
import visor.image as vimg
|
|
42
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
43
|
+
arr = img.read(img_type='raw',
|
|
44
|
+
zarr_file='slice_1_10x.zarr',
|
|
45
|
+
resolution=0)
|
|
46
|
+
|
|
47
|
+
# Read stack by label
|
|
48
|
+
# s1_array is a dask.array, dimensions: c,z,y,x
|
|
49
|
+
s1_array = arr.read(stack='stack_1')
|
|
50
|
+
print(f's1_array shape: {s1_array.shape}, dimensions: c,z,y,x')
|
|
51
|
+
|
|
52
|
+
# Read channel by wavelength
|
|
53
|
+
# c488_array is a dask.array, dimensions: s,z,y,x
|
|
54
|
+
c488_array = arr.read(channel='488')
|
|
55
|
+
print(f'c488_array shape: {c488_array.shape}, dimensions: s,z,y,x')
|
|
56
|
+
|
|
57
|
+
# Read stack and channel
|
|
58
|
+
# s1c488_array is a dask.array, dimensions: z,y,x
|
|
59
|
+
s1c488_array = arr.read(stack='stack_1', channel='488')
|
|
60
|
+
print(f's1c488_array shape: {s1c488_array.shape}, dimensions: z,y,x')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Read array by index
|
|
64
|
+
```py
|
|
65
|
+
import visor.image as vimg
|
|
66
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
67
|
+
arr = img.read(img_type='raw',
|
|
68
|
+
zarr_file='slice_1_10x.zarr',
|
|
69
|
+
resolution=0)
|
|
70
|
+
|
|
71
|
+
# Read by index
|
|
72
|
+
# the_array is a dask.array
|
|
73
|
+
the_array = arr.array
|
|
74
|
+
print(f'the_array shape: {the_array.shape}, dimensions: s,c,z,y,x')
|
|
75
|
+
# subarr is a dask.array
|
|
76
|
+
subarr = the_array[0,0,:,:,:]
|
|
77
|
+
print(f'subarr shape: {subarr.shape}, dimensions: z,y,x')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- Write .vsr
|
|
81
|
+
```py
|
|
82
|
+
import visor.image as vimg
|
|
83
|
+
import numpy as np
|
|
84
|
+
import dask.array as da
|
|
85
|
+
|
|
86
|
+
# Open .vsr file with write ('w') mode
|
|
87
|
+
# img is a visor.Image object
|
|
88
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'w')
|
|
89
|
+
|
|
90
|
+
# Generate a random array
|
|
91
|
+
# new_arr is a dask array
|
|
92
|
+
new_arr = da.random.random(size=(2, 2, 4, 4, 4), chunks=(1, 1, 2, 2, 2))
|
|
93
|
+
|
|
94
|
+
# Metadata
|
|
95
|
+
# follow https://visor-tech.github.io/visor-data-schema/
|
|
96
|
+
img_info = {} # info.json
|
|
97
|
+
arr_info = {} # .zattrs
|
|
98
|
+
selected = {} # selected.json
|
|
99
|
+
|
|
100
|
+
# Write array to .vsr
|
|
101
|
+
img.write(arr=new_arr,
|
|
102
|
+
img_type='raw',
|
|
103
|
+
file='slice_1_10x',
|
|
104
|
+
resolution=0,
|
|
105
|
+
img_info=img_info,
|
|
106
|
+
arr_info=arr_info,
|
|
107
|
+
selected=selected)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## References
|
|
111
|
+
[VISoR Image Schema](https://visor-tech.github.io/visor-data-schema)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "visor-py"
|
|
7
|
+
version = "2024.12.1"
|
|
8
|
+
description = "A Python Library for VISoR Image."
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Zijian Yi", email = "arrowzeke@gmail.com" }
|
|
11
|
+
]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "BSD" }
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: BSD License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.11"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"dask[complete]==2024.9.1",
|
|
22
|
+
"zarr==2.18.3",
|
|
23
|
+
"numcodecs==0.13.0"
|
|
24
|
+
]
|
|
25
|
+
keywords = ["VISoR"]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
repository = "https://github.com/visor-tech/visor-py"
|
|
File without changes
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
import zarr
|
|
5
|
+
import dask.array as da
|
|
6
|
+
from numcodecs import Blosc
|
|
7
|
+
|
|
8
|
+
# Schema Reference: https://visor-tech.github.io/visor-data-schema
|
|
9
|
+
|
|
10
|
+
class Image:
|
|
11
|
+
|
|
12
|
+
def __init__(self, path:Path, mode:str):
|
|
13
|
+
"""
|
|
14
|
+
Constructor of Image
|
|
15
|
+
|
|
16
|
+
Parameters:
|
|
17
|
+
path : the .vsr file path
|
|
18
|
+
mode : 'r' for read_only
|
|
19
|
+
'w' for write
|
|
20
|
+
"""
|
|
21
|
+
# path
|
|
22
|
+
path = Path(path)
|
|
23
|
+
if path.suffix != '.vsr':
|
|
24
|
+
raise ValueError(f'The path {path} does not have .vsr extension.')
|
|
25
|
+
if not path.exists() or not path.is_dir():
|
|
26
|
+
if 'w' == mode:
|
|
27
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
else:
|
|
29
|
+
raise NotADirectoryError(f'The path {path} is not a directory.')
|
|
30
|
+
self.path = path
|
|
31
|
+
self.mode = mode
|
|
32
|
+
|
|
33
|
+
# info
|
|
34
|
+
info_file = path/'info.json'
|
|
35
|
+
if not info_file.exists():
|
|
36
|
+
if 'w' == mode:
|
|
37
|
+
info_file.touch()
|
|
38
|
+
info_file.write_text('{}')
|
|
39
|
+
else:
|
|
40
|
+
raise FileNotFoundError(f'Metadata file info.json is not found in {path}.')
|
|
41
|
+
with open(info_file) as f:
|
|
42
|
+
self.info = json.load(f)
|
|
43
|
+
|
|
44
|
+
# file structure
|
|
45
|
+
self.image_types = self._get_image_types()
|
|
46
|
+
self.transforms = self._get_transforms()
|
|
47
|
+
self.image_files = {}
|
|
48
|
+
for image_type in self.image_types:
|
|
49
|
+
dir = path/f'visor_{image_type}_images'
|
|
50
|
+
if 'raw' == image_type:
|
|
51
|
+
with open(dir/'selected.json') as sf:
|
|
52
|
+
self.image_files[image_type] = json.load(sf)
|
|
53
|
+
else:
|
|
54
|
+
self.image_files[image_type] = [{
|
|
55
|
+
"path": d,
|
|
56
|
+
"channels": self._get_channels(dir/f/'.zattr')
|
|
57
|
+
} for d in dir.iterdir() if d.suffix == '.zarr']
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _get_image_types(self):
|
|
61
|
+
"""
|
|
62
|
+
Private method to get image type list
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of image types
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
return [d.name.split('_')[1] for d in self.path.glob('visor_*_images')]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _get_transforms(self):
|
|
72
|
+
"""
|
|
73
|
+
Private method to get transform list
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of transforms
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
transforms = []
|
|
80
|
+
|
|
81
|
+
transforms_dir = self.path/'visor_recon_transforms'
|
|
82
|
+
if transforms_dir.is_dir():
|
|
83
|
+
transforms = transforms_dir.iterdir()
|
|
84
|
+
|
|
85
|
+
return transforms
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _get_channels(meta_file):
|
|
89
|
+
"""
|
|
90
|
+
Private method to get channel list from metadata file
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
meta_file : the metadata file path
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
List of channel wavelengths
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
with open(meta_file) as mf:
|
|
100
|
+
meta = json.load(mf)
|
|
101
|
+
return [c['wavelength'] for c in meta['channels']]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _get_stacks(meta_file):
|
|
105
|
+
"""
|
|
106
|
+
Private method to get stack list
|
|
107
|
+
|
|
108
|
+
Parameters:
|
|
109
|
+
meta_file : the metadata file path
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
List of stack labels
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
with open(meta_file) as mf:
|
|
116
|
+
meta = json.load(mf)
|
|
117
|
+
return [s['label'] for s in meta['visor_stacks']]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def list(self, img_type=None):
|
|
121
|
+
"""
|
|
122
|
+
List image files
|
|
123
|
+
|
|
124
|
+
Parameters:
|
|
125
|
+
img_type : optional filter of image type
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Dictionary of image files
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
if None == img_type:
|
|
132
|
+
return self.image_files
|
|
133
|
+
elif img_type not in self.image_files:
|
|
134
|
+
return {}
|
|
135
|
+
else:
|
|
136
|
+
return self.image_files[img_type]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def read(self, img_type:str, zarr_file:str, resolution:int):
|
|
140
|
+
"""
|
|
141
|
+
Read Array from Image
|
|
142
|
+
|
|
143
|
+
Parameters:
|
|
144
|
+
img_type : visor image type, e.g. 'raw', 'projn', etc.
|
|
145
|
+
zarr_file : zarr file name with '.zarr' extension
|
|
146
|
+
resolution : resolution level
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Array
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
return Array(path=self.path/f'visor_{img_type}_images'/zarr_file,
|
|
153
|
+
resolution=resolution)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def write(self, arr:da, img_type:str, file:str,
|
|
157
|
+
resolution:int, img_info:dict,
|
|
158
|
+
arr_info:dict, selected:dict=None,
|
|
159
|
+
compression:str='zstd', clevel:int=5):
|
|
160
|
+
"""
|
|
161
|
+
Read Array from Image
|
|
162
|
+
|
|
163
|
+
Parameters:
|
|
164
|
+
arr : dask array to write
|
|
165
|
+
img_type : visor image type, e.g. 'raw', 'projn', etc.
|
|
166
|
+
file : file name without extension
|
|
167
|
+
resolution : resolution level
|
|
168
|
+
img_info : metadata to write to info.json
|
|
169
|
+
arr_info : metadata to write to .zattrs
|
|
170
|
+
selected : optional metadata to write to selected.json
|
|
171
|
+
compression : compression algorithm to use for zarr, default to 'zstd'
|
|
172
|
+
clevel : compression level to use for zarr, default to 5
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
if self.mode != 'w':
|
|
176
|
+
raise PermissionError('"write" method is only available in "w" mode.')
|
|
177
|
+
|
|
178
|
+
zarr_file = self.path/f'visor_{img_type}_images'/f'{file}.zarr'
|
|
179
|
+
component = str(resolution)
|
|
180
|
+
da.to_zarr(arr, zarr_file,
|
|
181
|
+
dimension_separator=os.sep,
|
|
182
|
+
component=component,
|
|
183
|
+
compressor=Blosc(cname=compression, clevel=clevel))
|
|
184
|
+
|
|
185
|
+
img_info_file = self.path/'info.json'
|
|
186
|
+
with open(img_info_file, 'w') as iif:
|
|
187
|
+
json.dump(img_info, iif, indent=2)
|
|
188
|
+
|
|
189
|
+
arr_info_file = zarr_file/'.zattrs'
|
|
190
|
+
with open(arr_info_file, 'w') as aif:
|
|
191
|
+
json.dump(arr_info, aif, indent=2)
|
|
192
|
+
|
|
193
|
+
if selected and 'raw' == img_type:
|
|
194
|
+
selected_file = self.path/'visor_raw_images'
|
|
195
|
+
with open(selected_file, 'w') as sf:
|
|
196
|
+
json.dump(selected, sf, indent=2)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class Array:
|
|
200
|
+
|
|
201
|
+
def __init__(self, path:Path, resolution:int):
|
|
202
|
+
"""
|
|
203
|
+
Constructor of Array
|
|
204
|
+
|
|
205
|
+
Parameters:
|
|
206
|
+
path : the .zarr file path
|
|
207
|
+
resolution : resolution level
|
|
208
|
+
"""
|
|
209
|
+
# path
|
|
210
|
+
if path.suffix != '.zarr':
|
|
211
|
+
raise ValueError(f'{path} is not a zarr file.')
|
|
212
|
+
if not path.is_dir():
|
|
213
|
+
raise NotADirectoryError(f'The path {path} is not a directory.')
|
|
214
|
+
self.path = path
|
|
215
|
+
|
|
216
|
+
# resolution
|
|
217
|
+
self.resolution = resolution
|
|
218
|
+
|
|
219
|
+
# .zarray
|
|
220
|
+
zarray_file = path/str(resolution)/'.zarray'
|
|
221
|
+
if not zarray_file.exists():
|
|
222
|
+
raise FileNotFoundError(f'Must contain a .zarray file in {path} directory.')
|
|
223
|
+
|
|
224
|
+
# info
|
|
225
|
+
meta_file = path/'.zattrs'
|
|
226
|
+
with open(meta_file) as mf:
|
|
227
|
+
self.info = json.load(mf)
|
|
228
|
+
|
|
229
|
+
# dictionares of named dimension
|
|
230
|
+
self.channel_map = {}
|
|
231
|
+
if 'channels' in self.info:
|
|
232
|
+
for channel in self.info['channels']:
|
|
233
|
+
self.channel_map[channel['wavelength']] = channel['index']
|
|
234
|
+
self.stack_map = {}
|
|
235
|
+
if 'visor_stacks' in self.info:
|
|
236
|
+
for stack in self.info['visor_stacks']:
|
|
237
|
+
self.stack_map[stack['label']] = stack['index']
|
|
238
|
+
|
|
239
|
+
# array
|
|
240
|
+
_store = zarr.DirectoryStore(path/str(resolution))
|
|
241
|
+
z_array = zarr.open(_store, mode='r')
|
|
242
|
+
self.array = da.from_array(z_array, chunks=z_array.chunks)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def read(self, channel=None, stack=None):
|
|
246
|
+
"""
|
|
247
|
+
Read a subarray from this array with optional `s` and `c` dimensions.
|
|
248
|
+
|
|
249
|
+
Parameters:
|
|
250
|
+
stack: str or None, the visor_stack label
|
|
251
|
+
Get index from stack_map. If None, take all `s` subarrays.
|
|
252
|
+
channel: str or None, the channel wavelength
|
|
253
|
+
Get index from channel_map. If None, take all `c` subarrays.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
dask.array
|
|
257
|
+
"""
|
|
258
|
+
# Total dimensions in the array
|
|
259
|
+
ndim = self.array.ndim
|
|
260
|
+
|
|
261
|
+
# Define slices for each dimension
|
|
262
|
+
arr_slices = [slice(None)] * ndim
|
|
263
|
+
|
|
264
|
+
# Validate `stack` and `channel` based on array dimensions
|
|
265
|
+
if ndim == 5: # (s, c, z, y, x)
|
|
266
|
+
if (stack is not None) and (stack not in self.stack_map):
|
|
267
|
+
raise KeyError(f'stack {stack} does not exist, valid stacks: {list(self.stack_map.keys())}')
|
|
268
|
+
if (channel is not None) and (channel not in self.channel_map):
|
|
269
|
+
raise KeyError(f'channel {channel} does not exist, valid channels: {list(self.channel_map.keys())}')
|
|
270
|
+
elif ndim == 4: # (c, z, y, x)
|
|
271
|
+
if (channel is not None) and (channel not in self.channel_map):
|
|
272
|
+
raise KeyError(f'channel {channel} does not exist, valid channels: {list(self.channel_map.keys())}')
|
|
273
|
+
elif ndim == 3: # (z, y, x)
|
|
274
|
+
if stack is not None or channel is not None:
|
|
275
|
+
raise IndexError("'s' or 'c' dimensions do not exist in a 3D array")
|
|
276
|
+
|
|
277
|
+
# Map the dimensions dynamically based on `ndim`
|
|
278
|
+
if ndim == 5: # (s, c, z, y, x)
|
|
279
|
+
if stack is not None:
|
|
280
|
+
arr_slices[0] = self.stack_map[stack]
|
|
281
|
+
if channel is not None:
|
|
282
|
+
arr_slices[1] = self.channel_map[channel]
|
|
283
|
+
elif ndim == 4: # (c, z, y, x)
|
|
284
|
+
if channel is not None:
|
|
285
|
+
arr_slices[0] = self.channel_map[channel]
|
|
286
|
+
elif ndim == 3: # (z, y, x)
|
|
287
|
+
pass # No `s` or `c` dimensions to handle
|
|
288
|
+
|
|
289
|
+
# Apply the arr_slices to the array
|
|
290
|
+
return self.array[tuple(arr_slices)]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def open_vsr(path:str, mode:str):
|
|
294
|
+
"""
|
|
295
|
+
Open vsr file, as an Image object
|
|
296
|
+
|
|
297
|
+
Parameters:
|
|
298
|
+
path : the .vsr file path
|
|
299
|
+
mode : 'r' for read_only
|
|
300
|
+
'w' for write
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Image
|
|
304
|
+
"""
|
|
305
|
+
return Image(Path(path), mode)
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import unittest
|
|
3
|
+
import shutil
|
|
4
|
+
|
|
5
|
+
import visor.image as vimg
|
|
6
|
+
|
|
7
|
+
class TestBase(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def setUp(self):
|
|
10
|
+
|
|
11
|
+
# Test Data: validated with https://ome.github.io/ome-ngff-validator
|
|
12
|
+
# simply use ome_zarr command line tool as below:
|
|
13
|
+
# $ ome_zarr view visor/image/tests/data/VISOR001/visor_raw_images/slice_1_10x.zarr
|
|
14
|
+
self.path = Path(__file__).parent/'data'/'VISOR001.vsr'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestCore(TestBase):
|
|
18
|
+
|
|
19
|
+
def test_open_vsr_r(self):
|
|
20
|
+
|
|
21
|
+
img = vimg.open_vsr(self.path, 'r')
|
|
22
|
+
|
|
23
|
+
self.assertIsInstance(img, vimg.core.Image)
|
|
24
|
+
self.assertEqual(img.info['animal_id'], 'VISOR001')
|
|
25
|
+
self.assertEqual(img.info['project_name'], 'VISOR')
|
|
26
|
+
self.assertEqual(img.info['species'], 'Mouse')
|
|
27
|
+
self.assertEqual(img.info['subproject_name'], 'XXX-XXXX-1X7-3X')
|
|
28
|
+
self.assertEqual(img.image_types, ['raw'])
|
|
29
|
+
self.assertEqual(img.transforms, [])
|
|
30
|
+
self.assertEqual(img.image_files, {'raw': [
|
|
31
|
+
{'path':'slice_1_10x.zarr', 'channels':["488","561"]},
|
|
32
|
+
{'path':'slice_1_10x_1.zarr', 'channels':["405","640"]},
|
|
33
|
+
]})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_open_vsr_w(self):
|
|
37
|
+
|
|
38
|
+
img = vimg.open_vsr(self.path, 'w')
|
|
39
|
+
self.assertIsInstance(img, vimg.core.Image)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestImageReadOnly(TestBase):
|
|
43
|
+
|
|
44
|
+
def setUp(self):
|
|
45
|
+
|
|
46
|
+
super().setUp()
|
|
47
|
+
self.img = vimg.open_vsr(self.path, 'r')
|
|
48
|
+
|
|
49
|
+
self.zarr_file = 'slice_1_10x.zarr'
|
|
50
|
+
self.resolution = 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_image_list(self):
|
|
54
|
+
|
|
55
|
+
self.assertEqual(self.img.list(), {'raw': [
|
|
56
|
+
{'path':'slice_1_10x.zarr', 'channels':["488","561"]},
|
|
57
|
+
{'path':'slice_1_10x_1.zarr', 'channels':["405","640"]},
|
|
58
|
+
]})
|
|
59
|
+
self.assertEqual(self.img.list('raw'), [
|
|
60
|
+
{'path':'slice_1_10x.zarr', 'channels':["488","561"]},
|
|
61
|
+
{'path':'slice_1_10x_1.zarr', 'channels':["405","640"]},
|
|
62
|
+
])
|
|
63
|
+
self.assertEqual(self.img.list('projn'), {})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_image_read(self):
|
|
67
|
+
|
|
68
|
+
arr = self.img.read('raw',self.zarr_file,0)
|
|
69
|
+
|
|
70
|
+
self.assertIsInstance(arr, vimg.core.Array)
|
|
71
|
+
self.assertIn('multiscales', arr.info)
|
|
72
|
+
self.assertIn('visor_stacks', arr.info)
|
|
73
|
+
self.assertIn('channels', arr.info)
|
|
74
|
+
self.assertEqual(arr.channel_map, {'488':0, '561':1})
|
|
75
|
+
self.assertEqual(arr.stack_map, {'stack_1':0, 'stack_2':1})
|
|
76
|
+
self.assertEqual(arr.array.shape, (2,2,4,4,4))
|
|
77
|
+
|
|
78
|
+
def test_image_write_exception(self):
|
|
79
|
+
|
|
80
|
+
arr = self.img.read('raw',self.zarr_file,resolution=self.resolution)
|
|
81
|
+
with self.assertRaises(PermissionError) as context:
|
|
82
|
+
self.img.write(arr,'raw',self.zarr_file,0,{},{})
|
|
83
|
+
self.assertEqual(str(context.exception), '"write" method is only available in "w" mode.')
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TestArrayRead(TestBase):
|
|
87
|
+
|
|
88
|
+
def setUp(self):
|
|
89
|
+
|
|
90
|
+
super().setUp()
|
|
91
|
+
img = vimg.open_vsr(self.path, 'w')
|
|
92
|
+
self.arr = img.read('raw','slice_1_10x.zarr',0)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_array_read(self):
|
|
96
|
+
|
|
97
|
+
arr = self.arr.read()
|
|
98
|
+
self.assertEqual(arr.shape, (2,2,4,4,4))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_array_read_channel(self):
|
|
102
|
+
|
|
103
|
+
arr = self.arr.read(channel='488')
|
|
104
|
+
self.assertEqual(arr.shape, (2,4,4,4))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_array_read_stack(self):
|
|
108
|
+
|
|
109
|
+
arr = self.arr.read(stack='stack_1')
|
|
110
|
+
self.assertEqual(arr.shape, (2,4,4,4))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_array_read_channel_and_stack(self):
|
|
114
|
+
|
|
115
|
+
arr = self.arr.read(channel='488',stack='stack_1')
|
|
116
|
+
self.assertEqual(arr.shape, (4,4,4))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_array_read_channel_not_exist(self):
|
|
120
|
+
|
|
121
|
+
with self.assertRaises(KeyError) as context:
|
|
122
|
+
self.arr.read(channel='777')
|
|
123
|
+
self.assertEqual(str(context.exception), '"channel 777 does not exist, valid channels: [\'488\', \'561\']"')
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestImageWrite(TestBase):
|
|
127
|
+
|
|
128
|
+
def setUp(self):
|
|
129
|
+
|
|
130
|
+
super().setUp()
|
|
131
|
+
self.src_img_type = 'raw'
|
|
132
|
+
self.src_img_file = 'slice_1_10x'
|
|
133
|
+
|
|
134
|
+
self.dst_img_type = 'raw'
|
|
135
|
+
self.dst_img_file = 'slice_1_10x'
|
|
136
|
+
|
|
137
|
+
self.resolution = 0
|
|
138
|
+
|
|
139
|
+
src_img = vimg.open_vsr(self.path, 'r')
|
|
140
|
+
self.src_img_info = src_img.info
|
|
141
|
+
self.arr = src_img.read(self.src_img_type,
|
|
142
|
+
f'{self.src_img_file}.zarr',
|
|
143
|
+
self.resolution)
|
|
144
|
+
|
|
145
|
+
self.dst_path = self.path.parent/'tmp.vsr'
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def tearDown(self):
|
|
149
|
+
|
|
150
|
+
if self.dst_path.exists():
|
|
151
|
+
shutil.rmtree(self.dst_path)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_image_write(self):
|
|
155
|
+
|
|
156
|
+
dst_img = vimg.open_vsr(self.dst_path, 'w')
|
|
157
|
+
dst_img.write(self.arr.array,
|
|
158
|
+
self.dst_img_type,
|
|
159
|
+
self.dst_img_file,
|
|
160
|
+
self.resolution,
|
|
161
|
+
self.src_img_info,
|
|
162
|
+
self.arr.info)
|
|
163
|
+
arr = dst_img.read(self.dst_img_type,
|
|
164
|
+
f'{self.dst_img_file}.zarr',
|
|
165
|
+
0)
|
|
166
|
+
self.assertIsInstance(arr, vimg.core.Array)
|
|
167
|
+
self.assertIn('multiscales', arr.info)
|
|
168
|
+
self.assertIn('visor_stacks', arr.info)
|
|
169
|
+
self.assertIn('channels', arr.info)
|
|
170
|
+
self.assertEqual(arr.channel_map, {'488':0, '561':1})
|
|
171
|
+
self.assertEqual(arr.stack_map, {'stack_1':0, 'stack_2':1})
|
|
172
|
+
self.assertEqual(arr.array.shape, (2,2,4,4,4))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# class TestImageUpdate(TestBase):
|
|
176
|
+
|
|
177
|
+
# def setUp(self):
|
|
178
|
+
|
|
179
|
+
# super().setUp()
|
|
180
|
+
# self.src_img_type = 'raw'
|
|
181
|
+
# self.src_img_file = 'slice_1_10x'
|
|
182
|
+
|
|
183
|
+
# self.dst_img_type = 'icorr'
|
|
184
|
+
# self.dst_img_file = 'slice_1_10x'
|
|
185
|
+
|
|
186
|
+
# self.resolution = 0
|
|
187
|
+
|
|
188
|
+
# src_img = vimg.open_vsr(self.path, 'r')
|
|
189
|
+
# self.src_img_info = src_img.info
|
|
190
|
+
# self.arr = src_img.read(self.src_img_type,
|
|
191
|
+
# f'{self.src_img_file}.zarr',
|
|
192
|
+
# self.resolution)
|
|
193
|
+
|
|
194
|
+
# self.dst_path = self.path/'visor_icorr_images'
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# def tearDown(self):
|
|
198
|
+
|
|
199
|
+
# if self.dst_path.exists():
|
|
200
|
+
# shutil.rmtree(self.dst_path)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# def test_image_write(self):
|
|
204
|
+
|
|
205
|
+
# dst_img = vimg.open_vsr(self.dst_path, 'w')
|
|
206
|
+
# dst_img.write(self.arr.array,
|
|
207
|
+
# self.dst_img_type,
|
|
208
|
+
# self.dst_img_file,
|
|
209
|
+
# self.resolution,
|
|
210
|
+
# self.src_img_info,
|
|
211
|
+
# self.arr.info)
|
|
212
|
+
# arr = dst_img.read(self.dst_img_type,
|
|
213
|
+
# f'{self.dst_img_file}.zarr',
|
|
214
|
+
# 0)
|
|
215
|
+
# self.assertIsInstance(arr, vimg.core.Array)
|
|
216
|
+
# self.assertIn('multiscales', arr.info)
|
|
217
|
+
# self.assertIn('visor_stacks', arr.info)
|
|
218
|
+
# self.assertIn('channels', arr.info)
|
|
219
|
+
# self.assertEqual(arr.channel_map, {'488':0, '561':1})
|
|
220
|
+
# self.assertEqual(arr.stack_map, {'stack_1':0, 'stack_2':1})
|
|
221
|
+
# self.assertEqual(arr.array.shape, (2,2,4,4,4))
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == '__main__':
|
|
225
|
+
unittest.main()
|
|
File without changes
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: visor-py
|
|
3
|
+
Version: 2024.12.1
|
|
4
|
+
Summary: A Python Library for VISoR Image.
|
|
5
|
+
Author-email: Zijian Yi <arrowzeke@gmail.com>
|
|
6
|
+
License: BSD
|
|
7
|
+
Project-URL: repository, https://github.com/visor-tech/visor-py
|
|
8
|
+
Keywords: VISoR
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: dask[complete]==2024.9.1
|
|
16
|
+
Requires-Dist: zarr==2.18.3
|
|
17
|
+
Requires-Dist: numcodecs==0.13.0
|
|
18
|
+
|
|
19
|
+
# visor-py
|
|
20
|
+
A Python Library for VISoR Image.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
#### Install Module
|
|
24
|
+
```sh
|
|
25
|
+
pip install visor-py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
#### Import Module
|
|
29
|
+
```py
|
|
30
|
+
import visor.image as vimg
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### Examples
|
|
34
|
+
- Read .vsr
|
|
35
|
+
```py
|
|
36
|
+
import visor.image as vimg
|
|
37
|
+
|
|
38
|
+
# Open .vsr file with read-only ('r') mode
|
|
39
|
+
# img is a visor.Image object
|
|
40
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
41
|
+
print(img.info)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Read raw image
|
|
45
|
+
```py
|
|
46
|
+
import visor.image as vimg
|
|
47
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
48
|
+
|
|
49
|
+
# Read raw image of slice_1_10x from zarr file
|
|
50
|
+
# arr is a visor.Array object
|
|
51
|
+
arr = img.read(img_type='raw',
|
|
52
|
+
zarr_file='slice_1_10x.zarr',
|
|
53
|
+
resolution=0)
|
|
54
|
+
print(arr.info)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Read arrays by named stacks or channels
|
|
58
|
+
```py
|
|
59
|
+
import visor.image as vimg
|
|
60
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
61
|
+
arr = img.read(img_type='raw',
|
|
62
|
+
zarr_file='slice_1_10x.zarr',
|
|
63
|
+
resolution=0)
|
|
64
|
+
|
|
65
|
+
# Read stack by label
|
|
66
|
+
# s1_array is a dask.array, dimensions: c,z,y,x
|
|
67
|
+
s1_array = arr.read(stack='stack_1')
|
|
68
|
+
print(f's1_array shape: {s1_array.shape}, dimensions: c,z,y,x')
|
|
69
|
+
|
|
70
|
+
# Read channel by wavelength
|
|
71
|
+
# c488_array is a dask.array, dimensions: s,z,y,x
|
|
72
|
+
c488_array = arr.read(channel='488')
|
|
73
|
+
print(f'c488_array shape: {c488_array.shape}, dimensions: s,z,y,x')
|
|
74
|
+
|
|
75
|
+
# Read stack and channel
|
|
76
|
+
# s1c488_array is a dask.array, dimensions: z,y,x
|
|
77
|
+
s1c488_array = arr.read(stack='stack_1', channel='488')
|
|
78
|
+
print(f's1c488_array shape: {s1c488_array.shape}, dimensions: z,y,x')
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- Read array by index
|
|
82
|
+
```py
|
|
83
|
+
import visor.image as vimg
|
|
84
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'r')
|
|
85
|
+
arr = img.read(img_type='raw',
|
|
86
|
+
zarr_file='slice_1_10x.zarr',
|
|
87
|
+
resolution=0)
|
|
88
|
+
|
|
89
|
+
# Read by index
|
|
90
|
+
# the_array is a dask.array
|
|
91
|
+
the_array = arr.array
|
|
92
|
+
print(f'the_array shape: {the_array.shape}, dimensions: s,c,z,y,x')
|
|
93
|
+
# subarr is a dask.array
|
|
94
|
+
subarr = the_array[0,0,:,:,:]
|
|
95
|
+
print(f'subarr shape: {subarr.shape}, dimensions: z,y,x')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- Write .vsr
|
|
99
|
+
```py
|
|
100
|
+
import visor.image as vimg
|
|
101
|
+
import numpy as np
|
|
102
|
+
import dask.array as da
|
|
103
|
+
|
|
104
|
+
# Open .vsr file with write ('w') mode
|
|
105
|
+
# img is a visor.Image object
|
|
106
|
+
img = vimg.open_vsr('path/to/VISOR001.vsr', 'w')
|
|
107
|
+
|
|
108
|
+
# Generate a random array
|
|
109
|
+
# new_arr is a dask array
|
|
110
|
+
new_arr = da.random.random(size=(2, 2, 4, 4, 4), chunks=(1, 1, 2, 2, 2))
|
|
111
|
+
|
|
112
|
+
# Metadata
|
|
113
|
+
# follow https://visor-tech.github.io/visor-data-schema/
|
|
114
|
+
img_info = {} # info.json
|
|
115
|
+
arr_info = {} # .zattrs
|
|
116
|
+
selected = {} # selected.json
|
|
117
|
+
|
|
118
|
+
# Write array to .vsr
|
|
119
|
+
img.write(arr=new_arr,
|
|
120
|
+
img_type='raw',
|
|
121
|
+
file='slice_1_10x',
|
|
122
|
+
resolution=0,
|
|
123
|
+
img_info=img_info,
|
|
124
|
+
arr_info=arr_info,
|
|
125
|
+
selected=selected)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## References
|
|
129
|
+
[VISoR Image Schema](https://visor-tech.github.io/visor-data-schema)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
visor/__init__.py
|
|
5
|
+
visor/image/__init__.py
|
|
6
|
+
visor/image/core.py
|
|
7
|
+
visor/image/tests/test_core.py
|
|
8
|
+
visor/transform/__init__.py
|
|
9
|
+
visor_py.egg-info/PKG-INFO
|
|
10
|
+
visor_py.egg-info/SOURCES.txt
|
|
11
|
+
visor_py.egg-info/dependency_links.txt
|
|
12
|
+
visor_py.egg-info/requires.txt
|
|
13
|
+
visor_py.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
visor
|