NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #2835 most downloaded on PyPI
Low level implementations for computer vision in Rust
Last release 8 days ago
26 Sep 2026
Release timing varies
gaps range from 9 days to 6 months
Most releases are documented
notes for 16 of 22 stable releases
Nothing withdrawn
no release was ever pulled
5 years old
26 releases · first in 2022
One column per quarter.
The breaking changes, all described under 0.1.15 and its release candidates below:
0.2.0 is 0.1.15 re-released under the right version. 0.1.15 shipped breaking API changes as a
patch-level bump, so every crate depending on kornia-* = "0.1" picked them up on its next
cargo update and stopped compiling (Copper among them). 0.1.15 is yanked on crates.io; ^0.1
now resolves to 0.1.14 again. To get the 0.1.15 work, depend on "0.2".
The breaking changes, all described under 0.1.15 and its release candidates below:
Image/Tensor lost the compile-time allocator type parameter:Image<f32, 3, CpuAllocator> → Image<f32, 3>.Tensor::get_unchecked / TensorView::get_unchecked are unsafe fn; permute_axes,as_contiguous, TensorStorage::layout and the GStreamer *_pipeline_description helpers returnResult; several error enums gained variants.resize_native samples on the half-pixel grid (pixel values change); f32 remap zero-fillsStereoRectifier::rectify_left/right are into-style; left_map()/right_map() becameleft_maps()/right_maps().ReconstructionConfig is split out of CalibConfig.Also since 0.1.15:
KORNIA_VERSION_PATCH in the generated kornia/version.hpp is now always an integer.16-rc, which broke any C++ code using it as a number.KORNIA_VERSION_PRERELEASE ("rc.1", empty on aKORNIA_VERSION_IS_PRERELEASE (0/1) for #if checks.Full changelog: v0.1.15...v0.2.0
First stable 0.1.15. Everything in the 0.1.15-rc.* entries below ships here, plus the following changes since rc.5.
First stable 0.1.15. Everything in the 0.1.15-rc.* entries below ships here,
plus the following changes since rc.5.
Security hardening across the workspace. A repo-wide audit fixed
memory-safety bugs that untrusted input could reach, each with a regression
test; upgrading is recommended for anyone decoding untrusted images or files.
kornia-bow: out-of-bounds read and unbounded allocation from craftedkornia-tensor / kornia-image: shape and size arithmetic is checkedpermute_axes rejects non-permutations; sum_elements no longer writes outkornia-imgproc: out-of-bounds reads/writes in normalize_rgb_u8,warp_affine_u8/warp_perspective_u8 (CPU and the CUDA affine kernel),remap/interpolate_pixel, and the nearest/bicubic resize rowkornia-io: decoders cap declared images at 2^30 pixels and allocateresize_normalize_to_tensor, uninitialised YUV decoder output, read-only orout= arrays, and a DLPack import leak are fixed.pypi / crates-io environments,cargo audit job, and SECURITY.md with a private reportingBreaking (security-related): Tensor::get_unchecked /
TensorView::get_unchecked are now unsafe fn; permute_axes,
as_contiguous and TensorStorage::layout return Result; from_raw_parts
and TryFrom<Tensor3> for Image reject mismatched layouts; Arrow imports are
read-only; the GStreamer *_pipeline_description helpers return Result;
several error enums gained variants; f32 remap zero-fills out-of-range
samples.
Stereo rectification runs on CUDA, byte-exact with the CPU path.
StereoRectifier now resamples through the residency-dispatched remap_u8, and
StereoRectifier::to_cuda(&stream) returns a CudaStereoRectifier that keeps
both eyes' maps device-resident and rectifies device frames zero-copy via
rectify_left_device/rectify_right_device. The two
backends produce identical bytes by kornia-imgproc's tested CPU↔CUDA contract,
so demoting to CPU on a CUDA failure is lossless. Border semantics changed
deliberately with the unification: source coordinates in the [w-1, w) band now
clamp-sample the edge texel (previously black; exactly w-1 sampled the edge under
both), and blending is Q10 fixed point.
Breaking: StereoRectifier::rectify_left/right are now into-style —
(&self, src, &mut dst) -> Result<(), StereoError> — matching imgproc's into-style convention;
left_map()/right_map() became left_maps()/right_maps() returning the x/y
planes remap_u8 consumes directly.
CUDA and NEON SIFT, bit-exact with cv::SIFT. Scale space, detection,
orientation and descriptors reproduce OpenCV bit-for-bit on CPU (NEON) and
CUDA, with deterministic keypoint and matcher output order; the vertical blur
is register-tiled and descriptor atomics are warp-aggregated.
kornia-calib: multi-camera extrinsic calibration. New crate calibrating a
rig from an AprilTag grid plus optional natural-feature tracks via bundle
adjustment, now publishable (planar pose moved to
kornia_3d::pose::planar). It returns the full reconstruction, bundle-adjusts
during incremental growth, picks the bootstrap pair on geometry, filters
outlier observations, and supports depth/orientation/motion priors and a
sequential() preset for video capture. Breaking:
ReconstructionConfig is split out of CalibConfig.
Bundle adjustment and pose. The Schur BA gains intrinsics (radial term,
free focal and k1), a block-sparse reduced camera system, orientation and
constant-velocity priors, and per-phase instrumentation; PnP refinement
supports robust (Huber) losses, and RANSAC gains SPRT early rejection
(use_sprt in Python). Sim3::exp/log are now true inverses.
CUDA remap and new filters. remap (f32 and u8) dispatches to CUDA when
images and maps are device-resident; Laplacian and integral-image filters run
on CPU and GPU; OpenCV parity checks now cover resize, warp-affine and
morphology on CUDA.
Tensors and memory. Element-wise and reduction ops dispatch on
MemoryDomain (CPU/GPU); UnifiedResource / CudaUnifiedAllocator add CUDA
managed memory and PinnedWcAllocator write-combined pinned host memory for
CPU→GPU streaming; tensors and images can borrow host buffers zero-copy.
I/O. RVL gains its run-length phase (5.7× instead of 3.0× compression on
live depth), a temporal-delta variant (RVLD) and a slice encoder; zero-copy
slice encoders for turbojpeg RGB and gray16 PNG.
Python. Windows ARM64 wheels; histogram, colour-check and pyramid APIs
filled in.
Full changelog: v0.1.14...v0.1.15
Connected components 3-5x faster on CPU, ~2x on GPU. The CPU path now indexes its union-find by run id instead of pixel index (tables stay cache-resid
Connected components 3-5x faster on CPU, ~2x on GPU. The CPU path
now indexes its union-find by run id instead of pixel index (tables stay
cache-resident rather than costing two full-image allocations per call)
and scans rows with u64 word tricks that skip 8 background/foreground
pixels per step; the CUDA path replaces the serial 1-thread-per-row init
with a block-parallel ballot/shared-scan run-start kernel, a
warp-shuffle compaction scan, and a u16 rank array. 1080p,
stream-synchronized, labels still exactly equal to cv2's CCL_WU
(SAUF): CPU 4.9-6.1 ms → 0.67-1.40 ms — now faster than OpenCV's
default (Spaghetti) algorithm on every tested content/connectivity
(1.1-2.2x); GPU 2.5-3.0 ms → 1.09-1.60 ms — faster than or tied with
cv2's default except many-small-components 8-connectivity content
(0.82x), where Spaghetti's specialized decision tree still wins against
the fixed 5-kernel pipeline cost. VPI has no CCL op.
CUDA module cleanup + faster pyramid borders. The per-module CUDA
error enums and check_slice/get_kernel helpers of the newer kernel
families (histogram, CLAHE, bilateral, median, Canny, CCL) are now
generated from one shared definition (public error types unchanged), and
the pyramid kernels use a single-fold reflect_101 variant for their
±2-tap borders instead of the integer-modulo form — 1080p u8 pyrdown
0.62 → 0.44 ms, bit-exactness pinned by the existing parity suites.
cuda-fusion example + docs. New runnable example
(examples/cuda_fusion) showing the kernel-fusion API (build/exec model
borrowed from the Fused Kernel Library; scoped to linear per-pixel
transform chains — documented explicitly as narrower than FKL):
composing the DNN-preprocess chain and a novel resize→normalize→gray
chain from the stage library, printing the generated CUDA source, and a
sustained benchmark (~0.13 ms/frame, 1080p → 640×640 CHW on Orin). The
README documents pipeline composition, batching, the custom-FusedStage
recipe, and the f32-intermediate precision contract.
Connected-component labeling (CPU and CUDA), label-exact with OpenCV's
SAUF. New connected_components for u8 single-channel masks
(kornia_rs.imgproc.connected_components in Python, returning
(n, labels) with int32 labels), matching
cv2.connectedComponentsWithAlgorithm(..., cv2.CCL_WU) label-for-label:
background 0, components numbered in raster order of first appearance,
for both 4- and 8-connectivity. (cv2's default 8-way algorithm, BBDT,
produces the same partition with a different numbering.) The CPU path is
a run-based min-index union-find; the CUDA path is an atomicMin
label-equivalence fixpoint with row-run initialization plus a device
compaction — device labels are identical to the CPU's; the union phase
is a single lock-free pass (Komura atomicMin-with-retry union, atomic
path halving), no iteration loop. Python Image gains int32 (label map)
dtype support. Measured 1080p: GPU 4.3 ms vs cv2 5.8 ms on dense 40%
noise (1.3×); sparse-blob content is fixed-overhead-bound (task #38
tracks the block-local rewrite). CPU is a stripe-parallel run-based
union-find (~22 ms dense; SAUF two-pass is the same follow-up).
Canny edge detection (CPU and CUDA), byte-for-byte with OpenCV. New
canny for u8 single-channel images (kornia_rs.imgproc.canny in
Python), matching cv2.Canny exactly — the all-integer pipeline (Sobel
3×3 CV_16S replicate-border gradients, L1/L2 magnitude with cv2's
threshold clamp-and-square rules, the fixed-point TG22 sector test with
cv2's exact per-sector tie-breaks, and hysteresis as pure reachability)
transcribes cv2's semantics, so CPU, GPU and cv2 agree on every byte.
CPU is NEON-vectorized end to end (separable Sobel, magnitude, a
branchless 4-lane NMS evaluating all three sector tests and selecting by
mask, map finalize) with hysteresis as a parallel tile-worklist flood
(round 1 floods from strong seeds, woken rounds re-scan only tile
boundary rings); the CUDA path runs fused sobel+magnitude, NMS, an
active-tile-worklist block-fixpoint hysteresis (blocks read-and-clear
their own worklist entries — no per-sweep buffer resets) and finalize.
(VPI's CUDA Canny uses a different algorithm — ~5% differing pixels vs
cv2, and its L2 threshold semantics diverge from the standard convention
— so it is not byte-comparable.) Measured 1080p under load: GPU
1.6–4.3 ms ≈ 2.1–2.6× cv2.Canny CPU; kornia CPU 3.4–5.7 ms ≈ 1.2–1.6×
cv2.
Median blur + bilateral filter (CPU and CUDA), byte-for-byte with
OpenCV — median also bit-identical to VPI. New median_blur (u8, 3×3
and 5×5, replicate borders, exact sorting-network medians — NEON on CPU,
register networks on CUDA; cv2.medianBlur and VPI's CUDA MedianFilter
agree bit-for-bit here, so kornia matches BOTH) and bilateral_filter
(u8 C1, cv2.bilateralFilter mirrored end to end: its own SIMD exp
polynomial for the color table, circular taps, reflect_101 borders, fma
accumulation — including cv2's different tap-summation order between its
16-pixel SIMD region and its scalar row tail; VPI's bilateral uses a
different formula and is not byte-comparable). CPU and GPU outputs are
byte-identical. The CPU paths are NEON-vectorized — 3×3 median via the
exact med3(max-lows, med-mids, min-highs) column identity with rolling
vext-shared column sorts and two output rows per pass; 5×5 via rolling
sorted columns feeding a 71-exchange selection network (derived from
Smith's 99 by greedy deletion, proven exact on the sorted-column input
subspace via the zero-one principle — pinned by a unit test over all 6^5
column-sorted 0-1 patterns); bilateral 16 lanes per tap — and beat
OpenCV's CPU on 5×5 median (1.15×) and bilateral (1.1×), with 3×3 median
at 0.77× (both sides at the DRAM wall). out= supported on the CPU
paths. Measured 1080p sustained (GPU, stream-synchronized): median3 0.31 ms
(1.1× cv2, 2.3× VPI), median5 1.18 ms (1.5× cv2, 1.1× VPI), bilateral
d=5 3.24 ms (2.9× cv2; VPI's non-comparable variant is faster).
CLAHE (CPU and CUDA), byte-for-byte with OpenCV. New clahe for u8
single-channel images (kornia_rs.imgproc.clahe in Python), matching
cv2.createCLAHE(clip, grid).apply() exactly — including OpenCV's
reflect_101 tile padding rule, integer clip limit with two-phase excess
redistribution, f32 LUT quantization (round-half-to-even) and the f32
bilinear blend of the four surrounding tile LUTs with the exact FMA
contraction OpenCV's aarch64 wheels compile to (calibrated empirically:
22 configurations, zero differing bytes). CPU and GPU outputs are
byte-identical (f32::mul_add / explicit fmaf, mirrored expression
trees). The CPU interpolation stage is NEON-vectorized with packed
per-span LUT tables (one gather per pixel; identical bytes, pure
access-pattern change). The Python binding takes out= on the CPU path.
Measured 1080p sustained (stream-synchronized): GPU 0.54 ms ≈ 7.1× and
CPU 1.9 ms ≈ 2.1× cv2.createCLAHE CPU (VPI has no CLAHE op).
GPU histogram + equalize_hist (CPU and CUDA), byte-for-byte with
OpenCV. New equalize_hist for u8 single-channel images on CPU and
device (kornia_rs.imgproc.equalize_hist in Python), matching
cv2.equalizeHist exactly — including its f32 round-half-to-even LUT
rounding and identity behavior on constant images. compute_histogram
now routes device images to a CUDA shared-memory-bins kernel whose counts
are exactly equal to the CPU's for any bin count. CPU and GPU equalize
outputs are byte-identical.
GPU Gaussian pyramids + PyramidPlan. pyrdown_f32 (one fused
5×5-Gaussian+subsample kernel), pyrup_f32 (polyphase pair), pyrdown_u8
and pyrup_u8 (separable [1,4,6,4,1] integer pairs) now route device
images to CUDA kernels that are bit-identical (f32) / byte-identical (u8)
to the CPU paths, including the reflect_101 borders and 1-pixel-wide
degenerate sources. PyramidPlan preallocates every level buffer so a
steady-state pyramid build launches kernels only (CUDA-Graph-capturable);
its levels are bit-identical to build_pyramid. Measured 1080p: pyrdown
u8 RGB 0.62 ms (4.1× OpenCV CPU), 4-level f32 plan 0.68 ms.
Faster Lanczos-3 warps on CUDA; sub-ULP output change in Lanczos warp/remap
sampling. The warp kernels now derive all six tap weights per axis from four
sin(πx) evaluations instead of twelve, using the sinc lattice's periodicity
(sin(π(frac+n)) = ±sin(π·frac)); the CPU sampler (lanczos_sample, used by
warp_affine/warp_perspective/remap with InterpolationMode::Lanczos) is
refactored identically, so CPU and GPU remain bit-exact to each other. Absolute
values shift by ≤1e-6 relative to previous releases (weights are identical in
real arithmetic, rounding differs). Measured on Jetson AGX Orin (locked
clocks, 1080p→1080p 3-channel f32): warp-affine lanczos 5.49→4.26 ms,
warp-perspective lanczos 5.93→4.28 ms sustained.
GPU separable filters: gaussian, box, sobel, scharr. New CUDA separable
engine (cuda/filter.rs) with residency dispatch on gaussian_blur,
box_blur, sobel, scharr (f32) and gaussian_blur_u8, box_blur_u8 —
f32 paths bit-identical to the CPU engine (skip-zero border, sequential
taps, fmad-free), u8 paths byte-identical to the Q8 striped blur (replicate
borders) and the 3×3 binomial fast path, with the u8 kernel/sigma routing
decided by one selector shared with the CPU. Python gaussian_blur /
box_blur accept device images; new imgproc.sobel (f32, numpy + device).
Measured 1080p vs OpenCV CPU: gaussian 5×5 u8 RGB 9.2×, box 5×5 9.5×,
sobel-3 f32 13×. The u8 binomial tails/edges now use the same nested
halving-add rounding as the SIMD bulk (≤1 LSB shift on border bytes vs
previous releases).
u8 color conversions are now byte-for-byte with OpenCV. Three alignments
(CPU and CUDA changed together, so device output stays assert_eq!-equal to
host): u8 grayscale now uses cv2's exact Q14 formula
((4899·R + 9617·G + 1868·B + 8192) >> 14; ±1 LSB vs previous releases);
yuv_from_rgb/rgb_from_yuv switch to the classic Y'UV chroma constants
cv2 and kornia-python use (0.492/0.877 forward, 1.140/2.032/−0.395/−0.581
inverse) instead of Cb/Cr in YUV order — a semantic change for chroma
values; and Bayer demosaic adopts cv2's border rule (the 1-px frame is the
interior neighbour's result). Verified byte-exact against cv2 for
yuv (both directions), bayer (all pixels), ycbcr, bgr, rgba and colormap;
gray matches cv2's documented formula exactly (cv2's own NEON build deviates
from its spec on ~0.25% of pixels). BGR/YCbCr/colormap already matched.
Python: every color conversion with a CUDA kernel now accepts a device
Image. Previously ~10 GPU color paths existed in the Rust crates but the
Python surface raised "no GPU kernel for a device Image": HLS, Luv, XYZ,
linear-RGB (f32 pairs), planar YUV (u8 and f32 pairs), plus the f32 arms
of YCbCr, sepia and gray (imgproc.gray_from_rgb on an f32 device image now
returns a device gray f32 image). u8 arms stay bit-exact with the CPU path;
f32 arms follow the existing tolerance contract. Covered by device-vs-CPU
parity tests and a memory-leak loop over the new arms.
u8 CUDA morphology (dilate / erode), bit-identical to the CPU ops, plus
first-time Python bindings. dilate / erode route u8 device images
(1/3/4-channel) to a CUDA kernel that samples the exact tap multiset the CPU
does — same active structuring-element offsets, same border index mapping
for all five padding modes — so min/max output is byte-for-byte equal
(assert_eq! parity tests over box/cross/ellipse and even-sized kernels).
The structuring element is cached on-device per geometry (Jetson pageable
H2D tail; warm path is CUDA-Graph-capturable). New
kornia_rs.imgproc.dilate / erode Python functions cover the numpy u8 CPU
path and u8 device images alike. Non-u8 device pairs error with the typed
no-GPU-kernel message instead of touching device memory from the CPU.
u8 CUDA warps (bilinear), bit-identical to the CPU fast paths — which are
now bit-identical to each other too. warp_affine_u8 and
warp_perspective_u8 route u8 device images (1/3/4-channel) to new
integer-only CUDA kernels that mirror the CPU's per-row span math and
Q16/Q10 fixed-point sampling exactly; kornia_rs.imgproc.warp_affine /
warp_perspective accept u8 device images including out=. Along the way
the CPU u8 perspective warp switched from incremental coordinate updates
(nx += dnx per column, with a SIMD reciprocal estimate) to direct
per-column evaluation with exact IEEE division — scalar, NEON, and AVX2 now
produce identical bytes instead of agreeing "up to sub-ULP noise". Output
shifts by at most one Q10 quantum on some pixels relative to earlier
releases; benchmarks show no measurable CPU cost (the division latency hides
behind the 4-lane structure).
u8 CUDA resize — every mode, bit-identical to the CPU fast paths. Camera
data is u8; until now the GPU only resized f32 (4× the memory traffic once you
count the conversion). resize_fast_u8_aa (Rust) and kornia_rs.imgproc.resize
(Python, including out=) now route u8 device images — 1/3/4-channel — to new
integer-only CUDA kernels covering the full CPU cascade: exact-2× pyramid fast
paths, nearest, Q14 bilinear, and antialiased/plain separable bicubic and
Lanczos-3. The coordinate and weight tables are built by the same host
functions the CPU uses and uploaded once per geometry (a device-side LUT
cache — Jetson pageable H2D uploads have a ~250 µs average latency tail that
otherwise dominates, and the warm path is CUDA-Graph-capturable), so device
output is byte-for-byte equal to host output (asserted with assert_eq!
across modes, channel counts, odd sizes, and extreme downscales). Measured on
Jetson Orin at 1080p→720p u8 RGB (median, out= reuse): nearest 0.22 ms,
bilinear 0.38 ms, bicubic 1.20 ms — faster than VPI-CUDA on every comparable
op (5.4× / 3.4× / 1.1×) and 3–6× ahead of OpenCV CPU. Kernel compile failures
now surface as errors instead of panics across all resize launchers.
Python: out= and CUDA Graphs for allocation-free frame loops. The device
geometry ops accept a preallocated device out= image (torch-style, returned
back); kornia_rs.cuda.Stream.new() creates a capturable non-default stream;
and kornia_rs.cuda.Graph.capture(f, retain, stream) records an
allocation-free op sequence for microsecond-overhead replay(). Measured on
Jetson Orin (1080p→720p f32 resize, per frame with sync): 1.31 ms → 0.62 ms
with out= on a created stream → 0.42 ms with graph replay — 20× the
naive pattern of two days ago, byte-exact output preserved through capture.
Jetson/CUDA: per-call device allocations no longer stall frame loops. The
default CUDA memory pool releases freed blocks back to the OS at every stream
synchronization (release threshold 0), so a per-frame op(); sync() pattern
re-paid the full allocation cost each frame — ~8.5 ms/frame of alloc/free churn
for 1080p f32 on Jetson Orin. kornia-tensor now raises the pool's release
threshold once per device at first allocation: the same loop drops to 1.3
ms/frame (and pipelined use improves 1.08 → 0.60 ms). Applies to every device
op (color conversions included).
Bicubic and Lanczos-3 for the f32 geometry ops, byte-exact CPU↔GPU.
resize, warp_affine, warp_perspective, and remap accept
InterpolationMode::Bicubic and ::Lanczos on the CPU, and device images
route to the previously launcher-only CUDA kernels — all six now reachable
from the public API, with bit-identical output between backends. Lanczos
weights use a shared deterministic sin(πx) polynomial (libm/CUDA sinf
rounding differs between platforms); resize-lanczos builds its per-axis weight
tables on the host once per call, shared verbatim with the GPU launcher —
which also made the GPU kernels up to 27% faster than the old __sinf path.
Hot-loop interpolation dispatch is hoisted per mode, making CPU f32 resize
~30% faster and warp-perspective ~7% faster than before.
Python: GPU resize and warps. kornia_rs.imgproc.resize / warp_affine /
warp_perspective now accept a device Image (f32, 3-channel) and run the
CUDA kernels, returning a device Image — output bit-identical to the CPU f32
path. numpy u8 inputs behave exactly as before; a device image with an
unsupported dtype/channel count raises instead of silently copying to the
host. out= is unsupported on the device path.
resize_native is renamed to resize (clean rename, no alias).
GPU resize and warps from the high-level API. resize (né resize_native),
warp_affine, and warp_perspective now run on the GPU when called with
device-resident images (Image::to_cuda / zeros_cuda), with bit-identical
output to the CPU path. Same rules as the color conversions: both operands
must be on the same device (mixed pairs are a typed error, no implicit
transfers), and unsupported dtype/channel combinations error instead of
silently falling back (the GPU kernels are 3-channel f32).
OpenCV-compatible resize (opt-in). New resize_opencv_u8 / resize_opencv_f32
reproduce OpenCV's reference INTER_LINEAR (u8: 11-bit fixed point; f32:
separable float) and floor-based INTER_NEAREST semantics, for migrating from
or cross-validating against cv2 pipelines. Validated against 72 reference
vectors generated by cv2.resize on Jetson Orin. Notable empirical finding
encoded in the tests: a shipped cv2 wheel is not internally byte-consistent for
INTER_LINEAR — the aarch64 wheel routes different channel counts and scales
through different backends (OpenCV, Carotene, Arm KleidiCV) whose bits disagree
with each other and with OpenCV's own reference arithmetic (u8 up to 2 LSB, f32
up to 3 ulp), so the tests assert exact bytes for nearest and a measured ≤2 LSB
/ ≤4 ulp corridor for linear; our reference arithmetic itself is pinned
byte-exactly by unit vectors.
CPU and CUDA warp-affine / warp-perspective are now byte-exact (f32,
bilinear + nearest): the CPU evaluates the source coordinate per pixel with the
same expression tree as the kernels (no more per-row accumulation drift), the
perspective kernels divide by w directly instead of multiplying by a
reciprocal, and the interpolation expression shapes match — parity tests assert
f32::to_bits equality on rotations, shears, and projective homographies.
Fixes a real GPU artifact along the way: cos(90°) is an ulp of noise rather
than zero, and the old strict border test zero-filled edge pixels of exact
right-angle rotations (splitting rows into half-warped, half-black); validity
of a near-constant axis is now judged on its row constant on both CPU and GPU.
No measured throughput change on Jetson Orin (CPU and GPU within run-to-run
jitter).
CPU and CUDA resize are now byte-exact. NVRTC kernels compile with
--fmad=false (no automatic FP contraction), and the resize coordinate and
bilinear arithmetic use the identical expression shapes on both sides, so
resize_native and the CUDA resize kernels produce bit-identical f32 output —
asserted by parity tests across dyadic and non-dyadic sizes. Kernels that want
fused multiply-adds keep them via explicit fmaf() (unaffected by the flag);
no measured throughput change on Jetson Orin. The bilinear_interpolation
restructure (weights formed before the tap products) shifts f32 bilinear
output by up to 1 ulp in every consumer — warp_perspective, remap, and
the optical-flow border path, not just resize.
BREAKING — resize_native now samples on the half-pixel grid. The f32
resize previously used align-corners (sx = x * (src-1)/(dst-1)); it now uses
the pixel-center convention (sx = (x + 0.5) * src/dst - 0.5) shared by
OpenCV, Pillow, ONNX Resize (half_pixel default), PyTorch
(align_corners=False), and NVIDIA VPI. Pixel values change for every
non-identity resize — typically a sub-pixel content shift. The u8 fast paths
(resize_fast_*) are unaffected. This also makes the CPU and CUDA resize
sample the same grid (resize_native was the sole align-corners holdout —
the fused CPU preprocess path and the CUDA kernels already sampled
half-pixel), and CPU/GPU parity is now tested.
kornia_imgproc::cuda::resize: launchers take a new PixelMappingHalfPixel — the default convention — or AlignCorners toGPU/CUDA wheels. pip install --pre kornia-rs now ships the kornia_rs.cuda module on Linux. Universal wheel: runs on CPU when no GPU , activates CUDA a
GPU/CUDA wheels. pip install --pre kornia-rs now ships the kornia_rs.cuda module on Linux. Universal wheel: runs on CPU when no GPU, activates CUDA at runtime when an NVIDIA driver + nvrtc are present. See the README GPU/CUDA section for the API + runtime requirements.
Verified on Jetson Orin: kr.cuda.is_available() → True, GPU gray_from_rgb matches CPU exactly.
Install
pip install --pre kornia-rs
cargo add kornia-imgproc@0.1.15-rc.4
Full changelog: v0.1.15-rc.3...v0.1.15-rc.4
GPU wheels. Linux wheels now build with --features cuda, so
pip install --pre kornia-rs ships the kornia_rs.cuda module (GPU color
conversions, CudaImage/CudaPreprocessor, zero-copy DLPack). The wheel loads
CUDA lazily (cudarc fallback-dynamic-loading): it runs on CPU when no GPU is
present and activates CUDA at runtime when an NVIDIA driver + nvrtc are
available. Same crate/library contents as rc.3 otherwise.
python_release.yml: linux job builds --features cuda (macOS/Windows stay
CPU-only — no NVIDIA hardware).upload → op → download
snippet and the runtime requirements.Full changelog: v0.1.15-rc.3...v0.1.15-rc.4
Try it: cargo add kornia-imgproc@0.1.15-rc.3 · pip install --pre kornia-rs
Two fixes on top of rc.2:
Try it: cargo add kornia-imgproc@0.1.15-rc.3 · pip install --pre kornia-rs
Full changelog: v0.1.15-rc.2...v0.1.15-rc.3
Two fixes on top of rc.2 (which brought the crates.io publish):
threshold_split,
default 0.33) so small / glary / low-contrast Tag36H11 markers decode instead
of returning zero detections; the tag's black border no longer merges with
adjacent dark regions (#985).CudaTexObject
RAII wrapper with CU_TR_ADDRESS_MODE_BORDER replaces the per-pixel OOB
bounds-check branch, cutting warp divergence on rotated images (~30–40% of
output pixels map outside the source); optional block_dim tuning (#979).Full changelog: v0.1.15-rc.2...v0.1.15-rc.3
First pre-release since 0.1.14. Try before stable 0.1.15: cargo add kornia-imgproc@0.1.15-rc.1 · pip install --pre kornia-rs
First pre-release since 0.1.14. Try before stable 0.1.15: cargo add kornia-imgproc@0.1.15-rc.1 · pip install --pre kornia-rs
kornia_rs.cuda Python module — GPU color conv + fused preprocess, pinned-staging, zero-copy DLPack import into torch/TensorRT (#969, #970, #972)CudaResource/CudaAllocator, Tensor::to_cuda, CudaKernel (#950)Tensor/Image + kornia-py, bidirectional with torch (#951)Tensor/Image; runtime AllocHandle + host-default constructors (#955). Migration: Image<f32, 3, CpuAllocator> → Image<f32, 3>.Full changelog: v0.1.14...v0.1.15-rc.1
First pre-release since 0.1.14 (2026-05-19), bundling ~247 commits. Headline is GPU acceleration landing across the stack plus a CPU warp-affine rewrite.
kornia_rs.cuda Python module — GPU color conversions + fused camera
preprocess from Python (#969), with a pinned-staging preprocessor and
zero-copy DLPack import into torch / TensorRT (#970, #972).CudaResource / CudaAllocator,
Tensor::to_cuda, CudaKernel (#950).Tensor / Image and kornia-py — zero-copy,
bidirectional with torch (#951).Tensor / Image;
runtime AllocHandle + host-default constructors (#955). Migration:
Image<f32, 3, CpuAllocator> → Image<f32, 3>.AllocationFailed error variant preserving the backend message.Full changelog: v0.1.14...v0.1.15-rc.1
The headline: turbojpeg is finally reachable from the Python API.
The headline: turbojpeg is finally reachable from the Python API.
In 0.1.13 we shipped wheels with libjpeg-turbo compiled into the binary, but the Python-facing symbols (kr.io.encode_image_jpegturbo, decode_image_jpegturbo, read_image_jpegturbo, write_image_jpegturbo, ImageEncoder, ImageDecoder) were silently absent because the #[cfg(feature = "turbojpeg")] gates in kornia-py/src/*.rs check kornia-py's own feature flag, not the kornia-io dep's. We had set the dep flag but never activated kornia-py's. 0.1.14 fixes that with default = ["turbojpeg"], so the gates fire on every default build.
Verification: the kornia-py Python test suite that asserts these symbols exist (e.g. test_decode_image_jpegturbo, test_compress_decompress, test_write_read_jpegturbo) is now passing on Linux + macOS + Windows.
windows-latest GitHub runners no longer ship Visual Studio 2022 (only 2026 Preview), which broke cmake auto-detection. Switched to CMAKE_GENERATOR=Ninja + ilammy/msvc-dev-cmd so the libjpeg-turbo build is VS-version-agnostic.features = ["turbojpeg"] on the kornia-io dep line. Now --no-default-features actually skips the libjpeg-turbo cmake build (was previously always forced on).import kornia_rs as kr
# Fast JPEG encode (libjpeg-turbo SIMD path)
enc = kr.io.ImageEncoder()
jpeg_bytes = enc.encode(img, quality=85)
# Fast JPEG decode
dec = kr.io.ImageDecoder()
arr = dec.decode(jpeg_bytes)The pure-Rust jpeg-encoder fallback is still used when libjpeg-turbo init fails, so building with --no-default-features continues to work.
Full diff: v0.1.13...v0.1.14
turbojpeg is finally reachable from the Python API. 0.1.13 shipped wheels with
libjpeg-turbo compiled in, but the Python-facing symbols were silently absent
because the #[cfg(feature = "turbojpeg")] gates checked kornia-py's own flag,
not the kornia-io dep's. Fixed with default = ["turbojpeg"]. Also: Windows CI
switched to CMAKE_GENERATOR=Ninja + ilammy/msvc-dev-cmd (VS-version-agnostic).
Full changelog: v0.1.13...v0.1.14
turbojpeg enabled by default in published wheels (2-4× faster JPEG encode/decode vs pure-Rust fallback)
CMAKE_GENERATOR=Visual Studio 17 2022 to prevent cmake auto-selecting VS 2026 preview#[cfg(feature = "turbojpeg")] — pure-Rust fallback still works for source builds without cmakeCMAKE_GENERATOR=Visual Studio 17 2022).#[cfg(feature = "turbojpeg")]; pure-Rust
fallback still works for source builds without cmake.Full changelog: v0.1.12...v0.1.13
Schur-complement bundle adjustment — dense reduced camera system ( k3d.bundle_adjust_schur )
k3d.bundle_adjust_schur)k3d.pose_graph_optimize)k3d.bundle_adjust)solve_pnp_ransac moved into k3d namespacekornia_rs.segmentation, kornia_rs.depth)depth.sample_depth — sample depth under segmentation masks (median or mean, parallel via rayon)
kornia_rs.imgproc.apply_colormap, Image.colormap())vqtbl4q_u8, 16 px/iter)kornia_rs.io)encode_image_rvl / decode_image_rvl — lossless Run-Length Variable depth compressionwrite_image_rvl / read_image_rvl — file IORVL1 magic + width/height headerkornia_rs.io, Image)encode_image_png_{rgb8,rgba8,gray8,rgb16,rgba16,gray16} — symmetric in-memory PNG encodeImage class natively holds uint16 (depth maps)Image.save(fp) accepts path or file-like (BytesIO, S3 objects)Image.encode(format) — in-memory bytes for JPEG and PNGpip install --upgrade kornia-rssolve_pnp_ransac → k3d.depth.sample_depth (19× vs Python).Full changelog: v0.1.11...v0.1.12
First release in 6 months (since v0.1.10, Nov 2025), bundling 122 commits across the Rust core, Python bindings, and C++ surface.
First release in 6 months (since v0.1.10, Nov 2025), bundling 122 commits across the Rust core, Python bindings, and C++ surface.
Full Changelog: v0.1.10...v0.1.11
First release in 6 months, 122 commits. New crates: kornia-vlm, kornia-pnp, kornia-lie, kornia-nn, kornia-apriltag, kornia-algebra, v4l2 camera capture. Python: PIL-parity Image API with zero-copy numpy, u16/f32 dtypes, WebP/TIFF, AprilTag bindings, Python 3.14t free-threaded builds.
Full changelog: v0.1.10...v0.1.11
Use std::black_box instead of criterion::black_box because it’s deprecated by @AS1100K in #381
Image by @AS1100K in #373kornia-lie crate on top of glam-rs by @andrew-shc in #303std::hint::black_box instead of criterion::black_box because it’s deprecated by @AS1100K in #381kornia-nn by @edgarriba in #386draw_polygon and draw_filled_polygon functions. by @TomatoChocolate12 in #353candle version into kornia-vlm by @andrew-shc in #397license-file from smol_vlm example by @AS1100K in #424TagFamilyKind::all method and use it in DecodeTagsConfig by @AS1100K in #433apriltag-imgs submodule URL by @AS1100K in #440Full Changelog: v0.1.9...v0.1.10
Video reader, custom Allocator for Image, kornia-lie on glam-rs, SIMD DNN
linear layer / kornia-nn, kornia-apriltag, zero-copy gstreamer images. See the
GitHub release for
the full per-PR list.
bump version 0.1.9-rc1 by @edgarriba in https://github.com/kornia/kornia-rs/pull/211
dog_response; improve doc of gaussian_blur by @Force1ess in https://github.com/kornia/kornia-rs/pull/261tensor-ops by @aayushhzz in https://github.com/kornia/kornia-rs/pull/272spatial_gradient by @Puellaquae in https://github.com/kornia/kornia-rs/pull/260harris_response by @andrew-shc in https://github.com/kornia/kornia-rs/pull/267kornia-io docs not building on docs.rs by @AS1100K in https://github.com/kornia/kornia-rs/pull/328Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.8...v0.1.9
bump version to 0.1.7 by @edgarriba in https://github.com/kornia/kornia-rs/pull/165
kornia-3d crate with ply reader and rerun example by @edgarriba in https://github.com/kornia/kornia-rs/pull/177kornia3d::pose for homography and affine by @edgarriba in https://github.com/kornia/kornia-rs/pull/194separable_filter regression by @edgarriba in https://github.com/kornia/kornia-rs/pull/201Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.7...v0.1.8
Deprecate ndarray phase1 by @edgarriba in https://github.com/kornia/kornia-rs/pull/114
impl AsRef<Path> for JPEG functions by @liamkinne in https://github.com/kornia/kornia-rs/pull/111imgproc functionality by @edgarriba in https://github.com/kornia/kornia-rs/pull/152draw_line in kornia-improc by @edgarriba in https://github.com/kornia/kornia-rs/pull/158Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.5...v0.1.7
fix webcam videorate by @edgarriba in https://github.com/kornia/kornia-rs/pull/71
kornia_rs::Interpolation module by @edgarriba in https://github.com/kornia/kornia-rs/pull/79Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.3...v0.1.5
fix webcam videorate by @edgarriba in https://github.com/kornia/kornia-rs/pull/71
kornia_rs::Interpolation module by @edgarriba in https://github.com/kornia/kornia-rs/pull/79Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.3...v0.1.4
implement std mean by @edgarriba in https://github.com/kornia/kornia-rs/pull/63
compute_histogram python bindings by @edgarriba in https://github.com/kornia/kornia-rs/pull/64io::WebcamCapture via gstreamer by @edgarriba in https://github.com/kornia/kornia-rs/pull/61ImageDtype trait to implement resize for various dtypes by @gau-nernst in https://github.com/kornia/kornia-rs/pull/68Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.2...v0.1.3
deprecate .image_size() in favour if img.size() by @edgarriba in https://github.com/kornia/kornia-rs/pull/55
Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.1...v0.1.2
Fix rust release by @edgarriba in https://github.com/kornia/kornia-rs/pull/38
Full Changelog: https://github.com/kornia/kornia-rs/compare/v0.1.0...v0.1.1
switch to maturin by @edgarriba in https://github.com/kornia/kornia-rs/pull/4
Full Changelog: https://github.com/kornia/kornia-rs/commits/v0.1.0
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →