Metadata-Version: 2.4
Name: bioio-qptiff
Version: 0.1.0
Summary: A BioIO reader plugin for PerkinElmer/Akoya QPTIFF (Vectra/Polaris/Fusion) images.
Author-email: Rafael Tubelleza <rafaelrtubelleza@gmail.com>
License: BSD-3-Clause
Project-URL: Homepage, https://github.com/rtubelleza/bioio-qptiff
Project-URL: Bug Tracker, https://github.com/rtubelleza/bioio-qptiff/issues
Project-URL: Documentation, https://rtubelleza.github.io/bioio-qptiff
Project-URL: User Support, https://github.com/rtubelleza/bioio-qptiff/issues
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: BSD License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: bioio-base>=0.3.0
Requires-Dist: dask[array]>=2021.4.1
Requires-Dist: fsspec>=2022.8.0
Requires-Dist: imagecodecs>=2020.5.30
Requires-Dist: multiscale-spatial-image<3,>=2.0.3
Requires-Dist: numpy>=1.16
Requires-Dist: ome-types>=0.5.0
Requires-Dist: ome-zarr-models>=1.6.0
Requires-Dist: tifffile[zarr]>=2023.7.10
Requires-Dist: xarray>=2024.10
Requires-Dist: zarr>=3
Provides-Extra: ome-tiff
Requires-Dist: bioio-ome-tiff>=1.0.0; extra == "ome-tiff"
Provides-Extra: lint
Requires-Dist: pre-commit>=2.20.0; extra == "lint"
Provides-Extra: test
Requires-Dist: bioio>=3.0.0; extra == "test"
Requires-Dist: bioio-ome-zarr>=2.0.0; extra == "test"
Requires-Dist: bioio-ome-tiff>=1.0.0; extra == "test"
Requires-Dist: coverage>=5.1; extra == "test"
Requires-Dist: pytest>=5.4.3; extra == "test"
Requires-Dist: pytest-cov>=2.9.0; extra == "test"
Requires-Dist: pytest-raises>=0.11; extra == "test"
Dynamic: license-file

# bioio-qptiff

[![Build Status](https://github.com/rtubelleza/bioio-qptiff/actions/workflows/ci.yml/badge.svg)](https://github.com/rtubelleza/bioio-qptiff/actions)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Python 3.11–3.13](https://img.shields.io/badge/python-3.11--3.13-blue.svg)](https://www.python.org/downloads/)

A [BioIO](https://github.com/bioio-devs/bioio) reader plugin for PerkinElmer / Akoya / Quanterix
**QPTIFF** images (Vectra, Polaris, PhenoCycler-Fusion).

Reads QPTIFFs with `tifffile` with parsing and organisation of all QPI XML metadata fields into richly annotated xarray dims, coords and attrs: per-channel biomarker,
fluorophore, filter passbands, exposure, camera settings, etc.

Includes writers to convert qptiffs directly to ome.zarr (OME-NGFF 0.5 spec) and ome.tiff images, with preservation of the original QPTIFF pyramid (if any) and per-channel/axis metadata.

## Documentation

## Installation

**Stable Release:** `pip install bioio-qptiff`<br>
**Development Head:** `pip install git+https://github.com/rtubelleza/bioio-qptiff.git`

## Quickstart
Install bioio-qptiff alongside bioio:

`pip install bioio bioio-qptiff`

Below shows how to read a .qptiff file and access the lazy xarray with all aligned
metadata. Every scene the file carries is exposed (`FullResolution` plus any
`Label` / `Macro` / `Overview` / `Thumbnail`). By default the first scene, normally
`FullResolution`, is loaded. 
```python
from bioio import BioImage
import bioio_qptiff

img = BioImage("slide.qptiff", reader=bioio_qptiff.Reader)

img.xarray_dask_data
```

Which gives the array plus every QPI XML field aligned to the channel axis:

```
<xarray.DataArray (C: 50, Y: 12345, X: 12345)> Size: 1234MB
dask.array<transpose, shape=(50, 12345, 12345), dtype=uint8, chunksize=(1, 1234, 1234)>
Coordinates:
  * C                               (C) <U4 'MARKER1' 'MARKER2' ...
    fluorophore                     (C) <U4 'AF123' 'Cy123' ...
    exposure_time_us                (C) float64 1+04 2+05 ...
    ... 

Dimensions without coordinates: Y, X
Attributes:
    slide_info:           {'slide_id': 'slide', ...} 
    image_info:           {'image_type': 'FullResolution', ...}
    pyramid_level_count:  5
    unprocessed:          {}
    processed:            {'slide': {...}, 'images': [...]}
```

To get all of the image scenes in the contained qptiff use the custom qptiff reader attribute:

```python
img.reader.xarray_dask_scene_datatree
```

Each scene becomes a node. Pyramidal scenes hold their levels as children,
single-image scenes hold `image` directly:

```
<xarray.DataTree>
Group: /
│   Attributes:
│       slide_info:  {'slide_id': 'SLIDE', ...}
├── Group: /FullResolution
│   │   Attributes:
│   │       image_info:           {'image_type': 'FullResolution', ...}
│   │       pyramid_level_count:  5
│   ├── Group: /FullResolution/scale0
│   │       Dimensions:  (c: 50, y: 12345, x: 12345)
│   │       Coordinates:
│   │         * c            (c) <U4 'MARKER1' 'MARKER2'
│   │           fluorophore  (c) <U4 'AF1234' 'Cy123'
│   │           ...          (all channel coords, as above)
│   ├── Group: /FullResolution/scale1
│   │       Dimensions:  (c: 50, y: 6172, x: 6172)
│   ├── Group: /FullResolution/scale2
│   │       Dimensions:  (c: 50, y: 3086, x: 3086)
│   ├── Group: /FullResolution/scale3
│   │       Dimensions:  (c: 50, y: 1543, x: 1543)
│   └── Group: /FullResolution/scale4
│           Dimensions:  (c: 50, y: 771, x: 771)
├── Group: /Thumbnail
│       Dimensions:  (c: 3, y: 321, x: 321)
├── Group: /Overview
│       Dimensions:  (c: 3, y: 321, x: 321)
└── Group: /Label
        Dimensions:  (c: 3, y: 321, x: 321)
```

Or use the inbuilt writers to convert qptiffs directly to ome.zarr and ome.tiffs:
```python
from bioio_qptiff.ome import qptiff_to_ome_zarr, qptiff_to_ome_tiff

# the current scene only
qptiff_to_ome_zarr("slide.qptiff", "slide.ome.zarr", overwrite=True)
qptiff_to_ome_tiff("slide.qptiff", "slide.ome.tiff")

# every scene in the file, or a named subset
qptiff_to_ome_zarr("slide.qptiff", "all.ome.zarr", scenes="all", overwrite=True)
qptiff_to_ome_tiff("slide.qptiff", "all.ome.tiff", scenes=["FullResolution", "Label"])
```

Both writers keep the source pyramid and the per-channel metadata. Written with
more than one scene, OME-TIFF emits consecutive series and OME-Zarr emits the
bioformats2raw layout.

```
all.ome.zarr/
  zarr.json          ome: {version: "0.5", bioformats2raw.layout: 3}
  OME/
    zarr.json        ome: {version: "0.5", series: ["0", "1", "2", "3"]}
    METADATA.ome.xml
  0/                 FullResolution   (scale0 .. scale4)
  1/                 Thumbnail
  2/                 Overview
  3/                 Label
```

## Development

See [CONTRIBUTING.md](CONTRIBUTING.md) for information related to developing the code.
