31
31
32
32
# Standard Imports
33
33
import os
34
+ from multiprocessing .managers import DictProxy
34
35
35
36
# Third Party Imports
36
37
import h5py
41
42
# Local imports
42
43
from .data_source import DataSource
43
44
from ..metadata_sources .bdv_metadata import BigDataViewerMetadata
44
- from multiprocessing . managers import DictProxy
45
+ from ... tools . slicing import ensure_slice , ensure_iter , slice_len
45
46
46
47
47
48
class BigDataViewerDataSource (DataSource ):
48
49
"""BigDataViewer data source.
49
50
50
51
This class is used to write data to a BigDataViewer-compatible file. It
51
- supports both HDF5 and N5 file formats. The file is written in a
52
- multi-resolution pyramid format, with each resolution level subdivided into
53
- 32x32x1 blocks. The number of blocks in each dimension is determined by the
54
- shape of the data and the resolution level.
52
+ supports both HDF5 and N5 file formats.
55
53
"""
56
54
57
55
def __init__ (self , file_name : str = None , mode : str = "w" ) -> None :
@@ -99,7 +97,7 @@ def __getitem__(self, keys):
99
97
"""Magic method to get slice requests passed by, e.g., ds[:,2:3,...].
100
98
Allows arbitrary slicing of dataset via calls to get_slice().
101
99
102
- Order is xycztps where x, y, z are Cartesian indices, c is channel,
100
+ Order is xycztps where x, y, z are array indices, c is channel,
103
101
t is timepoints, p is positions and s is subdivisions to index along.
104
102
105
103
TODO: Add subdivisions.
@@ -120,6 +118,7 @@ def __getitem__(self, keys):
120
118
length = 1
121
119
else :
122
120
length = len (keys )
121
+
123
122
if length < 1 :
124
123
raise IndexError (
125
124
"Too few indices. Indices may be (x, y, c, z, t, p, subdiv)."
@@ -128,84 +127,19 @@ def __getitem__(self, keys):
128
127
raise IndexError (
129
128
"Too many indices. Indices may be (x, y, c, z, t, p, subdiv)."
130
129
)
130
+
131
+ # Get indices as slices/ranges
132
+ xs = ensure_slice (keys , 0 )
133
+ ys = ensure_slice (keys , 1 )
134
+ cs = ensure_iter (keys , 2 , self .shape [2 ])
135
+ zs = ensure_slice (keys , 3 )
136
+ ts = ensure_iter (keys , 4 , self .shape [4 ])
137
+ ps = ensure_iter (keys , 5 , self .positions )
131
138
132
- # Handle "slice the rest"
133
139
if length > 1 and keys [- 1 ] == Ellipsis :
134
- keys = keys [:- 2 ]
140
+ keys = keys [:- 1 ]
135
141
length -= 1
136
142
137
- def ensure_iter (pos ):
138
- """Ensure the input is iterable.
139
-
140
- Parameters
141
- ----------
142
- pos : int
143
- The position.
144
-
145
- Returns
146
- -------
147
- range
148
- The range.
149
- """
150
- if length > pos :
151
- try :
152
- val = keys [pos ]
153
- except TypeError :
154
- # Only one key
155
- val = keys
156
- if isinstance (val , slice ):
157
- if val .start is None and val .stop is None and val .step is None :
158
- return range (self .shape [pos ])
159
- return range (10 ** 10 )[val ]
160
- elif isinstance (val , int ):
161
- return range (val , val + 1 )
162
- else :
163
- return range (self .shape [pos ])
164
-
165
- def ensure_slice (pos ):
166
- """Ensure the input is a slice or a single integer.
167
-
168
- Parameters
169
- ----------
170
- pos : int
171
- The position.
172
-
173
- Returns
174
- -------
175
- slice
176
- The slice.
177
- """
178
- # TODO: Handle list as input
179
- if length > pos :
180
- try :
181
- val = keys [pos ]
182
- except TypeError :
183
- # Only one key
184
- val = keys
185
- assert isinstance (val , slice ) or isinstance (val , int )
186
- return val
187
- else :
188
- # Default to all values
189
- return slice (None , None , None )
190
-
191
- # Get legal indices
192
- xs = ensure_slice (0 )
193
- ys = ensure_slice (1 )
194
- cs = ensure_iter (2 )
195
- zs = ensure_slice (3 )
196
- ts = ensure_iter (4 )
197
- if length > 5 :
198
- val = keys [5 ]
199
- if isinstance (val , slice ):
200
- if val .start is None and val .stop is None and val .step is None :
201
- ps = range (self .positions )
202
- else :
203
- ps = range (10 ** 10 )[val ]
204
- elif isinstance (val , int ):
205
- ps = range (val , val + 1 )
206
- else :
207
- ps = range (self .positions )
208
-
209
143
if length > 6 and isinstance (keys [6 ], int ):
210
144
subdiv = keys [6 ]
211
145
else :
@@ -214,24 +148,6 @@ def ensure_slice(pos):
214
148
if len (cs ) == 1 and len (ts ) == 1 and len (ps ) == 1 :
215
149
return self .get_slice (xs , ys , cs [0 ], zs , ts [0 ], ps [0 ], subdiv )
216
150
217
- def slice_len (sl , n ):
218
- """Calculate the length of the slice over an array of size n.
219
-
220
- Parameters
221
- ----------
222
- sl : slice
223
- The slice.
224
- n : int
225
- The size of the array.
226
-
227
- Returns
228
- -------
229
- int
230
- The length of the slice.
231
- """
232
- sx = sl .indices (n )
233
- return (sx [1 ] - sx [0 ]) // sx [2 ]
234
-
235
151
sliced_ds = np .empty (
236
152
(
237
153
len (ps ),
@@ -254,7 +170,7 @@ def slice_len(sl, n):
254
170
return sliced_ds
255
171
256
172
def get_slice (self , x , y , c , z = 0 , t = 0 , p = 0 , subdiv = 0 ):
257
- """Get a single slice of the dataset.
173
+ """Get a 3D slice of the dataset for a single c, t, p, subdiv .
258
174
259
175
Parameters
260
176
----------
@@ -524,7 +440,7 @@ def _setup_n5(self, *args, create_flag=True):
524
440
"""Set up the N5 file.
525
441
526
442
This function creates the file and the datasets to populate. By default,
527
- it appears to implement blosc compression. Consequently, the anticipated file
443
+ it implements blosc compression. Consequently, the anticipated file
528
444
size, and the actual file size, do not match. This is not the case for HDF5.
529
445
530
446
Note
0 commit comments