Skip to main content

inkrs (Python)

inkrs runs villa's flat ink readers on the GPU, from Python: hybrid_3d2d-seed42 and the readers with its architecture (reader-v2, Reader V3, ink_9um seed 43, dense_native, TRACE v3B and Rectina M1, M2 and M4). Give it the same input, layers, direction and stride as villa's vesuvius.ink_detection.inference.infer and it returns the uint8 maps that infer writes, or writes villa's TIFFs itself. inkrs.Engine also runs the v8-in exports, inkrs.hecate runs Hecate 9.6 µm and inkrs.ink3d runs villa's 3D ink models, each with the CLI's code. The model stays loaded on the GPU between calls, and Engine.sweep runs every depth and both directions of a segment in one pass.

Install

On Linux x86-64 or Windows x86-64:

pip install "inkrs[cu12]"

On an Apple silicon Mac, pip install inkrs (see macOS).

The Linux wheel is for CPython 3.9 and newer, on glibc 2.28 or newer. It doesn't need Rust or a CUDA toolkit, because the kernels ship inside it, compiled for the common NVIDIA GPUs and as PTX. You do need:

  • An NVIDIA GPU of compute capability 7.5 or above (T4 and RTX 20, RTX 30, 40 and 50, A100, H100) and a driver of r525 or newer.
  • cuDNN 9 and cuBLAS 12. The cu12 extra installs them from pip (nvidia-cudnn-cu12, nvidia-cublas-cu12). Without it, inkrs uses the copies already on the system, or the ones a CUDA 12 build of PyTorch installed. A CUDA 13 PyTorch ships cuBLAS 13 under a name inkrs doesn't look for, so there add pip install nvidia-cublas-cu12.

The wheel has three PTX tiers: sm_75 for Turing, sm_80 for every GPU from 8.0 up, and sm_120 (r570 or newer) for the RTX 50 series. The engine loads the highest tier the GPU runs, and INKRS_PTX_TIER=sm80 or sm75 forces a lower one. Each tier also ships compiled for the GPU families it serves: sm_75; sm_80, sm_86, sm_89, sm_90 and sm_100; and sm_120. On those GPUs the engine loads the compiled kernels and skips the driver's PTX compile, which takes over a second on a machine's first run. A GPU outside those families (compute capability 11.x), a tier forced below the GPU's own, or a driver that refuses the compiled kernels gets the PTX instead, the last with a warning on stderr. load_report["kernels"] says which loaded. inkrs.build_info() reports each tier and its minimum driver, and the families with compiled kernels. Hecate in bf16 and the 3D ink U-Net need 8.0 or above, so they're refused on Turing. It's tested on an RTX 5090 (every tier), a Tesla T4 (sm75), and an A100, an A10, an L4 and an RTX 4090 (sm80, the readers).

Which cuDNN and cuBLAS it loads

