ome-arrow 0.0.5__py3-none-any.whl → 0.0.6__py3-none-any.whl

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.
ome_arrow/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.5'
32
- __version_tuple__ = version_tuple = (0, 0, 5)
31
+ __version__ = version = '0.0.6'
32
+ __version_tuple__ = version_tuple = (0, 0, 6)
33
33
 
34
34
  __commit_id__ = commit_id = None
ome_arrow/core.py CHANGED
@@ -59,6 +59,7 @@ class OMEArrow:
59
59
  tcz: Tuple[int, int, int] = (0, 0, 0),
60
60
  column_name: str = "ome_arrow",
61
61
  row_index: int = 0,
62
+ image_type: str | None = None,
62
63
  ) -> None:
63
64
  """
64
65
  Construct an OMEArrow from:
@@ -71,6 +72,7 @@ class OMEArrow:
71
72
  with from_numpy defaults)
72
73
  - a dict already matching the OME-Arrow schema
73
74
  - a pa.StructScalar already typed to OME_ARROW_STRUCT
75
+ - optionally override/set image_type metadata on ingest
74
76
  """
75
77
 
76
78
  # set the tcz for viewing
@@ -83,6 +85,7 @@ class OMEArrow:
83
85
  default_dim_for_unspecified="C",
84
86
  map_series_to="T",
85
87
  clamp_to_uint16=True,
88
+ image_type=image_type,
86
89
  )
87
90
 
88
91
  # --- 2) String path/URL: OME-Zarr / OME-Parquet / OME-TIFF ---------------
@@ -98,6 +101,8 @@ class OMEArrow:
98
101
  or (path.exists() and path.is_dir() and path.suffix.lower() == ".zarr")
99
102
  ):
100
103
  self.data = from_ome_zarr(s)
104
+ if image_type is not None:
105
+ self.data = self._wrap_with_image_type(self.data, image_type)
101
106
 
102
107
  # OME-Parquet
