VALIS
A published, single-author Python package that registers a whole series of whole-slide images — rigid, then non-rigid, then optional micro-registration — and hands back an OME-TIFF plus a per-pair error table and a function to warp point coordinates; the strongest available answer to a question this wiki has been deferring, and it fits the group’s QuPath conventions better than the tool currently recommended.
Purpose
Verdict: not adopted, because no live project needs registration today — but it displaces Cross-Stain Registration’s standing recommendation for the multi-slide case, and it is the thing to reach for the moment that question arrives. Read that page’s revised recommendation before choosing anything.
The decisive question for this cluster is not what VALIS does but what it does that QuPath Tooling does not already cover. That index currently points at tiatoolbox’s DFBRegister for cross-stain registration — deep-feature, affine, optional non-rigid B-spline, pairwise. Four capabilities here have no counterpart in the indexed stack:
- A series, not a pair.
Valis(src_dir, dst_dir, ...)takes a directory of slides, orders them by feature similarity if the order is unknown, picks a reference (the middle of the stack by default) and aligns each image towards it through its neighbours. For a block cut into H&E plus several IHC levels that is the actual shape of the problem, and it is not the shapeDFBRegistersolves. - Point warping.
Slide.warp_xyandwarp_xy_from_toare public and documented for cell centroids and annotation vertices. That is the mechanism for carrying a QuPath annotation from one stain to another instead of redrawing it — the payoff QuPath Annotation Workflow names and nothing in the estate currently implements. - A per-pair error table — see the caveat below, which is most of this page’s value.
- OME-TIFF out, 300+ formats in. Reading goes through Bio-Formats and OpenSlide; output is pyramidal OME-TIFF that QuPath opens directly, which matches the group’s existing interchange habit.
Its multiplex-merge feature — combining registered non-RGB slides into one highly multiplexed image — is irrelevant here for the same reason CORAL and KRONOS were: there is no multiplex imaging in this department. See Spatial Proteomics.
Data used
Not a data project. It consumes slide files; the repository ships small IHC and CyCIF example sets under examples/.
Methods
Read at source level on 2026-08-01 from a full clone: pyproject.toml, LICENSE.txt, README.rst, CITATION.cff, Dockerfile, valis/registration.py (5,377 lines), valis/feature_matcher.py, valis/feature_detectors.py, tests/test_examples.py. Every claim below was independently re-checked by a reviewer instructed only to refute it, which corrected two of them; both corrections are noted where they land.
The pipeline, and the resolution it actually runs at
Rigid registration is fitted on downsampled images, then applied to the full-resolution slide. The parameter that sets this is max_processed_image_dim_px, default 512 — its own docstring says it “determines the size of the image in which features will be detected and displacement fields computed”, and registration.py enforces it by scaling every processed image to fit. (max_image_dim_px, default 1024, is a different thing: a memory cap on the images written out. An earlier draft of this page attributed the transform-estimation cap to that constant. It was wrong, and the correction narrows the number rather than changing the conclusion.) Non-rigid registration runs at up to 2048 px, and micro-registration is off by default — micro_rigid_registrar_cls=None, register() gates on it explicitly, and register_micro() is a separate call the user has to make, at up to 4096 px.
Defaults worth knowing before quoting any benchmark: matcher is LightGlue over DISK features, transform is SimilarityTransform, affine optimisation is off, non-rigid warper is optical flow.
The error table, and why it is not the failure signal it looks like
This is the finding that matters. measure_error() builds a summary_df carrying, per image pair, original_D, rigid_D, non_rigid_D and the matching original_rTRE, rigid_rTRE, non_rigid_rTRE, plus physical units and resolution, and register() writes it to <name>_summary.csv on a normal run. Before-and-after error per pair, in microns, without asking: that is more than any other registration tool in this wiki offers, and Cross-Stain Registration argues that a thresholdable quality score is worth more than marginally better accuracy.
But the number is a training residual, not an independent check. D is the median distance between the same matched keypoints that were used to fit the transform, warped through it and scaled to microns; rTRE divides that by the reference image diagonal. Those keypoints are the registration’s own RANSAC inliers, measured at the 512-px processed scale. A transform that confidently aligns the wrong structures will report a small D, because the points it is scored on are the points it was fitted to. It is a useful relative signal — did non-rigid improve on rigid — and it is not evidence that the alignment is right. Independent landmarks would be, and nothing here produces them.
Two smaller notes on the same table: register_micro() overwrites the same CSV, so the micro-registration result replaces rather than extends the record; and the whole of register() sits inside one except Exception that prints a warning, kills the JVM and returns (None, None, None) instead of raising. A batch loop that does not check the return value will record a failure as a quiet skip. That is the third instance in this wiki of an operation that fails while the surrounding process reports normally — see Slide Metadata Extraction for the other two.
The project’s own accuracy bar
tests/test_examples.py gates brightfield IHC registration at max_error=70 and CyCIF at max_error=3. Seventy microns is roughly seven cell diameters. That is the maintainers’ own regression threshold rather than a claim about achievable accuracy, but it is the cleanest evidence in this wiki for the premise Cross-Stain Registration states from sectioning physics: serial-section registration delivers the same region, never the same cell. The twenty-fold tighter CyCIF bar is the contrast that proves the point — those are repeated images of one physical section, so there is no serial-section gap to lose.
Operational shape
Reading slides through Bio-Formats means a JVM: the package imports jpype and scyjava and exposes init_jvm / kill_jvm, and pyvips and OpenSlide are required alongside. pyproject.toml hard-pins opencv-contrib-python-headless==4.9.0.80 and caps numpy<2.0.0, which will fight most current imaging environments. The maintainers ship an official Docker image (cdgatenbee/valis-wsi, built from an Ubuntu Dockerfile in-repo), and on the Windows workstations this group runs that is the realistic route rather than a native install.
Current state / open questions
- The tool is dormant but finished, and those are different things. No commits in the 13 months to 2026-08-01, but HEAD matches the newest tag and the packaged version, the paper is published in Nature Communications (Gatenbee et al. 2023, 10.1038/s41467-023-40218-9), and there is a Zenodo DOI. Nothing here suggests abandonment mid-change. It is a single-author package, so the maintenance risk is concentrated in one person.
- The package vendors Magic Leap’s SuperPoint/SuperGlue source and ~98 MB of weights, carrying a “CONFIDENTIAL / dissemination strictly forbidden” header, inside an otherwise MIT distribution, and
feature_matcher.pyimports that module unconditionally even though the default matcher is LightGlue. Recorded, not investigated, perAGENTS.md§8; only the repository owner can resolve it. - Would the reported
rTREactually be usable as an acceptance threshold? Not as shipped, per the circularity above. The open design question is what independent landmark set would serve — a handful of hand-placed fiducials per case would be enough and nobody has specified them. - Does the group have a question that needs registration at all? Cross-Stain Registration says not today. Deciding that is a prerequisite to any of this, not a consequence of it.
How it connects
Cross-Stain Registration — the method page this reading rewrites; VALIS is now the recommendation there for the series case and for annotation transfer, with the error-metric caveat attached.
QuPath Tooling — the hub whose registration entry pointed only at tiatoolbox’s DFBRegister; the two now sit side by side there with the reason to pick each.
QuPath Annotation Workflow — warp_xy is the missing mechanism for carrying an annotation between stains rather than redrawing it, and GeoJSON vertices are exactly the coordinates it takes.
HER2 Intratumoral Heterogeneity — the project where a serial IHC/ISH series exists, and so the first place this would be pointed if the question moved from case-level discordance to whether the same regions are positive on both.
Slide Metadata Extraction — the fail-quietly pattern: register() swallowing every exception and returning None is the same failure shape as the QuPath metadata script reporting a 100% success rate on 107 failed images.
Spatial Proteomics — the multiplex-merge half of VALIS, and the reason it does not apply here.
Valis-GUI — the desktop front end, evaluated separately and not usable as shipped.
Derived from: repository cloned at full depth 2026-08-01 (325828c, 429 commits, six tags); files read as listed under Methods; currency and contributor figures computed from the clone’s own git history, because the GitHub API was unreachable from this session — star, fork and open-issue counts were therefore not obtained and are not stated here.