Skip to main content

zcmir

Multimodal image registration on the GPU, in the browser. zcmir aligns two images of the same region taken with different instruments or contrasts, such as EBSD maps to polarized light micrographs, near-infrared to RGB aerial photos, or fluorescence to bright field.

Registering a rotated, scaled Landsat pair: Auto finds keypoints, fits a homography and refines it

Run the app

Open it at https://zacharyvarley.github.io/zcmir/, or run it locally:

pip install zcmir
python -m zcmir

This serves the app on http://127.0.0.1:4180/ and opens it in your browser. Everything runs on your GPU inside the page, so your images never leave your machine. You need Chrome or Edge 137+ (WebGPU and JavaScript Promise Integration). --port picks another port (a free one is used if 4180 is taken), and --no-browser only prints the URL. pip also installs a zcmir command that does the same; like any package's command, it exists only inside the environment you installed into.

To install from a clone instead, run pip install . in it. The first build downloads Zig and wgpu-native into .deps/ and compiles for your machine, which takes a minute or two.

Open a moving and a fixed image (or drop both on the window), or pick one of the Examples, then press Auto. Each step also has its own card, settings and play button:

Step What it does
Detect POS-GIFT keypoints [7], modified (see below), or GLS-MIFT as a faster alternative
Match nearest descriptors, a robust fit of your choice (FSC / PROSAC / MAGSAC++) at your inlier distance, optional POS-guided re-matching; every fit is held to pose limits (no folding, mirroring, or extreme scale, stretch or perspective)
Refine Gauss–Newton ascent of the similarity over an affine or homography, optionally with a cubic B-spline under a thin-plate bending penalty, entirely on the GPU
Brute force optional: a sweep over rotation (the full circle, ± a half-range, or a from–to range) × scale × translation, optionally × shear × stretch, or a cloud of seeds around the pose

The Maps tab shows the similarity at every translation, and at every rotation × scale, around the current pose. Stack mode registers serial sections or a time series slice by slice. It carries each pose to the next slice, retries keypoints when the score drops, and flags slices that break from their neighbours' trend. A registered pair exports its transform and a displacement field (NRRD plus an ITK .tfm); a stack exports as a TIFF stack or PNG files.

How the similarity is computed

Mutual information (MI) is the standard similarity for images of different modalities. Heldmann et al. [1] and August and Kanade [2] originated estimating it densely with FFTs and the convolution theorem. Öfverstedt, Lindblad and Sladoje [3] made it fast for global alignment: binned MI at every translation, from FFT cross-correlations of the intensity-bin indicator images. Its cost grows with the number of bin pairs.

I proposed replacing binned MI with cheaper estimates built from a few moments, so the whole map takes a small, fixed number of FFTs. I first tried the Edgeworth approximation of Rubeaux et al. [4], which is very inexpensive in moments and FFTs. In my thesis [5] I extended it to fourth order from non-central moments (still available as the E4 score), but I found these approximations numerically unstable.

After exploring the literature I settled on square-loss mutual information (SMI) [6]. Each image is described by four channels: its rank r (histogram-equalized intensity), r², the Sobel gradient magnitude of r, and the Laplacian of r. With this linear basis, SMI is the squared norm of the whitened cross-covariance of the two images' channels over their overlap. Every term is a sum over the overlap, so the score at every translation takes 45 FFT cross-correlations, however many intensity levels the images have. The fourth channel is a setting (smi_features): the Laplacian over 3 × 3 pixels, none, which takes 28 correlations and runs the maps and brute force about 20% faster, or r³, the original set. The same moments give Refine its score, gradient and Gauss–Newton curvature.

