Convert Aperio SVS (compression 33007 / YUYV) to pyramidal OME-TIFF
Project description
svs-to-ometiff
Convert Aperio SVS files with private compression tag 33007 into OME BigTIFF.
⚠️ Experimental: This converter has structural validation on one real Aperio
33007SVS file and synthetic pixel-level tests. Manual visual review and multi-scanner validation are still limited. Do not use converted output for diagnostic decisions.
At a glance
| Question | Answer |
|---|---|
| Primary use | Convert Aperio SVS Compression=33007 to RGB OME BigTIFF |
| Default output | Pyramidal OME BigTIFF (no compression by default) |
| Validated profile | --compression none --num-levels 3 --tile-size 512 (these are the defaults) |
| Memory model | Streamed tile decode plus disk-backed RGB pyramid levels |
| Current validation | Synthetic tests plus one real lung H&E SVS structural check |
| Not for | JPEG/JPEG 2000 SVS, diagnostic use, unvalidated scanners |
Contents
- Why this exists
- Install
- Quick start
- CLI options
- Programmatic use
- How it works
- Verify an output
- Validation status
- Limitations
- Downstream compatibility goals
- Troubleshooting
- Web GUI
- Development
- How to help validate
Why this exists
Some Aperio AT2/GT450 exports store .svs image tiles with TIFF Compression
tag 33007. In the validation files seen so far, those tiles are not standard
JPEG/JPEG 2000 payloads. They are raw YUYV YCbCr 4:2:2 bytes where each
two-pixel pair is stored as [Y0, U, Y1, V].
That private compression tag can block common whole-slide and spatial biology tools before registration or image review starts.
| Tool | Typical failure mode |
|---|---|
| OpenSlide | Unsupported TIFF compression: 33007 |
Bio-Formats / bfconvert |
Attempts JPEG-family decoding and fails on scanlines |
| QuPath | Fails through OpenSlide/Bio-Formats backends |
| libvips | Fails on unsupported compression 33007 |
svs-to-ometiff decodes that YUYV tile payload and writes a standards-oriented
OME BigTIFF with RGB tiles and optional pyramid levels.
Install
pip install svs-to-ometiff
Development install from GitHub:
pip install git+https://github.com/tommy2scripts/svs-to-ometiff.git
Developer tools:
pip install "svs-to-ometiff[dev]"
Compression dependency note
Compressed output (lzw, zlib, or deflate) depends on imagecodecs through
tifffile. A normal package install declares imagecodecs as a dependency.
The default output compression is none (uncompressed), so imagecodecs is only
needed when you explicitly choose a compression scheme:
svs-to-ometiff input.svs output.ome.tiff --compression lzw
Uncompressed output is larger but is the safest fallback for compatibility and debugging.
Quick start
Optional source check before converting:
python - <<'PY'
import tifffile
with tifffile.TiffFile("input.svs") as tif:
print("compression:", tif.pages[0].tags["Compression"].value)
print("shape:", tif.pages[0].shape)
PY
Continue only if the source compression is 33007.
Default pyramidal conversion:
svs-to-ometiff input.svs output.ome.tiff
Known-good conservative command from the real-file structural validation:
svs-to-ometiff input.svs output.ome.tiff \
--num-levels 3 \
--compression none \
--tile-size 512
Single-resolution output, useful when the downstream tool does not need a pyramid:
svs-to-ometiff input.svs output.ome.tiff --num-levels 1
Run as a module:
python -m svs_to_ometiff input.svs output.ome.tiff
CLI options
| Option | Default | Use when |
|---|---|---|
--tile-size |
512 |
You need a different square output tile size |
--compression |
none |
Choose lzw, zlib, deflate, or none |
--num-levels |
3 |
Reduce levels for faster conversion or use 1 for single-resolution output |
--downsample-factor |
2 |
Change pyramid spacing between levels |
--edge-mode |
crop |
Use pad if you prefer padded borders over cropped odd edges |
--image-name |
input stem | Override the OME Image name |
--quiet |
off | Suppress progress output |
--verbose |
off | Print detailed progress, including every source tile row |
Programmatic use
from svs_to_ometiff import ConvertConfig, convert
# Simple kwargs interface
convert("input.svs", "output.ome.tiff", verbose=True)
# Typed config for pipelines
config = ConvertConfig(
input_svs="slide.svs",
output_ometiff="slide.ome.tiff",
compression=None, # Equivalent to --compression none
num_levels=3,
tile_size=512,
verbose=True,
)
convert(config)
How it works
The converter uses a streamed, disk-backed path by default:
- Read SVS metadata and validate that the source uses Compression tag
33007. - Iterate source tiles without decoding the whole slide into a single Python array.
- Decode each raw YUYV YCbCr 4:2:2 tile to RGB using BT.601-style conversion.
- Stage level 0 and derived pyramid levels as disk-backed NumPy memmaps near the output path.
- Write a tiled OME BigTIFF, linking pyramid levels through TIFF SubIFDs.
- Replace the destination only after a successful write, so a failed conversion does not overwrite an existing output file.
This lowers Python heap pressure compared with building the entire full-resolution pyramid in memory. It does not remove the need for disk space: temporary RGB pyramid levels plus the final OME-TIFF can be large.
Verify an output
Run this after every real conversion:
python - <<'PY'
import tifffile
path = "output.ome.tiff"
with tifffile.TiffFile(path) as tif:
assert tif.is_ome, "missing OME metadata"
assert tif.is_bigtiff, "expected BigTIFF output"
levels = tif.series[0].levels
assert len(levels) >= 1, "missing image level"
assert levels[0].shape[-1] == 3, "expected RGB output"
print("verified", path)
print("levels:", [level.shape for level in levels])
print("subifds:", len(tif.pages[0].pages))
PY
At minimum, also review a thumbnail visually before using the converted image for registration or handoff.
For the current validation checklist and real-file record, see
docs/validation_protocol.md.
Validation status
Current evidence
| Claim | Evidence | Confidence |
|---|---|---|
| YUYV decoder produces expected RGB on controlled inputs | Unit tests for grayscale, color tint, clipping, and synthetic tile reconstruction | Medium |
| Synthetic compression-33007 SVS converts end-to-end | Integration tests with known pixel values | Medium |
| OME BigTIFF structure is readable | tifffile opens output as OME BigTIFF with pyramid levels |
Medium-high |
| SubIFD pyramid linkage works | tifffile.TiffFile.series[0].levels enumerates levels |
Medium-high |
| Single-resolution output works | --num-levels 1 uses the same generalized writer path |
Medium |
| Disk-backed path is exercised | Synthetic memory profiling tests and memmap-vs-in-memory pyramid parity tests | Medium |
| Real SVS structural conversion works | One Aperio 33007 lung H&E SVS converted to 3-level uncompressed OME BigTIFF |
Single file |
Real-file validation snapshot
| Field | Result |
|---|---|
| Source | 67174_PT_Lung.svs |
| Compression | 33007 |
| Dimensions | 39599 x 39858 px |
| Source tiles | 256 x 256, 24180 tiles |
| Command | --num-levels 3 --compression none --tile-size 512 |
| Output levels | (39858, 39599, 3), (19929, 19799, 3), (9964, 9899, 3) |
| Output structure | is_bigtiff=True, is_ome=True, 2 SubIFDs |
| Output size | 5.86 GiB / 6.30 GB |
| Peak RSS in local run | 1.91 GB from /usr/bin/time -l |
| Visual review | Pending manual thumbnail review |
Still open
| Area | Status |
|---|---|
| Other Aperio scanner models | Not tested |
| Other AT2/GT450 firmware/export settings | Not tested |
| Other tissues, stains, IHC, IF, or special stains | Not tested |
| Xenium Explorer H&E overlay workflow | Planned, not completed |
| Space Ranger / Visium image input workflow | Intended, not completed |
| Color accuracy vs. vendor/reference decoder | Not validated |
| Multi-file reproducibility | One real file tested |
Limitations
- Handles only Aperio Compression tag
33007with the observed raw YUYV tile payload. - Does not handle standard JPEG SVS (
Compression=7); use OpenSlide, libvips, or Bio-Formats for those. - Does not handle JPEG 2000 SVS variants such as
33003or33005. - Does not validate color fidelity against a scanner profile or reference decoder.
- Does not recover from malformed, truncated, or corrupted tiles.
- Requires substantial free disk space near the output path for temporary memmaps and final OME-TIFF output.
- RSS and runtime vary with OS page cache, disk speed, pyramid level count, tile size, and compression codec behavior.
Downstream compatibility goals
Outputs are intended to be ordinary tiled OME BigTIFF files for tools that can consume OME-TIFF pyramids, including:
- Xenium Ranger / Xenium Explorer H&E registration and overlay workflows
- Space Ranger image inputs after OME-TIFF conversion
- QuPath
- napari through
tifffile - HALO and other OME-TIFF-aware pathology viewers
These are compatibility goals, not a completed validation matrix. Please verify your own downstream import before relying on a converted slide.
Troubleshooting
| Symptom | Likely cause | What to try |
|---|---|---|
input uses compression 7 |
Standard JPEG SVS, not Aperio 33007 |
Use OpenSlide/libvips/Bio-Formats instead |
pip install imagecodecs error during write |
Compression encoder unavailable in active Python env | Install imagecodecs or rerun with --compression none |
| Output is very large | Default is uncompressed (--compression none) |
Use --compression lzw if imagecodecs is available |
| Conversion uses lots of disk | Disk-backed RGB pyramid levels plus final output | Use fewer --num-levels, ensure free disk space near output |
| Downstream viewer is slow | Large uncompressed BigTIFF or many levels | Try compressed output or fewer levels after validation |
Web GUI
A separate experimental Flask GUI is available as svs-to-ometiff-gui:
pip install svs-to-ometiff-gui
svs-to-ometiff-gui
It opens a local browser UI at http://127.0.0.1:8765 with drag-and-drop
upload, progress streaming, and expandable settings.
📌 Note: The GUI is separate from this core CLI package.
Development
git clone git@github.com:tommy2scripts/svs-to-ometiff.git
cd svs-to-ometiff
pip install -e ".[dev]"
python -m pytest -q
python -m ruff check .
Strict local memory checks can be enabled manually:
SVS_OMETIFF_STRICT_MEMORY=1 python -m pytest tests/test_memory.py -v -s
How to help validate
If you have Aperio SVS files with Compression tag 33007, please test and
report the outcome through GitHub Issues. Useful details include:
- scanner model and firmware, if known
- tissue type and stain
- source dimensions and tile size
- source TIFF Compression tag
- exact conversion command
- output structural check results
- whether visual review or downstream import passed
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file svs_to_ometiff-0.4.1.tar.gz.
File metadata
- Download URL: svs_to_ometiff-0.4.1.tar.gz
- Upload date:
- Size: 47.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3771973aa5747d117b9d2678278809c8ba0f5f0e9081418b0e59d20b1752e4df
|
|
| MD5 |
9737bdfe4724214c3f57fb1900f8f0ed
|
|
| BLAKE2b-256 |
fef61a0ff8693f4c7bc3e0ac705e8773689a1c0ecf1d60ad2ad6bbf4bb3cbdfd
|
File details
Details for the file svs_to_ometiff-0.4.1-py3-none-any.whl.
File metadata
- Download URL: svs_to_ometiff-0.4.1-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f58423c0eac681fe6dc303fbb98e05e3f3fff755b57d20c0e9fdca1afea36d73
|
|
| MD5 |
b9a9b7991ebc7ec5f143852c9b3ccd79
|
|
| BLAKE2b-256 |
18a95fc8dcd36295bfcc3e4b93cde8b77504d707da5ba5398b198c5c3ff2db6a
|