napari-ome-arrow 0.0.2__py3-none-any.whl → 0.0.3__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.
@@ -11,6 +11,7 @@ Behavior:
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import math
14
15
  import os
15
16
  import warnings
16
17
  from collections.abc import Sequence
@@ -18,7 +19,9 @@ from pathlib import Path
18
19
  from typing import Any, Union
19
20
 
20
21
  import numpy as np
22
+ import pyarrow as pa
21
23
  from ome_arrow.core import OMEArrow
24
+ from ome_arrow.meta import OME_ARROW_STRUCT
22
25
 
23
26
  PathLike = Union[str, Path]
24
27
  LayerData = tuple[np.ndarray, dict[str, Any], str]
@@ -170,6 +173,124 @@ def _looks_like_ome_source(path_str: str) -> bool:
170
173
  )
171
174
 
172
175
 
176
+ # --------------------------------------------------------------------- #
177
+ # OME-Parquet helpers (multi-row grid)
178
+ # --------------------------------------------------------------------- #
179
+
180
+
181
+ def _find_ome_parquet_columns(table: pa.Table) -> list[str]:
182
+ """Return struct columns matching the OME-Arrow schema."""
183
+ import pyarrow as pa
184
+
185
+ expected_fields = {f.name for f in OME_ARROW_STRUCT}
186
+ names: list[str] = []
187
+ for name, col in zip(table.column_names, table.columns, strict=False):
188
+ if (
189
+ pa.types.is_struct(col.type)
190
+ and {f.name for f in col.type} == expected_fields
191
+ ):
192
+ names.append(name)
193
+ return names
194
+
195
+
196
+ def _enable_grid(n_layers: int) -> None:
197
+ """Switch current viewer into grid view when possible."""
198
+ if n_layers <= 1:
199
+ return
200
+ try:
201
+ import napari
202
+
203
+ viewer = napari.current_viewer()
204
+ except Exception:
205
+ return
206
+ if viewer is None:
207
+ return
208
+
209
+ cols = math.ceil(math.sqrt(n_layers))
210
+ rows = math.ceil(n_layers / cols)
211
+ try:
212
+ viewer.grid.enabled = True
213
+ viewer.grid.shape = (rows, cols)
214
+ except Exception:
215
+ # grid is best-effort; ignore if unavailable
216
+ return
217
+
218
+
219
+ def _read_parquet_rows(src: str, mode: str) -> list[LayerData] | None:
220
+ """
221
+ Specialized path for multi-row OME-Parquet:
222
+ create one layer per row and enable napari grid view.
223
+ """
224
+ s = src.lower()
225
+ if not (s.endswith((".ome.parquet", ".parquet", ".pq"))):
226
+ return None
227
+
228
+ try:
229
+ import pyarrow as pa
230
+ import pyarrow.parquet as pq
231
+ except Exception:
232
+ return None
233
+
234
+ table = pq.read_table(src)
235
+ ome_cols = _find_ome_parquet_columns(table)
236
+ if not ome_cols or table.num_rows <= 1:
237
+ return None
238
+
239
+ override = os.environ.get("NAPARI_OME_ARROW_PARQUET_COLUMN")
240
+ if override:
241
+ matched = [c for c in ome_cols if c.lower() == override.lower()]
242
+ selected = matched[0] if matched else ome_cols[0]
243
+ if not matched:
244
+ warnings.warn(
245
+ f"Column '{override}' not found in {Path(src).name}; using {selected!r}.",
246
+ stacklevel=2,
247
+ )
248
+ else:
249
+ selected = ome_cols[0]
250
+
251
+ column = table[selected]
252
+ layers: list[LayerData] = []
253
+
254
+ for idx in range(table.num_rows):
255
+ try:
256
+ record = column.slice(idx, 1).to_pylist()[0]
257
+ scalar = pa.scalar(record, type=OME_ARROW_STRUCT)
258
+ arr = OMEArrow(scalar).export(
259
+ how="numpy", dtype=np.uint16, strict=False
260
+ )
261
+ except Exception as e: # pragma: no cover - warn and skip bad rows
262
+ warnings.warn(
263
+ f"Skipping row {idx} in column '{selected}': {e}",
264
+ stacklevel=2,
265
+ )
266
+ continue
267
+
268
+ add_kwargs: dict[str, Any] = {
269
+ "name": f"{Path(src).name}[{selected}][row {idx}]"
270
+ }
271
+ if mode == "image":
272
+ if arr.ndim >= 5:
273
+ add_kwargs["channel_axis"] = 1 # TCZYX
274
+ elif arr.ndim == 4:
275
+ add_kwargs["channel_axis"] = 0 # CZYX
276
+ layer_type = "image"
277
+ else:
278
+ if arr.ndim == 5:
279
+ arr = arr[:, 0, ...]
280
+ elif arr.ndim == 4:
281
+ arr = arr[0, ...]
282
+ arr = _as_labels(arr)
283
+ add_kwargs.setdefault("opacity", 0.7)
284
+ layer_type = "labels"
285
+
286
+ _maybe_set_viewer_3d(arr)
287
+ layers.append((arr, add_kwargs, layer_type))
288
+
289
+ if layers:
290
+ _enable_grid(len(layers))
291
+ return layers or None
292
+
293
+
173
294
  # --------------------------------------------------------------------- #
174
295
  # napari entry point: napari_get_reader
175
296
  # --------------------------------------------------------------------- #
