napari-ome-arrow 0.0.2__tar.gz → 0.0.3__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.
Files changed (30) hide show
  1. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/workflows/publish-pypi.yml +1 -1
  2. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/workflows/run-tests.yml +2 -2
  3. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.pre-commit-config.yaml +2 -2
  4. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/CITATION.cff +65 -0
  5. {napari_ome_arrow-0.0.2/src/napari_ome_arrow.egg-info → napari_ome_arrow-0.0.3}/PKG-INFO +9 -1
  6. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/README.md +8 -0
  7. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow/_reader.py +125 -0
  8. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow/_version.py +3 -3
  9. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3/src/napari_ome_arrow.egg-info}/PKG-INFO +9 -1
  10. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/ISSUE_TEMPLATE/issue.yml +0 -0
  11. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  12. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/dependabot.yml +0 -0
  13. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/release-drafter.yml +0 -0
  14. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.github/workflows/draft-release.yml +0 -0
  15. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.gitignore +0 -0
  16. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/.napari-hub/config.yml +0 -0
  17. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/CODE_OF_CONDUCT.md +0 -0
  18. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/CONTRIBUTING.md +0 -0
  19. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/LICENSE +0 -0
  20. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/MANIFEST.in +0 -0
  21. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/pyproject.toml +0 -0
  22. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/setup.cfg +0 -0
  23. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow/__init__.py +0 -0
  24. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow/napari.yaml +0 -0
  25. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow.egg-info/SOURCES.txt +0 -0
  26. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow.egg-info/dependency_links.txt +0 -0
  27. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow.egg-info/entry_points.txt +0 -0
  28. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow.egg-info/requires.txt +0 -0
  29. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/src/napari_ome_arrow.egg-info/top_level.txt +0 -0
  30. {napari_ome_arrow-0.0.2 → napari_ome_arrow-0.0.3}/uv.lock +0 -0
@@ -16,7 +16,7 @@ jobs:
16
16
  id-token: write
17
17
  steps:
18
18
  - name: Checkout
19
- uses: actions/checkout@v5
19
+ uses: actions/checkout@v6
20
20
  with:
21
21
  fetch-depth: 0
22
22
  - name: Fetch tags
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-24.04
14
14
  steps:
15
15
  # checks out the repo
16
- - uses: actions/checkout@v5
16
+ - uses: actions/checkout@v6
17
17
  # run pre-commit
18
18
  - name: Python setup
19
19
  uses: actions/setup-python@v6
@@ -40,7 +40,7 @@ jobs:
40
40
  OS: ${{ matrix.os }}
41
41
  steps:
42
42
  - name: Checkout
43
- uses: actions/checkout@v5
43
+ uses: actions/checkout@v6
44
44
  - name: Python setup
45
45
  uses: actions/setup-python@v6
46
46
  with:
@@ -39,12 +39,12 @@ repos:
39
39
  - id: yamllint
40
40
  exclude: pre-commit-config.yaml
41
41
  - repo: https://github.com/astral-sh/ruff-pre-commit
42
- rev: "v0.14.5"
42
+ rev: "v0.14.7"
43
43
  hooks:
44
44
  - id: ruff-format
45
45
  - id: ruff-check
46
46
  - repo: https://github.com/rhysd/actionlint
47
- rev: v1.7.8
47
+ rev: v1.7.9
48
48
  hooks:
49
49
  - id: actionlint
50
50
  - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update
@@ -60,3 +60,68 @@ references:
60
60
  - description: "OME Model Documentation — OME-TIFF Sample Data"
61
61
  type: url
62
62
  value: "https://ome-model.readthedocs.io/en/stable/ome-tiff/data.html"
63
+ - authors:
64
+ - family-names: Chandrasekaran
65
+ given-names: Srinivas Niranj
66
+ - family-names: Cimini
67
+ given-names: Beth A.
68
+ - family-names: Goodale
69
+ given-names: Amy
70
+ - family-names: Miller
71
+ given-names: Lisa
72
+ - family-names: Kost-Alimova
73
+ given-names: Maria
74
+ - family-names: Jamali
75
+ given-names: Nasim
76
+ - family-names: Doench
77
+ given-names: John G.
78
+ - family-names: Fritchman
79
+ given-names: Briana
80
+ - family-names: Skepner
81
+ given-names: Adam
82
+ - family-names: Melanson
83
+ given-names: Michelle
84
+ - family-names: Kalinin
85
+ given-names: Alexandr A.
86
+ - family-names: Arevalo
87
+ given-names: John
88
+ - family-names: Haghighi
89
+ given-names: Marzieh
90
+ - family-names: Caicedo
91
+ given-names: Juan C.
92
+ - family-names: Kuhn
93
+ given-names: Daniel
94
+ - family-names: Hernandez
95
+ given-names: Desiree
96
+ - family-names: Berstler
97
+ given-names: James
98
+ - family-names: Shafqat-Abbasi
99
+ given-names: Hamdah
100
+ - family-names: Root
101
+ given-names: David E.
102
+ - family-names: Swalley
103
+ given-names: Susanne E.
104
+ - family-names: Garg
105
+ given-names: Sakshi
106
+ - family-names: Singh
107
+ given-names: Shantanu
108
+ - family-names: Carpenter
109
+ given-names: Anne E.
110
+ date-accessed: "2024-08-21"
111
+ title: >-
112
+ Three million images and morphological profiles of cells treated with matched chemical and genetic perturbations
113
+ type: article
114
+ issn: 1548-7105
115
+ issue: 6
116
+ journal: Nature Methods
117
+ pages: 1114-1121
118
+ volume: 21
119
+ url: https://doi.org/10.1038/s41592-024-02241-6
120
+ date-published: "2024-06-01"
121
+ identifiers:
122
+ - type: doi
123
+ value: 10.1038/s41592-024-02241-6
124
+ notes: >-
125
+ JUMP (cpg0000-jump-pilot) was used to help demonstrate napari-ome-arrow's
126
+ ability to parse various image data (specifically, plate BR00117006).
127
+ https://github.com/broadinstitute/cellpainting-gallery
@@ -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
@@ -57,6 +57,9 @@ It provides a single, explicit pathway for loading OME-style bioimage data:
57
57
  - respects `NAPARI_OME_ARROW_LAYER_TYPE`, and
58
58
  - defaults to `"image"` if the variable is not set.
59
59
 
60
+ - ✅ **Grid view for multi-row OME-Parquet**
61
+ 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.
62
+
60
63
  ______________________________________________________________________
61
64
 
62
65
  This [napari] plugin was generated with [copier] using the [napari-plugin-template] (None).
@@ -106,6 +109,11 @@ NAPARI_OME_ARROW_LAYER_TYPE=image napari my_data.ome.tif
106
109
 
107
110
  # Load as labels (segmentation)
108
111
  NAPARI_OME_ARROW_LAYER_TYPE=labels napari my_labels.ome.parquet
112
+
113
+ # Pick a specific column in a multi-row OME-Parquet and show in grid mode
114
+ NAPARI_OME_ARROW_LAYER_TYPE=image \\
115
+ NAPARI_OME_ARROW_PARQUET_COLUMN=Image_FileName_OrigDNA_OMEArrow_ORIG \\
116
+ napari tests/data/cytodataframe/BR00117006.ome.parquet
109
117
  ```
110
118
 
111
119
  ## Contributing
@@ -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
- __commit_id__ = commit_id = 'g6635efe48'
34
+ __commit_id__ = commit_id = 'g837da2b31'
@@ -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