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
cu12extra 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 addpip 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, thehybrid_3d2d-seed42architecture and contract (its own weights or another reader's of the same architecture) or a v8-in export. Hecate runs throughinkrs.hecateand 3D ink throughinkrs.ink3d. - On macOS, the villa readers, Hecate and the v8-in exports only.
inkrs.ink3dandtta=8raise 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logRelease 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 logRelease 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 logSecurity 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.
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 logA 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.