@@ -319,6 +440,10 @@ def reader_function(
319
440
 
320
441
  for src in paths:
321
442
  try:
443
+ parquet_layers = _read_parquet_rows(src, mode)
444
+ if parquet_layers is not None:
445
+ layers.extend(parquet_layers)
446
+ continue
322
447
  layers.append(_read_one(src, mode=mode))
323
448
  except Exception as e:
324
449
  warnings.warn(
@@ -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.2'
32
- __version_tuple__ = version_tuple = (0, 0, 2)
31
+ __version__ = version = '0.0.3'
32
+ __version_tuple__ = version_tuple = (0, 0, 3)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-ome-arrow
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: A Napari plugin for OME-Arrow and OME-Parquet bioimage data
5
5
  License:
6
6
  Copyright (c) 2025, Dave Bunten
@@ -126,6 +126,9 @@ It provides a single, explicit pathway for loading OME-style bioimage data:
126
126
  - respects `NAPARI_OME_ARROW_LAYER_TYPE`, and
127
127
  - defaults to `"image"` if the variable is not set.
128
128
 
129
+ - ✅ **Grid view for multi-row OME-Parquet**
130
+ When a Parquet file contains multiple OME-Arrow rows, each row is loaded as its own layer and the viewer is switched to napari’s grid mode. Set `NAPARI_OME_ARROW_PARQUET_COLUMN` to pick which image column to visualize.
131
+
129
132
  ______________________________________________________________________
130
133
 
131
134
  This [napari] plugin was generated with [copier] using the [napari-plugin-template] (None).
@@ -175,6 +178,11 @@ NAPARI_OME_ARROW_LAYER_TYPE=image napari my_data.ome.tif
175
178
 
176
179
  # Load as labels (segmentation)
177
180
  NAPARI_OME_ARROW_LAYER_TYPE=labels napari my_labels.ome.parquet
181
+
182
+ # Pick a specific column in a multi-row OME-Parquet and show in grid mode
183
+ NAPARI_OME_ARROW_LAYER_TYPE=image \\
184
+ NAPARI_OME_ARROW_PARQUET_COLUMN=Image_FileName_OrigDNA_OMEArrow_ORIG \\
185
+ napari tests/data/cytodataframe/BR00117006.ome.parquet
178
186
  ```
179
187
 
180
188
  ## Contributing
@@ -0,0 +1,10 @@
1
+ napari_ome_arrow/__init__.py,sha256=hfpJMmD3HYHJH-0lh-esO04Pe57u8YbK4OO_qcx1b8o,236
2
+ napari_ome_arrow/_reader.py,sha256=JobcKfWGscMonX9u7cu_SdZtZL_Hbcj8-Uu1whuFe0o,14230
3
+ napari_ome_arrow/_version.py,sha256=pBZsQt6tlL02W-ri--X_4JCubpAK7jjCSnOmUp_isjc,704
4
+ napari_ome_arrow/napari.yaml,sha256=L64Y_YyvIlkOBgP2b8HciimyE3xmzfo9ASj5o-wu9gI,567
5
+ napari_ome_arrow-0.0.3.dist-info/licenses/LICENSE,sha256=RuLVTR9eFL6OX1phw5Ue8tdGrhgoG9ZErP6QrxrUlaE,1486
6
+ napari_ome_arrow-0.0.3.dist-info/METADATA,sha256=ABKwECGALgq03J4rmVonPtxUoP_EUxrkySZ7PIjgR6I,8596
7
+ napari_ome_arrow-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ napari_ome_arrow-0.0.3.dist-info/entry_points.txt,sha256=ZNpIZF-jrixFQlUNA1kR_jCBsBDxvoIrmc_lrQqu8VE,66
9
+ napari_ome_arrow-0.0.3.dist-info/top_level.txt,sha256=IRsRRK6uFbyipfWh77Br3G4_6uvSYp8CL01RjMN6Mzk,17
10
+ napari_ome_arrow-0.0.3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- napari_ome_arrow/__init__.py,sha256=hfpJMmD3HYHJH-0lh-esO04Pe57u8YbK4OO_qcx1b8o,236
2
- napari_ome_arrow/_reader.py,sha256=98vYiA5zK19sLCywWRKXJESmd3DbULIvpDJeiIg7raA,10398
3
- napari_ome_arrow/_version.py,sha256=huLsL1iGeXWQKZ8bjwDdIWC7JOkj3wnzBh-HFMZl1PY,704
4
- napari_ome_arrow/napari.yaml,sha256=L64Y_YyvIlkOBgP2b8HciimyE3xmzfo9ASj5o-wu9gI,567
5
- napari_ome_arrow-0.0.2.dist-info/licenses/LICENSE,sha256=RuLVTR9eFL6OX1phw5Ue8tdGrhgoG9ZErP6QrxrUlaE,1486
6
- napari_ome_arrow-0.0.2.dist-info/METADATA,sha256=3WRgK92QSCD5CT0k4FcY9bcRtCqm09QZdwZ1A_zWE-E,8087
7
- napari_ome_arrow-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- napari_ome_arrow-0.0.2.dist-info/entry_points.txt,sha256=ZNpIZF-jrixFQlUNA1kR_jCBsBDxvoIrmc_lrQqu8VE,66
9
- napari_ome_arrow-0.0.2.dist-info/top_level.txt,sha256=IRsRRK6uFbyipfWh77Br3G4_6uvSYp8CL01RjMN6Mzk,17
10
- napari_ome_arrow-0.0.2.dist-info/RECORD,,