The λmax score (in the Refine card's Score menu) also works well, especially for images without sharp boundaries. It is the Gaussian-copula MI of the top canonical correlation between [z, z², z³] of each image's normal scores.

POS-GIFT in zcmir

POS-GIFT [7] gives good matches across a huge variety of multimodal registration tasks, but its authors released it only as closed MATLAB code. I reverse engineered it, reimplemented it on the GPU, and modified it. zcmir's defaults differ from the authors' code as follows:

Authors' released code zcmir default
Keypoint map normalized phase-congruency sum minimum moment of phase congruency
Rotation an orientation per keypoint (Sobel histogram) upright descriptors; one global rotation chosen among 24 hypotheses 15° apart (descriptors turned by channel permutation, plus a 15°-turned copy of the image), ranked by match consensus
Descriptor sharpness per-pixel range-normalized channels the same, raised to a power (1.5, pg_desc_pow) and normalized per sampled point
Patch radius (P1) 16 10
Matching one pooled match and FSC over every scale pair consensus per scale pair, then one fit
Final model homography the model you choose (homography by default): the POS fit or the pooled fit, whichever scores higher

I chose these changes on EBSD ↔ BSE sections and rotated cross-modal pairs, where they reduced gross failures. Per-keypoint orientation is reliable within one modality but often wrong across modalities, hence the global rotation search. zcmir.register(moving, fixed, pg_released=True) reproduces the authors' behaviour, and the app's Detect card has switches for the rotation search and the keypoint map.

Python

The same engine is available as plain Python functions:

import zcmir

r = zcmir.register(moving, fixed)                 # keypoints → robust fit → refine (homography)
r.H, r.score                                      # 3×3 moving → fixed (1-based pixels)
warped = r.warp(moving)
r = zcmir.register(moving, fixed, spline=True)    # + a cubic B-spline
s = zcmir.register_stack(moving_slices, fixed_slices)
s.save_tiff("registered.tif", moving_slices)

Images are numpy arrays, or file paths if Pillow is installed (pip install zcmir[images]). zcmir.settings() lists every option, and every function accepts them as keywords.

Tutorials

Three notebooks in examples/ work through one example pair, two Landsat 8 views of a river in flood and dry seasons, with a known pose:

Notebook What it shows
01_register Registering the pair, checking the result against the truth, refining from a rough guess, and a B-spline recovering a known bend
02_pos_gift POS-GIFT step by step: oriented phase congruency, keypoints on the pyramid, one descriptor drawn on its rings, the rotation search and the matches
03_smi SMI rebuilt in NumPy and checked against the engine: the 45 overlap sums, the score, the shift and roto-scale maps as FFT cross-correlations, and the Jacobian behind Refine's gradient

CI runs them on a GitHub M1 runner against the freshly built wheel. The executed notebooks, with every figure, are at https://zacharyvarley.github.io/zcmir/tutorials/ and attached to each release. To run them yourself, pip install zcmir matplotlib scipy jupytext, then open the .py files as notebooks or run python scripts/run_tutorials.py out/.

Building and contributing

The registration code is Zig and WGSL. It compiles to a native library (Vulkan, Metal or Direct3D 12 through wgpu-native) and to WebAssembly for the browser, so the app and the Python package run the same code. See CONTRIBUTING.md.

License

MIT (LICENSE). The wheels bundle wgpu-native (MIT / Apache-2.0). The example images keep their own terms, public domain (NIST, NASA / USGS) or CC BY 4.0 (sources).

References

  1. S. Heldmann, O. Mahnke, D. Potts, J. Modersitzki, B. Fischer. Fast computation of mutual information in a variational image registration approach. Bildverarbeitung für die Medizin (BVM) 2004, pp. 448–452. doi:10.1007/978-3-642-18536-6_92
  2. J. August, T. Kanade. The role of non-overlap in image registration. Information Processing in Medical Imaging (IPMI) 2005, LNCS 3565, pp. 713–724. doi:10.1007/11505730_59
  3. J. Öfverstedt, J. Lindblad, N. Sladoje. Fast computation of mutual information in the frequency domain with applications to global multimodal image alignment. Pattern Recognition Letters 159 (2022) 196–203. doi:10.1016/j.patrec.2022.05.022
  4. M. Rubeaux, J.-C. Nunes, L. Albera, M. Garreau. Medical image registration using Edgeworth-based approximation of mutual information. IRBM 35(3) (2014) 139–148. doi:10.1016/j.irbm.2013.12.004
  5. Z. T. Varley. Algorithms for Crystallography in the Scanning Electron Microscope. PhD thesis, Carnegie Mellon University, 2024.
  6. M. Sugiyama. Machine learning with squared-loss mutual information. Entropy 15(1) (2013) 80–112. doi:10.3390/e15010080
  7. Z. Hou, Y. Liu, L. Zhang. POS-GIFT: A geometric and intensity-invariant feature transformation for multimodal images. Information Fusion 102 (2024) 102027. doi:10.1016/j.inffus.2023.102027

Metadata

Release files for zcmir 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for zcmir 0.1.4
File
zcmir-0.1.4-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
zcmir-0.1.4-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
zcmir-0.1.4-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
zcmir-0.1.4-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
zcmir-0.1.4-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
zcmir-0.1.4-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 49.5 MB

Release files / zcmir-0.1.4-py3-none-win_arm64.whl

Download URL zcmir-0.1.4-py3-none-win_arm64.whl
Size 7.7 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
4994eb52eb0f05719bfdc2461f793c176cc894d8592a25b24896b69335d92614
BLAKE2b-256 checksum
How to use checksums
c0ce0a2642fef9d4fc48d27d5142291fee91980f0f94641ad7689538ea97492a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / zcmir-0.1.4-py3-none-win_amd64.whl

Download URL zcmir-0.1.4-py3-none-win_amd64.whl
Size 7.9 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
424ac67135d38e7c1afbd5fac6ea6aed6615b8652f27cdbea54477e6391c6204
BLAKE2b-256 checksum
How to use checksums
48c3a221c4305297f73a8d0c4ee37e6ed7f8841549b458d23c05739a917d8721
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / zcmir-0.1.4-py3-none-manylinux_2_28_x86_64.whl

Download URL zcmir-0.1.4-py3-none-manylinux_2_28_x86_64.whl
Size 9.7 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
c041436a6bf5586373473aa8f4b8a1493babb2c0d7916e5f12b8f4ec6aa049cd
BLAKE2b-256 checksum
How to use checksums
1b7162b3df7216733f8480e1901392bcfcebfa30e5ad8647ff59cf71f29f466d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / zcmir-0.1.4-py3-none-manylinux_2_28_aarch64.whl

Download URL zcmir-0.1.4-py3-none-manylinux_2_28_aarch64.whl
Size 9.7 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
6075d8b69fd248f708b1b69e88986bdbeb737e4372c107797c574b959ec29e36
BLAKE2b-256 checksum
How to use checksums
04f7d8e30f8c5345fa4edd8531f1e7d0d41df95e45f132decb99c7863ae156f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / zcmir-0.1.4-py3-none-macosx_11_0_x86_64.whl

Download URL zcmir-0.1.4-py3-none-macosx_11_0_x86_64.whl
Size 7.3 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
ef06916fe26833a6ffde15e2829582a93939d03a8bd766c04e9c0c29cce98d61
BLAKE2b-256 checksum
How to use checksums
63c39b45792c42876ace34776c4c9c962599e6a74b8db3e65ff04c43cba57cd9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / zcmir-0.1.4-py3-none-macosx_11_0_arm64.whl

Download URL zcmir-0.1.4-py3-none-macosx_11_0_arm64.whl
Size 7.2 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a9c49be6eedd610430c67a86602ae19b0be1d6dc6661a5d9ecebb7812dd1778f
BLAKE2b-256 checksum
How to use checksums
cb09bb1cafbe13cfd6028d2ebd4993676b711233bb886d5f9d1460036d27a953
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.4 This release

6 release files

0.1.3

6 release files

0.1.2

6 release files

0.1.1

6 release files

0.1.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page