inkrs takes the system copies first (the linker path, LD_LIBRARY_PATH, or a copy already loaded, such as PyTorch's), then the pip packages. INKRS_CUDA_LIBS=pip loads the pip copies even when the system has its own, and INKRS_CUDA_LIBS=cudnn (or cublas) does that for one of them. The engine tries the unversioned name (libcublas.so) first, so a system copy under that name still wins; when it would, the forced load raises instead of running on the other copy. If a library is found nowhere, or a pip copy fails to load, inkrs.Engine raises inkrs.CudaLibraryError naming the package to install. inkrs.load_cuda_libraries() and eng.load_report["cuda_libraries"] show the file the engine calls for each.

Windows

The x86-64 Windows wheel is for CPython 3.9 and newer too. It imports only Python's stable ABI DLL and Windows system DLLs, but it hasn't been run on Windows itself yet.

There the libraries are cudnn64_9.dll and cublas64_12.dll, and the cu12 extra installs them as well. When the pip packages are installed, inkrs loads them from their bin directories before the engine starts, so they win over a CUDA toolkit on PATH, and INKRS_CUDA_LIBS=pip only makes them required. NVRTC, which cuDNN uses for some of its engines, loads from the nvidia-cuda-nvrtc-cu12 package's bin directory when that's installed. Without the pip packages, the engine looks in the executable's directory, System32, each absolute PATH entry, then %CUDA_PATH%\bin\x64 and %CUDA_PATH%\bin.

It never loads a library from the current directory. It raises inkrs.CudaLibraryError when a copy there or behind a relative PATH entry would load instead, or when a file it tries first, such as a cudnn.dll on PATH, would load a second copy beside the pip one. The CLI also takes the current directory and PATH out of its own DLL search at startup. The Python package can't do that for your interpreter, so a DLL written into the current directory after the engine's checks, under a name it tries first, could still load.

macOS

pip install inkrs

The arm64 wheel installs on any Apple silicon Mac, but the engine needs macOS 26 or newer, and on an older macOS it raises inkrs.UnsupportedError naming the version. There the engine runs on Metal. It needs no CUDA libraries and no Xcode, because the Metal shaders ship as source and compile when the engine opens. Pass device=0, "mps", "mps:0" or a torch MPS device; the engine uses the system's default GPU. algo does nothing there, and inkrs.build_info() reports "backend": "metal" and no PTX or compiled kernels. Only the M4 Pro has been tested.

Experimental, and reader-v2 only: the engine can run most batches' 2D network on the Neural Engine through Core ML while the GPU runs every batch's 3D stem. Build the Core ML bodies once with metal/coreml_body.py, set INKRS_COREML_ANE to the fp16 .mlmodelc it prints, and run at --batch-size 8 (batch_size=8). The engine refuses a body built from another checkpoint, batch or precision. The maps stay within villa's parity limits and are the same on every run, but differ from the GPU-only maps by up to a few 1/255 steps. src/metal/coreml.rs lists the tuning variables, including an opt-in CPU lane.

Build from source

pip builds the source distribution where no wheel fits, which needs Rust but no CUDA toolkit, since the kernels ship compiled and as PTX. From a checkout, python/build-wheel.sh builds the Linux x86-64 wheel (manylinux_2_28, through zig) or, on a Mac, the arm64 wheel, into target/wheels. It needs Rust and uv. On Windows, run maturin build --release in python/.

The package's version is the one in python/Cargo.toml, which CI keeps equal to the engine's in Cargo.toml. The Python code is linted with uvx ruff@0.17.4 check python.

Weights

inkrs reads a safetensors export of villa's checkpoint. Make it once, on the CPU, where villa and torch are installed:

python -m inkrs.export_weights step-075000.pth weights/

That writes weights/hybrid_3d2d-seed42.safetensors and its .json sidecar, and inkrs.Engine takes the .safetensors file directly.

It takes the .pth too. inkrs then looks for an export of those exact bytes (by SHA-256) in weights_dir (default $INKRS_WEIGHTS_DIR, else ./weights), then in ~/.cache/inkrs/weights/<sha256>/ (or under $XDG_CACHE_HOME, or $INKRS_CACHE_DIR; %LOCALAPPDATA%\inkrs\weights\<sha256>\ on Windows). If neither has one and torch and villa are installed, as in a villa environment, it exports the checkpoint into that cache once and reuses it from then on. Without them it raises inkrs.UnsupportedError with the command above. A checkpoint the exporter refuses also raises inkrs.UnsupportedError: one that isn't a flat model, has non-float32 weights, or whose keys or tensor shapes don't match the model its config builds. The message names the missing and unexpected keys.

Use

import inkrs

eng = inkrs.Engine("weights/hybrid_3d2d-seed42.safetensors", device=0)

# one villa `infer` call: --layer-start 25 --layer-end 42 --direction both
maps = eng.infer("segment.zarr", layer_start=25, layer_end=42, direction="both")
maps["forward"], maps["reverse"]          # uint8 [H, W]; forward is the blind side, reverse the reading side

# or write villa's files: out.tif and out_reverse.tif
eng.infer("segment.zarr", layer_start=25, layer_end=42, direction="both", output_tiff="out.tif")

# every depth d = -4..4, both directions, in one pass (layers from Z//2 + d - 8)
sweep = eng.sweep("segment.zarr", range(-4, 5))
sweep[(0, "reverse")]

# an in-memory volume: numpy or torch, uint8 [Z, H, W]; a C-contiguous array is read in place
maps = eng.infer(volume, layer_start=25, layer_end=42, occupancy="segment.zarr")

occupancy controls villa's low-resolution empty-tile skip. For a zarr path the default does what villa does. For an in-memory volume, pass the zarr group it came from (or one pyramid level as an array) to get villa's covered pixels. Pass nothing and only the per-tile skip runs, as villa does for a bare array.

eng.load_report and eng.last_report hold the load and per-call timings. The GIL is released while the engine loads and runs, and one call runs at a time per engine.

API.md has the full contract, including how it maps onto villa's infer.

Limits

Input, a model or a GPU outside these limits raises inkrs.UnsupportedError, a subclass of inkrs.InkrsError. So a caller with a PyTorch path can fall back on UnsupportedError and treat any other InkrsError as a failure.

  • In Engine, the hybrid_3d2d-seed42 architecture and contract (its own weights or another reader's of the same architecture) or a v8-in export. Hecate runs through inkrs.hecate and 3D ink through inkrs.ink3d.
  • On macOS, the villa readers, Hecate and the v8-in exports only. inkrs.ink3d and tta=8 raise there.
  • uint8 input only.
  • The Hann blend only.
  • The unpadded tile phase only.
  • A volume under 4 GiB, with both spatial dimensions at least 128. That's measured on the array passed to the call, so a 17-layer window can cover up to about 15,800 px square.
  • A GPU the embedded PTX serves, with a driver new enough to load it, or on macOS a GPU with Metal 4.

The maps match villa eager's to within 2/255, with the same covered pixels. Two processes can differ from each other by 1/255 on a few pixels when algo="find" (the default) times cuDNN's algorithms and picks differently. The timed picks are cached on disk (see API.md), so later processes on the same setup reuse them; algo="heuristic" makes the choice repeatable without the cache.

Either the Apache License, Version 2.0 or the MIT licence, at your option. The embedded kernels include NVIDIA libdevice code; see THIRD_PARTY_NOTICES.

Metadata

Release files for inkrs 0.2.1

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

Source distribution (sdist)

Source distribution for inkrs 0.2.1
File Size Uploaded Provenance Actions
inkrs-0.2.1.tar.gz 3.2 MB With provenance Details for inkrs-0.2.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for inkrs 0.2.1
File Interpreter ABI Platform Actions
inkrs-0.2.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details for inkrs-0.2.1-cp39-abi3-win_amd64.whl
inkrs-0.2.1-cp39-abi3-manylinux_2_28_x86_64.whl CPython 3.9 abi3 Linux glibc 2.28+ x86-64 Details for inkrs-0.2.1-cp39-abi3-manylinux_2_28_x86_64.whl
inkrs-0.2.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details for inkrs-0.2.1-cp39-abi3-macosx_11_0_arm64.whl

Total release size: 13.3 MB

Release files / inkrs-0.2.1.tar.gz

Download URL inkrs-0.2.1.tar.gz
Size 3.2 MB
Tags Source
SHA-256 checksum
How to use checksums
6ea7dbfb3bf63690daef68022ce355cc50a8730c5e019d58f0ed4bbea1b039a9
BLAKE2b-256 checksum
How to use checksums
20d95ed5c78e5893f93c4c81740342b53e7219d474f2db61c39af94f9c917862
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 Nov 13, 2026.

Transparency log

Release files / inkrs-0.2.1-cp39-abi3-win_amd64.whl

Download URL inkrs-0.2.1-cp39-abi3-win_amd64.whl
Size 4.5 MB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
6aa1334de2de2f1f1ad9b0c892dc04acba1982a8510effc6f63100f4cbca34b8
BLAKE2b-256 checksum
How to use checksums
aec73e03273901ed95dbe369aff9cfda20eb607d301c10d350f4d6fde8ce8685
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 Nov 13, 2026.

Transparency log

Release files / inkrs-0.2.1-cp39-abi3-manylinux_2_28_x86_64.whl

Download URL inkrs-0.2.1-cp39-abi3-manylinux_2_28_x86_64.whl
Size 4.5 MB
Tags CPython 3.9 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
e183b870800c146cb9232e529b2336766a45add8c763823b4e5122a3800c4a9d
BLAKE2b-256 checksum
How to use checksums
25f72dbeb34455ec90b43f21b7195e55e51fb0ce4e50539782d533179526015c
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 Nov 13, 2026.

Transparency log

Release files / inkrs-0.2.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL inkrs-0.2.1-cp39-abi3-macosx_11_0_arm64.whl
Size 1.1 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
374d96b87a1bcb4a64c599869f5b83d6d8758e7144becf5109b80eb9042b71aa
BLAKE2b-256 checksum
How to use checksums
35a562e9f759790b64c8589c8dc2e6043bf215116189781120cb66c34cc68e1d
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 Nov 13, 2026.

Transparency log

Security for inkrs 0.2.1

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.

Full provenance

All 4 files in this release have been uploaded with provenance metadata.

Select from 4 files

Attestation for inkrs-0.2.1.tar.gz

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 Nov 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.1 This release

4 release files Full provenance

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