103
108
  elif s.lower().endswith((".parquet", ".pq")) or path.suffix.lower() in {
@@ -107,18 +112,24 @@ class OMEArrow:
107
112
  self.data = from_ome_parquet(
108
113
  s, column_name=column_name, row_index=row_index
109
114
  )
115
+ if image_type is not None:
116
+ self.data = self._wrap_with_image_type(self.data, image_type)
110
117
 
111
118
  # Vortex
112
119
  elif s.lower().endswith(".vortex") or path.suffix.lower() == ".vortex":
113
120
  self.data = from_ome_vortex(
114
121
  s, column_name=column_name, row_index=row_index
115
122
  )
123
+ if image_type is not None:
124
+ self.data = self._wrap_with_image_type(self.data, image_type)
116
125
 
117
126
  # TIFF
118
127
  elif path.suffix.lower() in {".tif", ".tiff"} or s.lower().endswith(
119
128
  (".tif", ".tiff")
120
129
  ):
121
130
  self.data = from_tiff(s)
131
+ if image_type is not None:
132
+ self.data = self._wrap_with_image_type(self.data, image_type)
122
133
 
123
134
  elif path.exists() and path.is_dir():
124
135
  raise ValueError(
@@ -140,15 +151,20 @@ class OMEArrow:
140
151
  # Uses from_numpy defaults: dim_order="TCZYX", clamp_to_uint16=True, etc.
141
152
  # If the array is YX/ZYX/CYX/etc.,
142
153
  # from_numpy will expand/reorder accordingly.
143
- self.data = from_numpy(data)
154
+ self.data = from_numpy(data, image_type=image_type)
144
155
 
145
156
  # --- 4) Already-typed Arrow scalar ---------------------------------------
146
157
  elif isinstance(data, pa.StructScalar):
147
158
  self.data = data
159
+ if image_type is not None:
160
+ self.data = self._wrap_with_image_type(self.data, image_type)
148
161
 
149
162
  # --- 5) Plain dict matching the schema -----------------------------------
150
163
  elif isinstance(data, dict):
151
- self.data = pa.scalar(data, type=OME_ARROW_STRUCT)
164
+ record = {f.name: data.get(f.name) for f in OME_ARROW_STRUCT}
165
+ self.data = pa.scalar(record, type=OME_ARROW_STRUCT)
166
+ if image_type is not None:
167
+ self.data = self._wrap_with_image_type(self.data, image_type)
152
168
 
153
169
  # --- otherwise ------------------------------------------------------------
154
170
  else:
@@ -156,6 +172,18 @@ class OMEArrow:
156
172
  "input data must be str, dict, pa.StructScalar, or numpy.ndarray"
157
173
  )
158
174
 
175
+ @staticmethod
176
+ def _wrap_with_image_type(
177
+ data: pa.StructScalar, image_type: str
178
+ ) -> pa.StructScalar:
179
+ return pa.scalar(
180
+ {
181
+ **data.as_py(),
182
+ "image_type": str(image_type),
183
+ },
184
+ type=OME_ARROW_STRUCT,
185
+ )
186
+
159
187
  def export( # noqa: PLR0911
160
188
  self,
161
189
  how: str = "numpy",
@@ -212,7 +240,8 @@ class OMEArrow:
212
240
  compression / compression_level / tile:
213
241
  OME-TIFF options (passed through to tifffile via BioIO).
214
242
  chunks / zarr_compressor / zarr_level :
215
- OME-Zarr options (chunk shape, compressor hint, level).
243
+ OME-Zarr options (chunk shape, compressor hint, level). If chunks is
244
+ None, a TCZYX default is chosen (1,1,<=4,<=512,<=512).
216
245
  use_channel_colors:
217
246
  Try to embed per-channel display colors when safe; otherwise omitted.
218
247
  parquet_*:
ome_arrow/export.py CHANGED
@@ -21,7 +21,8 @@ def to_numpy(
21
21
  Convert an OME-Arrow record into a NumPy array shaped (T,C,Z,Y,X).
22
22
 
23
23
  The OME-Arrow "planes" are flattened YX slices indexed by (z, t, c).
24
- This function reconstitutes them into a dense TCZYX ndarray.
24
+ When chunks are present, this function reconstitutes the dense TCZYX array
25
+ from chunked pixels instead of planes.
25
26
 
26
27
  Args:
27
28
  data:
@@ -58,7 +59,7 @@ def to_numpy(
58
59
  if sx <= 0 or sy <= 0 or sz <= 0 or sc <= 0 or st <= 0:
59
60
  raise ValueError("All size_* fields must be positive integers.")
60
61
 
61
- expected_len = sx * sy
62
+ expected_plane_len = sx * sy
62
63
 
63
64
  # Prepare target array (T,C,Z,Y,X), zero-filled by default.
64
65
  out = np.zeros((st, sc, sz, sy, sx), dtype=dtype)
@@ -78,6 +79,70 @@ def to_numpy(
78
79
  a = np.clip(a, lo, hi)
79
80
  return a.astype(dtype, copy=False)
80
81
 
82
+ chunks = data.get("chunks") or []
83
+ if chunks:
84
+ chunk_grid = data.get("chunk_grid") or {}
85
+ chunk_order = str(chunk_grid.get("chunk_order") or "ZYX").upper()
86
+ if chunk_order != "ZYX":
87
+ raise ValueError("Only chunk_order='ZYX' is supported for now.")
88
+
89
+ for i, ch in enumerate(chunks):
90
+ # Chunk coordinates include time/channel plus spatial indices.
91
+ t = int(ch["t"])
92
+ c = int(ch["c"])
93
+ z = int(ch["z"])
94
+ y = int(ch["y"])
95
+ x = int(ch["x"])
96
+ # Chunk shape is only spatial (Z, Y, X).
97
+ shape_z = int(ch["shape_z"])
98
+ shape_y = int(ch["shape_y"])
99
+ shape_x = int(ch["shape_x"])
100
+
101
+ # Validate chunk indices and extents within the full 5D array.
102
+ if not (0 <= t < st and 0 <= c < sc and 0 <= z < sz):
103
+ raise ValueError(
104
+ f"chunks[{i}] index out of range: (t,c,z)=({t},{c},{z})"
105
+ )
106
+ if y < 0 or x < 0 or shape_z <= 0 or shape_y <= 0 or shape_x <= 0:
107
+ raise ValueError(f"chunks[{i}] has invalid shape or origin.")
108
+ if z + shape_z > sz:
109
+ raise ValueError(
110
+ f"chunks[{i}] extent out of range: z+shape_z={z + shape_z} "
111
+ f"> sz={sz}"
112
+ )
113
+ if y + shape_y > sy:
114
+ raise ValueError(
115
+ f"chunks[{i}] extent out of range: y+shape_y={y + shape_y} "
116
+ f"> sy={sy}"
117
+ )
118
+ if x + shape_x > sx:
119
+ raise ValueError(
120
+ f"chunks[{i}] extent out of range: x+shape_x={x + shape_x} "
121
+ f"> sx={sx}"
122
+ )
123
+
124
+ pix = ch["pixels"]
125
+ try:
126
+ n = len(pix)
127
+ except Exception as e:
128
+ raise ValueError(f"chunks[{i}].pixels is not a sequence") from e
129
+
130
+ expected_len = shape_z * shape_y * shape_x
131
+ if n != expected_len:
132
+ if strict:
133
+ raise ValueError(
134
+ f"chunks[{i}].pixels length {n} != expected {expected_len}"
135
+ )
136
+ if n > expected_len:
137
+ pix = pix[:expected_len]
138
+ else:
139
+ pix = list(pix) + [0] * (expected_len - n)
140
+
141
+ arr3d = np.asarray(pix).reshape(shape_z, shape_y, shape_x)
142
+ arr3d = _cast_plane(arr3d)
143
+ out[t, c, z : z + shape_z, y : y + shape_y, x : x + shape_x] = arr3d
144
+ return out
145
+
81
146
  # Fill planes.
82
147
  for i, p in enumerate(data.get("planes", [])):
83
148
  z = int(p["z"])
@@ -94,16 +159,17 @@ def to_numpy(
94
159
  except Exception as e:
95
160
  raise ValueError(f"planes[{i}].pixels is not a sequence") from e
96
161
 
97
- if n != expected_len:
162
+ if n != expected_plane_len:
98
163
  if strict:
99
164
  raise ValueError(
100
- f"planes[{i}].pixels length {n} != size_x*size_y {expected_len}"
165
+ f"planes[{i}].pixels length {n} != size_x*size_y "
166
+ f"{expected_plane_len}"
101
167
  )
102
168
  # Lenient mode: fix length by truncation or zero-pad.
103
- if n > expected_len:
104
- pix = pix[:expected_len]
169
+ if n > expected_plane_len:
170
+ pix = pix[:expected_plane_len]
105
171
  else:
106
- pix = list(pix) + [0] * (expected_len - n)
172
+ pix = list(pix) + [0] * (expected_plane_len - n)
107
173
 
108
174
  # Reshape to (Y,X) and cast.
109
175
  arr2d = np.asarray(pix).reshape(sy, sx)
@@ -113,6 +179,162 @@ def to_numpy(
113
179
  return out
114
180
 
115
181
 
182
+ # Note: x/y are implicit because this returns the full XY plane for (t, c, z).
183
+ def plane_from_chunks(
184
+ data: Dict[str, Any] | pa.StructScalar,
185
+ *,
186
+ t: int,
187
+ c: int,
188
+ z: int,
189
+ dtype: np.dtype = np.uint16,
190
+ strict: bool = True,
191
+ clamp: bool = False,
192
+ ) -> np.ndarray:
193
+ """Extract a single (t, c, z) plane using chunked pixels when available.
194
+
195
+ Args:
196
+ data: OME-Arrow data as a Python dict or a `pa.StructScalar`.
197
+ t: Time index for the plane.
198
+ c: Channel index for the plane.
199
+ z: Z index for the plane.
200
+ dtype: Output dtype (default: np.uint16).
201
+ strict: When True, raise if chunk pixels are malformed.
202
+ clamp: If True, clamp values to the valid range of the target dtype.
203
+
204
+ Returns:
205
+ np.ndarray: 2D array with shape (Y, X).
206
+
207
+ Raises:
208
+ KeyError: If required OME-Arrow fields are missing.
209
+ ValueError: If indices are out of range or pixels are malformed.
210
+ """
211
+ # The plane spans full X/Y for the given (t, c, z); x/y are implicit.
212
+ if isinstance(data, pa.StructScalar):
213
+ data = data.as_py()
214
+
215
+ # Read pixel metadata and validate requested plane indices.
216
+ pm = data["pixels_meta"]
217
+ sx, sy = int(pm["size_x"]), int(pm["size_y"])
218
+ sz, sc, st = int(pm["size_z"]), int(pm["size_c"]), int(pm["size_t"])
219
+ if not (0 <= t < st and 0 <= c < sc and 0 <= z < sz):
220
+ raise ValueError(f"Requested plane (t={t}, c={c}, z={z}) out of range.")
221
+
222
+ # Prepare dtype conversion (optional clamping for integer outputs).
223
+ if np.issubdtype(dtype, np.integer):
224
+ info = np.iinfo(dtype)
225
+ lo, hi = info.min, info.max
226
+ elif np.issubdtype(dtype, np.floating):
227
+ lo, hi = -np.inf, np.inf
228
+ else:
229
+ lo, hi = -np.inf, np.inf
230
+
231
+ def _cast_plane(a: np.ndarray) -> np.ndarray:
232
+ if clamp:
233
+ a = np.clip(a, lo, hi)
234
+ return a.astype(dtype, copy=False)
235
+
236
+ # Prefer chunked pixels if present, assembling the requested Z plane.
237
+ chunks = data.get("chunks") or []
238
+ if chunks:
239
+ chunk_grid = data.get("chunk_grid") or {}
240
+ chunk_order = str(chunk_grid.get("chunk_order") or "ZYX").upper()
241
+ if chunk_order != "ZYX":
242
+ raise ValueError("Only chunk_order='ZYX' is supported for now.")
243
+
244
+ # Allocate an empty XY plane; fill in tiles from matching chunks.
245
+ plane = np.zeros((sy, sx), dtype=dtype)
246
+ any_chunk_matched = False
247
+ for i, ch in enumerate(chunks):
248
+ # Skip chunks from other (t, c) positions.
249
+ if int(ch["t"]) != t or int(ch["c"]) != c:
250
+ continue
251
+ z0 = int(ch["z"])
252
+ szc = int(ch["shape_z"])
253
+ # Skip chunks whose Z slab does not cover the target plane.
254
+ if not (z0 <= z < z0 + szc):
255
+ continue
256
+ y0 = int(ch["y"])
257
+ x0 = int(ch["x"])
258
+ syc = int(ch["shape_y"])
259
+ sxc = int(ch["shape_x"])
260
+ # Validate chunk bounds (strict mode can fail fast).
261
+ if z0 < 0 or y0 < 0 or x0 < 0:
262
+ msg = f"chunks[{i}] has negative origin: (z,y,x)=({z0},{y0},{x0})"
263
+ if strict:
264
+ raise ValueError(msg)
265
+ continue
266
+ if z0 + szc > sz:
267
+ msg = f"chunks[{i}] extent out of range: z+shape_z={z0 + szc} > sz={sz}"
268
+ if strict:
269
+ raise ValueError(msg)
270
+ continue
271
+ if y0 + syc > sy:
272
+ msg = f"chunks[{i}] extent out of range: y+shape_y={y0 + syc} > sy={sy}"
273
+ if strict:
274
+ raise ValueError(msg)
275
+ continue
276
+ if x0 + sxc > sx:
277
+ msg = f"chunks[{i}] extent out of range: x+shape_x={x0 + sxc} > sx={sx}"
278
+ if strict:
279
+ raise ValueError(msg)
280
+ continue
281
+ pix = ch["pixels"]
282
+ try:
283
+ n = len(pix)
284
+ except Exception as e:
285
+ raise ValueError(f"chunks[{i}].pixels is not a sequence") from e
286
+ expected_len = szc * syc * sxc
287
+ if n != expected_len:
288
+ if strict:
289
+ raise ValueError(
290
+ f"chunks[{i}].pixels length {n} != expected {expected_len}"
291
+ )
292
+ # Lenient mode: truncate or zero-pad to match the expected size.
293
+ if n > expected_len:
294
+ pix = pix[:expected_len]
295
+ else:
296
+ pix = list(pix) + [0] * (expected_len - n)
297
+
298
+ # Convert to a Z/Y/X slab and copy the requested Z slice into the plane.
299
+ slab = np.asarray(pix).reshape(szc, syc, sxc)
300
+ slab = _cast_plane(slab)
301
+ zi = z - z0
302
+ plane[y0 : y0 + syc, x0 : x0 + sxc] = slab[zi]
303
+ any_chunk_matched = True
304
+
305
+ if any_chunk_matched:
306
+ return plane
307
+
308
+ # Fallback to planes list if chunks are absent.
309
+ target = next(
310
+ (
311
+ p
312
+ for p in data.get("planes", [])
313
+ if int(p["t"]) == t and int(p["c"]) == c and int(p["z"]) == z
314
+ ),
315
+ None,
316
+ )
317
+ if target is None:
318
+ raise ValueError(f"plane (t={t}, c={c}, z={z}) not found")
319
+
320
+ pix = target["pixels"]
321
+ try:
322
+ n = len(pix)
323
+ except Exception as e:
324
+ raise ValueError("plane pixels is not a sequence") from e
325
+ expected_len = sx * sy
326
+ if n != expected_len:
327
+ if strict:
328
+ raise ValueError(f"plane pixels length {n} != size_x*size_y {expected_len}")
329
+ if n > expected_len:
330
+ pix = pix[:expected_len]
331
+ else:
332
+ pix = list(pix) + [0] * (expected_len - n)
333
+
334
+ arr2d = np.asarray(pix).reshape(sy, sx)
335
+ return _cast_plane(arr2d)
336
+
337
+
116
338
  def to_ome_tiff(
117
339
  data: Dict[str, Any] | pa.StructScalar,
118
340
  out_path: str,
@@ -255,6 +477,7 @@ def to_ome_zarr(
255
477
  - Creates level shapes for a multiscale pyramid (if multiscale_levels>1).
256
478
  - Chooses Blosc codec compatible with zarr_format (v2 vs v3).
257
479
  - Populates axes names/types/units and physical pixel sizes from pixels_meta.
480
+ - Uses default TCZYX chunks if none are provided.
258
481
  """
259
482
  # --- local import to avoid hard deps at module import time
260
483
  # Use the class you showed
@@ -317,6 +540,15 @@ def to_ome_zarr(
317
540
  def _down(a: int, f: int) -> int:
318
541
  return max(1, a // f)
319
542
 
543
+ def _default_chunks_tcxyz(
544
+ shape: Tuple[int, int, int, int, int],
545
+ ) -> Tuple[int, int, int, int, int]:
546
+ _t, _c, z, y, x = shape
547
+ cz = min(z, 4) if z > 1 else 1
548
+ cy = min(y, 512)
549
+ cx = min(x, 512)
550
+ return (1, 1, cz, cy, cx)
551
+
320
552
  def _level_shapes_tcxyz(levels: int) -> List[Tuple[int, int, int, int, int]]:
321
553
  shapes = [(st, sc, sz, sy, sx)]
322
554
  for _ in range(levels - 1):
@@ -340,6 +572,8 @@ def to_ome_zarr(
340
572
  # 5) Chunking / shards (can be single-shape or per-level;
341
573
  # we pass single-shape if provided)
342
574
  chunk_shape: Optional[List[Tuple[int, ...]]] = None
575
+ if chunks is None:
576
+ chunks = _default_chunks_tcxyz((st, sc, sz, sy, sx))
343
577
  if chunks is not None:
344
578
  chunk_shape = [tuple(int(v) for v in chunks)] * multiscale_levels
345
579
 
@@ -393,7 +627,8 @@ def to_ome_parquet(
393
627
  record_dict = data.as_py()
394
628
  else:
395
629
  # Validate by round-tripping through a typed scalar, then back to dict.
396
- record_dict = pa.scalar(data, type=OME_ARROW_STRUCT).as_py()
630
+ record_dict = {f.name: data.get(f.name) for f in OME_ARROW_STRUCT}
631
+ record_dict = pa.scalar(record_dict, type=OME_ARROW_STRUCT).as_py()
397
632
 
398
633
  # 2) Build a single-row struct array from the dict, explicitly passing the schema
399
634
  struct_array = pa.array([record_dict], type=OME_ARROW_STRUCT) # len=1
@@ -456,7 +691,8 @@ def to_ome_vortex(
456
691
  record_dict = data.as_py()
457
692
  else:
458
693
  # Validate by round-tripping through a typed scalar, then back to dict.
459
- record_dict = pa.scalar(data, type=OME_ARROW_STRUCT).as_py()
694
+ record_dict = {f.name: data.get(f.name) for f in OME_ARROW_STRUCT}
695
+ record_dict = pa.scalar(record_dict, type=OME_ARROW_STRUCT).as_py()
460
696
 
461
697
  # 2) Build a single-row struct array from the dict, explicitly passing the schema
462
698
  struct_array = pa.array([record_dict], type=OME_ARROW_STRUCT) # len=1
ome_arrow/ingest.py CHANGED
@@ -50,8 +50,9 @@ def _ome_arrow_from_table(
50
50
  # 1) Locate the OME-Arrow column
51
51
  def _struct_matches_ome_fields(t: pa.StructType) -> bool:
52
52
  ome_fields = {f.name for f in OME_ARROW_STRUCT}
53
+ required_fields = ome_fields - {"image_type", "chunk_grid", "chunks"}
53
54
  col_fields = {f.name for f in t}
54
- return ome_fields == col_fields
55
+ return required_fields.issubset(col_fields)
55
56
 
56
57
  requested_name = column_name
57
58
  candidate_col = None
@@ -105,6 +106,11 @@ def _ome_arrow_from_table(
105
106
 
106
107
  # 2) Extract the row as a Python dict
107
108
  record_dict: Dict[str, Any] = candidate_col.slice(row_index, 1).to_pylist()[0]
109
+ # Back-compat: older files won't include image_type; default to None.
110
+ if "image_type" not in record_dict:
111
+ record_dict["image_type"] = None
112
+ # Drop unexpected fields before casting to the canonical schema.
113
+ record_dict = {f.name: record_dict.get(f.name) for f in OME_ARROW_STRUCT}
108
114
 
109
115
  # 3) Reconstruct a typed StructScalar using the canonical schema
110
116
  scalar = pa.scalar(record_dict, type=OME_ARROW_STRUCT)
@@ -243,11 +249,123 @@ def _read_ngff_scale(zarr_path: Path) -> tuple[float, float, float, str | None]
243
249
  return psize_x, psize_y, psize_z, unit
244
250
 
245
251
 
252
+ def _normalize_chunk_shape(
253
+ chunk_shape: Optional[Tuple[int, int, int]],
254
+ size_z: int,
255
+ size_y: int,
256
+ size_x: int,
257
+ ) -> Tuple[int, int, int]:
258
+ """Normalize a chunk shape against image bounds.
259
+
260
+ Args:
261
+ chunk_shape: Desired chunk shape as (Z, Y, X), or None.
262
+ size_z: Total Z size of the image.
263
+ size_y: Total Y size of the image.
264
+ size_x: Total X size of the image.
265
+
266
+ Returns:
267
+ Tuple[int, int, int]: Normalized (Z, Y, X) chunk shape.
268
+ """
269
+ if chunk_shape is None:
270
+ chunk_shape = (1, 512, 512)
271
+ if not isinstance(chunk_shape, (list, tuple)) or len(chunk_shape) != 3:
272
+ raise ValueError("chunk_shape must be a sequence of three integers (z,y,x)")
273
+ try:
274
+ cz_raw, cy_raw, cx_raw = (int(v) for v in chunk_shape)
275
+ except Exception as exc:
276
+ raise ValueError(
277
+ "chunk_shape must be a sequence of three integers (z,y,x)"
278
+ ) from exc
279
+ if cz_raw <= 0 or cy_raw <= 0 or cx_raw <= 0:
280
+ raise ValueError("chunk_shape values must be positive integers")
281
+ cz = max(1, min(cz_raw, int(size_z)))
282
+ cy = max(1, min(cy_raw, int(size_y)))
283
+ cx = max(1, min(cx_raw, int(size_x)))
284
+ return cz, cy, cx
285
+
286
+
287
+ def _build_chunks_from_planes(
288
+ *,
289
+ planes: List[Dict[str, Any]],
290
+ size_t: int,
291
+ size_c: int,
292
+ size_z: int,
293
+ size_y: int,
294
+ size_x: int,
295
+ chunk_shape: Optional[Tuple[int, int, int]],
296
+ chunk_order: str = "ZYX",
297
+ ) -> List[Dict[str, Any]]:
298
+ """Build chunked pixels from a list of flattened planes.
299
+
300
+ Args:
301
+ planes: List of plane dicts with keys z, t, c, and pixels.
302
+ size_t: Total T size of the image.
303
+ size_c: Total C size of the image.
304
+ size_z: Total Z size of the image.
305
+ size_y: Total Y size of the image.
306
+ size_x: Total X size of the image.
307
+ chunk_shape: Desired chunk shape as (Z, Y, X).
308
+ chunk_order: Flattening order for chunk pixels (default "ZYX").
309
+
310
+ Returns:
311
+ List[Dict[str, Any]]: Chunk list with pixels stored as flat lists.
312
+
313
+ Raises:
314
+ ValueError: If an unsupported chunk_order is requested.
315
+ """
316
+ if str(chunk_order).upper() != "ZYX":
317
+ raise ValueError("Only chunk_order='ZYX' is supported for now.")
318
+
319
+ cz, cy, cx = _normalize_chunk_shape(chunk_shape, size_z, size_y, size_x)
320
+
321
+ plane_map: Dict[Tuple[int, int, int], np.ndarray] = {}
322
+ for p in planes:
323
+ z = int(p["z"])
324
+ t = int(p["t"])
325
+ c = int(p["c"])
326
+ pix = p["pixels"]
327
+ arr2d = np.asarray(pix).reshape(size_y, size_x)
328
+ plane_map[(t, c, z)] = arr2d
329
+
330
+ dtype = next(iter(plane_map.values())).dtype if plane_map else np.uint16
331
+
332
+ chunks: List[Dict[str, Any]] = []
333
+ for t in range(size_t):
334
+ for c in range(size_c):
335
+ for z0 in range(0, size_z, cz):
336
+ sz = min(cz, size_z - z0)
337
+ for y0 in range(0, size_y, cy):
338
+ sy = min(cy, size_y - y0)
339
+ for x0 in range(0, size_x, cx):
340
+ sx = min(cx, size_x - x0)
341
+ slab = np.zeros((sz, sy, sx), dtype=dtype)
342
+ for zi in range(sz):
343
+ plane = plane_map.get((t, c, z0 + zi))
344
+ if plane is None:
345
+ continue
346
+ slab[zi] = plane[y0 : y0 + sy, x0 : x0 + sx]
347
+ chunks.append(
348
+ {
349
+ "t": t,
350
+ "c": c,
351
+ "z": z0,
352
+ "y": y0,
353
+ "x": x0,
354
+ "shape_z": sz,
355
+ "shape_y": sy,
356
+ "shape_x": sx,
357
+ "pixels": slab.reshape(-1).tolist(),
358
+ }
359
+ )
360
+ return chunks
361
+
362
+
246
363
  def to_ome_arrow(
247
364
  type_: str = OME_ARROW_TAG_TYPE,
248
365
  version: str = OME_ARROW_TAG_VERSION,
249
366
  image_id: str = "unnamed",
250
367
  name: str = "unknown",
368
+ image_type: str | None = "image",
251
369
  acquisition_datetime: Optional[datetime] = None,
252
370
  dimension_order: str = "XYZCT",
253
371
  dtype: str = "uint16",
@@ -262,6 +380,10 @@ def to_ome_arrow(
262
380
  physical_size_unit: str = "µm",
263
381
  channels: Optional[List[Dict[str, Any]]] = None,
264
382
  planes: Optional[List[Dict[str, Any]]] = None,
383
+ chunks: Optional[List[Dict[str, Any]]] = None,
384
+ chunk_shape: Optional[Tuple[int, int, int]] = (1, 512, 512), # (Z, Y, X)
385
+ chunk_order: str = "ZYX",
386
+ build_chunks: bool = True,
265
387
  masks: Any = None,
266
388
  ) -> pa.StructScalar:
267
389
  """
@@ -276,6 +398,9 @@ def to_ome_arrow(
276
398
  version: Specification version string.
277
399
  image_id: Unique image identifier.
278
400
  name: Human-friendly name.
401
+ image_type: Open-ended image kind (e.g., "image", "label"). Note that
402
+ from_* helpers pass image_type=None by default to preserve
403
+ "unspecified" vs explicitly set ("image").
279
404
  acquisition_datetime: Datetime of acquisition (defaults to now).
280
405
  dimension_order: Dimension order ("XYZCT" or "XYCT").
281
406
  dtype: Pixel data type string (e.g., "uint16").
@@ -284,6 +409,12 @@ def to_ome_arrow(
284
409
  physical_size_unit: Unit string, default "µm".
285
410
  channels: List of channel dicts. Autogenerates one if None.
286
411
  planes: List of plane dicts. Empty if None.
412
+ chunks: Optional list of chunk dicts. If None and build_chunks is True,
413
+ chunks are derived from planes using chunk_shape.
414
+ chunk_shape: Chunk shape as (Z, Y, X). Defaults to (1, 512, 512).
415
+ chunk_order: Flattening order for chunk pixels (default "ZYX").
416
+ build_chunks: If True, build chunked pixels from planes when chunks
417
+ is None.
287
418
  masks: Optional placeholder for future annotations.
288
419
 
289
420
  Returns:
@@ -299,6 +430,7 @@ def to_ome_arrow(
299
430
  version = str(version)
300
431
  image_id = str(image_id)
301
432
  name = str(name)
433
+ image_type = None if image_type is None else str(image_type)
302
434
  dimension_order = str(dimension_order)
303
435
  dtype = str(dtype)
304
436
  physical_size_unit = str(physical_size_unit)
@@ -328,11 +460,62 @@ def to_ome_arrow(
328
460
  if planes is None:
329
461
  planes = [{"z": 0, "t": 0, "c": 0, "pixels": [0] * (size_x * size_y)}]
330
462
 
463
+ if chunks is None and build_chunks:
464
+ chunks = _build_chunks_from_planes(
465
+ planes=planes,
466
+ size_t=size_t,
467
+ size_c=size_c,
468
+ size_z=size_z,
469
+ size_y=size_y,
470
+ size_x=size_x,
471
+ chunk_shape=chunk_shape,
472
+ chunk_order=chunk_order,
473
+ )
474
+
475
+ chunk_grid = None
476
+ if chunks is not None:
477
+ chunk_order = str(chunk_order).upper()
478
+ if chunk_order != "ZYX":
479
+ raise ValueError("Only chunk_order='ZYX' is supported for now.")
480
+ if len(chunks) == 0:
481
+ raise ValueError("chunks must not be an empty list")
482
+ first = chunks[0]
483
+ try:
484
+ derived_shape = (
485
+ int(first["shape_z"]),
486
+ int(first["shape_y"]),
487
+ int(first["shape_x"]),
488
+ )
489
+ except Exception as exc:
490
+ raise ValueError(
491
+ "chunks entries must include shape_z/shape_y/shape_x"
492
+ ) from exc
493
+ if derived_shape[0] <= 0 or derived_shape[1] <= 0 or derived_shape[2] <= 0:
494
+ raise ValueError("chunk shapes must be positive integers")
495
+ if chunk_shape is not None:
496
+ norm_shape = _normalize_chunk_shape(chunk_shape, size_z, size_y, size_x)
497
+ if norm_shape != derived_shape:
498
+ raise ValueError(
499
+ "chunk_shape does not match provided chunks "
500
+ f"(chunk_shape={norm_shape}, chunks_shape={derived_shape})"
501
+ )
502
+ cz, cy, cx = _normalize_chunk_shape(derived_shape, size_z, size_y, size_x)
503
+ chunk_grid = {
504
+ "order": "TCZYX",
505
+ "chunk_t": 1,
506
+ "chunk_c": 1,
507
+ "chunk_z": cz,
508
+ "chunk_y": cy,
509
+ "chunk_x": cx,
510
+ "chunk_order": str(chunk_order),
511
+ }
512
+
331
513
  record = {
332
514
  "type": type_,
333
515
  "version": version,
334
516
  "id": image_id,
335
517
  "name": name,
518
+ "image_type": image_type,
336
519
  "acquisition_datetime": acquisition_datetime or datetime.now(timezone.utc),
337
520
  "pixels_meta": {
338
521
  "dimension_order": dimension_order,
@@ -350,6 +533,8 @@ def to_ome_arrow(
350
533
  "physical_size_z_unit": physical_size_unit,
351
534
  "channels": channels,
352
535
  },
536
+ "chunk_grid": chunk_grid,
537
+ "chunks": chunks,
353
538
  "planes": planes,
354
539
  "masks": masks,
355
540
  }
@@ -363,9 +548,13 @@ def from_numpy(
363
548
  dim_order: str = "TCZYX",
364
549
  image_id: Optional[str] = None,
365
550
  name: Optional[str] = None,
551
+ image_type: Optional[str] = None,
366
552
  channel_names: Optional[Sequence[str]] = None,
367
553
  acquisition_datetime: Optional[datetime] = None,
368
554
  clamp_to_uint16: bool = True,
555
+ chunk_shape: Optional[Tuple[int, int, int]] = (1, 512, 512),
556
+ chunk_order: str = "ZYX",
557
+ build_chunks: bool = True,
369
558
  # meta
370
559
  physical_size_x: float = 1.0,
371
560
  physical_size_y: float = 1.0,
@@ -373,42 +562,39 @@ def from_numpy(
373
562
  physical_size_unit: str = "µm",
374
563
  dtype_meta: Optional[str] = None, # if None, inferred from output dtype
375
564
  ) -> pa.StructScalar:
376
- """
377
- Build an OME-Arrow StructScalar from a NumPy array.
378
-
379
- Parameters
380
- ----------
381
- arr : np.ndarray
382
- Image data with axes described by `dim_order`.
383
- dim_order : str, default "TCZYX"
384
- Axis labels for `arr`. Must include "Y" and "X".
385
- Supported examples: "YX", "ZYX", "CYX", "CZYX", "TYX", "TCYX", "TCZYX".
386
- image_id, name : Optional[str]
387
- Identifiers to embed in the record.
388
- channel_names : Optional[Sequence[str]]
389
- Names for channels; defaults to C0..C{n-1}.
390
- acquisition_datetime : Optional[datetime]
391
- Defaults to now (UTC) if None.
392
- clamp_to_uint16 : bool, default True
393
- If True, clamp/cast planes to uint16 before serialization.
394
- physical_size_x/y/z : float
395
- Spatial pixel sizes (µm), Z used if present.
396
- physical_size_unit : str
397
- Unit string for spatial axes (default "µm").
398
- dtype_meta : Optional[str]
399
- Pixel dtype string to place in metadata; if None, inferred from the
400
- (possibly cast) array's dtype.
401
-
402
- Returns
403
- -------
404
- pa.StructScalar
405
- Typed OME-Arrow record (schema = OME_ARROW_STRUCT).
406
-
407
- Notes
408
- -----
409
- - If Z is not in `dim_order`, `size_z` will be 1 and the meta
410
- dimension_order becomes "XYCT"; otherwise "XYZCT".
411
- - If T/C are absent in `dim_order`, they default to size 1.
565
+ """Build an OME-Arrow StructScalar from a NumPy array.
566
+
567
+ Args:
568
+ arr: Image data with axes described by `dim_order`.
569
+ dim_order: Axis labels for `arr`. Must include "Y" and "X".
570
+ Supported examples: "YX", "ZYX", "CYX", "CZYX", "TYX", "TCYX", "TCZYX".
571
+ image_id: Optional stable image identifier.
572
+ name: Optional human label.
573
+ image_type: Open-ended image kind (e.g., "image", "label").
574
+ channel_names: Names for channels; defaults to C0..C{n-1}.
575
+ acquisition_datetime: Defaults to now (UTC) if None.
576
+ clamp_to_uint16: If True, clamp/cast planes to uint16 before serialization.
577
+ chunk_shape: Chunk shape as (Z, Y, X). Defaults to (1, 512, 512).
578
+ chunk_order: Flattening order for chunk pixels (default "ZYX").
579
+ build_chunks: If True, build chunked pixels from planes.
580
+ physical_size_x: Spatial pixel size (µm) for X.
581
+ physical_size_y: Spatial pixel size (µm) for Y.
582
+ physical_size_z: Spatial pixel size (µm) for Z when present.
583
+ physical_size_unit: Unit string for spatial axes (default "µm").
584
+ dtype_meta: Pixel dtype string to place in metadata; if None, inferred
585
+ from the (possibly cast) array's dtype.
586
+
587
+ Returns:
588
+ pa.StructScalar: Typed OME-Arrow record (schema = OME_ARROW_STRUCT).
589
+
590
+ Raises:
591
+ TypeError: If `arr` is not a NumPy ndarray.
592
+ ValueError: If `dim_order` is invalid or dimensions are non-positive.
593
+
594
+ Notes:
595
+ - If Z is not in `dim_order`, `size_z` will be 1 and the meta
596
+ dimension_order becomes "XYCT"; otherwise "XYZCT".
597
+ - If T/C are absent in `dim_order`, they default to size 1.
412
598
  """
413
599
 
414
600
  if not isinstance(arr, np.ndarray):
@@ -496,6 +682,7 @@ def from_numpy(
496
682
  return to_ome_arrow(
497
683
  image_id=str(image_id or "unnamed"),
498
684
  name=str(name or "unknown"),
685
+ image_type=image_type,
499
686
  acquisition_datetime=acquisition_datetime or datetime.now(timezone.utc),
500
687
  dimension_order=meta_dim_order,
501
688
  dtype=dtype_str,
@@ -510,6 +697,9 @@ def from_numpy(
510
697
  physical_size_unit=str(physical_size_unit),
511
698
  channels=channels,
512
699
  planes=planes,
700
+ chunk_shape=chunk_shape,
701
+ chunk_order=chunk_order,
702
+ build_chunks=build_chunks,
513
703
  masks=None,
514
704
  )
515
705
 
@@ -518,6 +708,7 @@ def from_tiff(
518
708
  tiff_path: str | Path,
519
709
  image_id: Optional[str] = None,
520
710
  name: Optional[str] = None,
711
+ image_type: Optional[str] = None,
521
712
  channel_names: Optional[Sequence[str]] = None,
522
713
  acquisition_datetime: Optional[datetime] = None,
523
714
  clamp_to_uint16: bool = True,
@@ -532,6 +723,7 @@ def from_tiff(
532
723
  tiff_path: Path to a TIFF readable by bioio.
533
724
  image_id: Optional stable image identifier (defaults to stem).
534
725
  name: Optional human label (defaults to file name).
726
+ image_type: Optional image kind (e.g., "image", "label").
535
727
  channel_names: Optional channel names; defaults to C0..C{n-1}.
536
728
  acquisition_datetime: Optional acquisition time (UTC now if None).
537
729
  clamp_to_uint16: If True, clamp/cast planes to uint16.
@@ -601,6 +793,7 @@ def from_tiff(
601
793
  return to_ome_arrow(
602
794
  image_id=img_id,
603
795
  name=display_name,
796
+ image_type=image_type,
604
797
  acquisition_datetime=acquisition_datetime or datetime.now(timezone.utc),
605
798
  dimension_order=dim_order,
606
799
  dtype="uint16",
@@ -627,6 +820,7 @@ def from_stack_pattern_path(
627
820
  channel_names: Optional[List[str]] = None,
628
821
  image_id: Optional[str] = None,
629
822
  name: Optional[str] = None,
823
+ image_type: Optional[str] = None,
630
824
  ) -> pa.StructScalar:
631
825
  """Build an OME-Arrow record from a filename pattern describing a stack.
632
826
 
@@ -638,6 +832,7 @@ def from_stack_pattern_path(
638
832
  channel_names: Optional list of channel names to apply.
639
833
  image_id: Optional image identifier override.
640
834
  name: Optional display name override.
835
+ image_type: Optional image kind (e.g., "image", "label").
641
836
 
642
837
  Returns:
643
838
  A validated OME-Arrow StructScalar describing the stack.
@@ -907,6 +1102,7 @@ def from_stack_pattern_path(
907
1102
  return to_ome_arrow(
908
1103
  image_id=str(img_id),
909
1104
  name=str(display_name),
1105
+ image_type=image_type,
910
1106
  acquisition_datetime=None,
911
1107
  dimension_order=dim_order,
912
1108
  dtype="uint16",
@@ -929,6 +1125,7 @@ def from_ome_zarr(
929
1125
  zarr_path: str | Path,
930
1126
  image_id: Optional[str] = None,
931
1127
  name: Optional[str] = None,
1128
+ image_type: Optional[str] = None,
932
1129
  channel_names: Optional[Sequence[str]] = None,
933
1130
  acquisition_datetime: Optional[datetime] = None,
934
1131
  clamp_to_uint16: bool = True,
@@ -947,6 +1144,8 @@ def from_ome_zarr(
947
1144
  Optional stable image identifier (defaults to directory stem).
948
1145
  name:
949
1146
  Optional display name (defaults to directory name).
1147
+ image_type:
1148
+ Optional image kind (e.g., "image", "label").
950
1149
  channel_names:
951
1150
  Optional list of channel names. Defaults to C0, C1, ...
952
1151
  acquisition_datetime:
@@ -1028,6 +1227,7 @@ def from_ome_zarr(
1028
1227
  return to_ome_arrow(
1029
1228
  image_id=img_id,
1030
1229
  name=display_name,
1230
+ image_type=image_type,
1031
1231
  acquisition_datetime=acquisition_datetime or datetime.now(timezone.utc),
1032
1232
  dimension_order=dim_order,
1033
1233
  dtype="uint16",
ome_arrow/meta.py CHANGED
@@ -12,8 +12,10 @@ OME_ARROW_TAG_VERSION = ome_arrow_version
12
12
  # OME_ARROW_STRUCT: ome-arrow record (describes one image/value).
13
13
  # - type/version: quick identity & evolution.
14
14
  # - id/name/acquisition_datetime: identity & provenance.
15
+ # - image_type: open-ended image kind (e.g., "image", "label").
15
16
  # - pixels_meta: pixels struct (sizes, units, channels).
16
17
  # - planes: list of planes struct entries, one per (t,c,z).
18
+ # - chunk_grid/chunks: optional chunked pixels (TCZYX-aware), stored as Arrow lists.
17
19
  # - masks: reserved for future labels/ROIs (placeholder).
18
20
  OME_ARROW_STRUCT: pa.StructType = pa.struct(
19
21
  [
@@ -21,6 +23,7 @@ OME_ARROW_STRUCT: pa.StructType = pa.struct(
21
23
  pa.field("version", pa.string()), # e.g., "1.0.0"
22
24
  pa.field("id", pa.string()), # stable image identifier
23
25
  pa.field("name", pa.string()), # human label
26
+ pa.field("image_type", pa.string()), # open-ended (e.g., "image", "label")
24
27
  pa.field("acquisition_datetime", pa.timestamp("us")),
25
28
  # PIXELS: OME-like "Pixels" header summarizing shape & scale.
26
29
  # - dimension_order: hint like "XYZCT" (or "XYCT" when Z==1).
@@ -68,6 +71,44 @@ OME_ARROW_STRUCT: pa.StructType = pa.struct(
68
71
  ]
69
72
  ),
70
73
  ),
74
+ # CHUNK GRID: optional chunking metadata for random access.
75
+ # - order: axis order for the full array, e.g., "TCZYX".
76
+ # - chunk_*: chunk sizes for each axis (defaults to 1 for T/C).
77
+ # - chunk_order: order used to flatten chunk pixels (default "ZYX").
78
+ pa.field(
79
+ "chunk_grid",
80
+ pa.struct(
81
+ [
82
+ pa.field("order", pa.string()),
83
+ pa.field("chunk_t", pa.int32()),
84
+ pa.field("chunk_c", pa.int16()),
85
+ pa.field("chunk_z", pa.int32()),
86
+ pa.field("chunk_y", pa.int32()),
87
+ pa.field("chunk_x", pa.int32()),
88
+ pa.field("chunk_order", pa.string()),
89
+ ]
90
+ ),
91
+ ),
92
+ # CHUNKS: list of chunk entries (Arrow-native, no binary payloads).
93
+ # - pixels flattened in chunk_order (default "ZYX").
94
+ pa.field(
95
+ "chunks",
96
+ pa.list_(
97
+ pa.struct(
98
+ [
99
+ pa.field("t", pa.int32()),
100
+ pa.field("c", pa.int16()),
101
+ pa.field("z", pa.int32()),
102
+ pa.field("y", pa.int32()),
103
+ pa.field("x", pa.int32()),
104
+ pa.field("shape_z", pa.int32()),
105
+ pa.field("shape_y", pa.int32()),
106
+ pa.field("shape_x", pa.int32()),
107
+ pa.field("pixels", pa.list_(pa.uint16())),
108
+ ]
109
+ )
110
+ ),
111
+ ),
71
112
  # PLANES: one 2D image plane for a specific (t, c, z).
72
113
  # - pixels: flattened numeric list (Y*X) for analysis-ready computation.
73
114
  pa.field(
ome_arrow/transform.py CHANGED
@@ -8,6 +8,7 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple
8
8
  import numpy as np
9
9
  import pyarrow as pa
10
10
 
11
+ from ome_arrow.ingest import _build_chunks_from_planes, _normalize_chunk_shape
11
12
  from ome_arrow.meta import OME_ARROW_STRUCT
12
13
 
13
14
 
@@ -179,4 +180,37 @@ def slice_ome_arrow(
179
180
  rec_out["pixels_meta"] = pm_out
180
181
  rec_out["planes"] = planes_out
181
182
 
183
+ chunk_grid_in = row.get("chunk_grid") or {}
184
+ if chunk_grid_in or row.get("chunks"):
185
+ chunk_shape = (
186
+ int(chunk_grid_in.get("chunk_z", 1)),
187
+ int(chunk_grid_in.get("chunk_y", 512)),
188
+ int(chunk_grid_in.get("chunk_x", 512)),
189
+ )
190
+ chunk_order = str(chunk_grid_in.get("chunk_order") or "ZYX")
191
+ chunks_out = _build_chunks_from_planes(
192
+ planes=planes_out,
193
+ size_t=new_st,
194
+ size_c=new_sc,
195
+ size_z=new_sz,
196
+ size_y=new_sy,
197
+ size_x=new_sx,
198
+ chunk_shape=chunk_shape,
199
+ chunk_order=chunk_order,
200
+ )
201
+ cz, cy, cx = _normalize_chunk_shape(chunk_shape, new_sz, new_sy, new_sx)
202
+ rec_out["chunk_grid"] = {
203
+ "order": "TCZYX",
204
+ "chunk_t": 1,
205
+ "chunk_c": 1,
206
+ "chunk_z": cz,
207
+ "chunk_y": cy,
208
+ "chunk_x": cx,
209
+ "chunk_order": chunk_order,
210
+ }
211
+ rec_out["chunks"] = chunks_out
212
+ else:
213
+ rec_out["chunk_grid"] = row.get("chunk_grid")
214
+ rec_out["chunks"] = row.get("chunks")
215
+
182
216
  return pa.scalar(rec_out, type=OME_ARROW_STRUCT)
ome_arrow/view.py CHANGED
@@ -23,6 +23,8 @@ except ImportError: # pragma: no cover - exercised when viz extra missing
23
23
  if TYPE_CHECKING:
24
24
  import pyvista
25
25
 
26
+ from ome_arrow.export import plane_from_chunks
27
+
26
28
 
27
29
  def view_matplotlib(
28
30
  data: dict[str, object] | pa.StructScalar,
@@ -50,29 +52,8 @@ def view_matplotlib(
50
52
  Raises:
51
53
  ValueError: If the requested plane is missing or pixel sizes mismatch.
52
54
  """
53
- if isinstance(data, pa.StructScalar):
54
- data = data.as_py()
55
-
56
- pm = data["pixels_meta"]
57
- sx, sy = int(pm["size_x"]), int(pm["size_y"])
58
55
  t, c, z = (int(x) for x in tcz)
59
-
60
- plane = next(
61
- (
62
- p
63
- for p in data["planes"]
64
- if int(p["t"]) == t and int(p["c"]) == c and int(p["z"]) == z
65
- ),
66
- None,
67
- )
68
- if plane is None:
69
- raise ValueError(f"plane (t={t}, c={c}, z={z}) not found")
70
-
71
- pix = plane["pixels"]
72
- if len(pix) != sx * sy:
73
- raise ValueError(f"pixels len {len(pix)} != size_x*size_y ({sx * sy})")
74
-
75
- img = np.asarray(pix, dtype=np.uint16).reshape(sy, sx).copy()
56
+ img = plane_from_chunks(data, t=t, c=c, z=z, dtype=np.uint16).copy()
76
57
 
77
58
  if (vmin is None or vmax is None) and autoscale:
78
59
  lo, hi = int(img.min()), int(img.max())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ome-arrow
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Using OME specifications with Apache Arrow for fast, queryable, and language agnostic bioimage data.
5
5
  Author: Dave Bunten
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -0,0 +1,14 @@
1
+ ome_arrow/__init__.py,sha256=WWenJP9XxLZNGQPVOEFBDlDM1kSvj_QdHssrET6UuNQ,644
2
+ ome_arrow/_version.py,sha256=7MyqQ3iPP2mJruPfRYGCNCq1z7_Nk7c-eyYecYITxsY,704
3
+ ome_arrow/core.py,sha256=K1-jfojhqYkn3h-0AdmBQYFza0wtFqduJs6K7aw4Kuc,21303
4
+ ome_arrow/export.py,sha256=XbKVIeMLbPshgo_OGO3e0PRqtZ3vEx_JAGyF9oUmZJw,26024
5
+ ome_arrow/ingest.py,sha256=CptjVXXL8YzfHKlKqyoA-ani5SLbFlQSUVPqOXlPhXA,46931
6
+ ome_arrow/meta.py,sha256=peIx6NLriaFpGBx9Y3NyTHLfGAg91v9YQpoBzrveKFQ,6031
7
+ ome_arrow/transform.py,sha256=X3gKZkgTDNQSBcU3_YmJRav8JIBrDdYJci3PbFZ4t38,7200
8
+ ome_arrow/utils.py,sha256=XHovcqmjqoiBpKvXY47-_yUwf07f8zVE_F9BR_VKaPU,2383
9
+ ome_arrow/view.py,sha256=j9dpmSnVbiukwH6asFhCJ_WVRxwyCAdTeLiUnv_xvcE,10187
10
+ ome_arrow-0.0.6.dist-info/licenses/LICENSE,sha256=9-2Pyhu3vTt2RJU8DorHQtHeNO_e5RLeFJTyOU4hOi4,1508
11
+ ome_arrow-0.0.6.dist-info/METADATA,sha256=VWirz2ueYrPLOrsNj1EC2BGcsPtLpJU6Jay_NW_irZ8,6110
12
+ ome_arrow-0.0.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
+ ome_arrow-0.0.6.dist-info/top_level.txt,sha256=aWOtkGXo_pfU-yy82guzGhz8Zh2h2nFl8Kc5qdzMGuE,10
14
+ ome_arrow-0.0.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- ome_arrow/__init__.py,sha256=WWenJP9XxLZNGQPVOEFBDlDM1kSvj_QdHssrET6UuNQ,644
2
- ome_arrow/_version.py,sha256=YRV1ohn6CdKEhsUOmFFMmr5UTjMv4Ydw3WJGxF2BHBs,704
3
- ome_arrow/core.py,sha256=fgEFOwckYi3asosEUhGB8UL9Q93hO56H6qw9fUczFO8,19946
4
- ome_arrow/export.py,sha256=e9Nx25bD2K51gQng-4rUXM4v1l8-K1YkxGjWKImFrJ4,16972
5
- ome_arrow/ingest.py,sha256=Vt9hljI718vR-qpJXH4jk4Shs1OtFPfIVhmsILkbNxQ,38714
6
- ome_arrow/meta.py,sha256=qeD0e_ItAQyZDT7ypkBU0rBh9oHIu2ziz9MCfPpPp9g,4199
7
- ome_arrow/transform.py,sha256=0275_Mn1mlGXSWJ86llch8JoJyvqEOfvG-ub1dUWFNI,5997
8
- ome_arrow/utils.py,sha256=XHovcqmjqoiBpKvXY47-_yUwf07f8zVE_F9BR_VKaPU,2383
9
- ome_arrow/view.py,sha256=B2ZEE8LWlYzTBk0Fa19GHC1seEN_IdgOkfmJXcLRG2U,10691
10
- ome_arrow-0.0.5.dist-info/licenses/LICENSE,sha256=9-2Pyhu3vTt2RJU8DorHQtHeNO_e5RLeFJTyOU4hOi4,1508
11
- ome_arrow-0.0.5.dist-info/METADATA,sha256=l_CqAdgv7NFsNT48eDaC3s2uvKpg9g2FDgA3TAtricI,6110
12
- ome_arrow-0.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- ome_arrow-0.0.5.dist-info/top_level.txt,sha256=aWOtkGXo_pfU-yy82guzGhz8Zh2h2nFl8Kc5qdzMGuE,10
14
- ome_arrow-0.0.5.dist-info/RECORD,,