chinese-hershey-font
Convert Chinese Characters to Single-Line Fonts using Computer Vision
๐ฆ Low-level 3D Computer Vision library in Rust
English | ็ฎไฝไธญๆ
The kornia crate is a low-level computer vision library for Rust ๐ฆ
Fast, thread-safe image I/O and processing with a single API that runs on the CPU or an NVIDIA GPU โ the same Image and operators dispatch on where the data lives. It hands results to PyTorch and TensorRT with no host copy (DLPack, CUDA Array Interface), and fuses a camera frame into a normalized model input in one CUDA kernel โ built for real-time pipelines.
The following example demonstrates how to read and display image information:
use kornia::image::Image;
use kornia::io::functional as F;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// read the image
let image: Image<u8, 3> = F::read_image_any_rgb8("tests/data/dog.jpeg")?;
println!("Hello, world! ๐ฆ");
println!("Loaded Image size: {:?}", image.size());
println!("\nGoodbyte!");
Ok(())
}
Hello, world! ๐ฆ
Loaded Image size: ImageSize { width: 258, height: 195 }
Goodbyte!
Image and operators dispatch on residency โ no separate GPU types.__cuda_array_interface__ to and from PyTorch, plus numpy views.Add the following to your Cargo.toml:
[dependencies] kornia = "0.1"
Alternatively, you can use each sub-crate separately:
[dependencies] kornia-tensor = "0.1" kornia-tensor-ops = "0.1" kornia-io = "0.1" kornia-image = "0.1" kornia-imgproc = "0.1" kornia-3d = "0.1" kornia-apriltag = "0.1" kornia-vlm = "0.1" kornia-bow = "0.1" kornia-algebra = "0.1"
pip install kornia-rs
A subset of the full rust API is exposed. See the kornia documentation for more detail about the API for python functions and objects exposed by the kornia-rs Python module.
The kornia-rs library is thread-safe for use under the free-threaded Python build.
Depending on the features you want to use, you might need to install the following dependencies in your system:
sudo apt-get install clang
sudo apt-get install nasm
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
Note: Check the gstreamer installation guide for more details.
The following example shows how to read an image, convert it to grayscale and resize it. The image is then logged to a rerun recording stream for visualization.
For more examples and use cases, check out the examples directory, which includes:
use kornia::{image::{Image, ImageSize}, imgproc};
use kornia::io::functional as F;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// read the image
let image: Image<u8, 3> = F::read_image_any_rgb8("tests/data/dog.jpeg")?;
let image_viz = image.clone();
let image_f32: Image<f32, 3> = image.cast_and_scale::<f32>(1.0 / 255.0)?;
// convert the image to grayscale
let mut gray = Image::<f32, 1>::from_size_val(image_f32.size(), 0.0)?;
imgproc::color::gray_from_rgb(&image_f32, &mut gray)?;
// resize the image
let new_size = ImageSize {
width: 128,
height: 128,
};
let mut gray_resized = Image::<f32, 1>::from_size_val(new_size, 0.0)?;
imgproc::resize::resize_native(
&gray, &mut gray_resized,
imgproc::interpolation::InterpolationMode::Bilinear,
)?;
println!("gray_resize: {:?}", gray_resized.size());
// create a Rerun recording stream
let rec = rerun::RecordingStreamBuilder::new("Kornia App").spawn()?;
rec.log(
"image",
&rerun::Image::from_elements(
image_viz.as_slice(),
image_viz.size().into(),
rerun::ColorModel::RGB,
),
)?;
rec.log(
"gray",
&rerun::Image::from_elements(gray.as_slice(), gray.size().into(), rerun::ColorModel::L),
)?;
rec.log(
"gray_resize",
&rerun::Image::from_elements(
gray_resized.as_slice(),
gray_resized.size().into(),
rerun::ColorModel::L,
),
)?;
Ok(())
}
Load an image, which is converted directly to a numpy array to ease the integration with other libraries.
import kornia_rs as K
import numpy as np
import torch
# load a JPEG with libjpeg-turbo
img: np.ndarray = K.io.read_image_jpeg("dog.jpeg", "rgb")
# or read any supported format
# img: np.ndarray = K.io.read_image("dog.png")
assert img.shape == (195, 258, 3)
# convert to dlpack to import to torch
img_t = torch.from_dlpack(img)
assert img_t.shape == (195, 258, 3)
Write an image to disk:
import kornia_rs as K
import numpy as np
# load a JPEG with libjpeg-turbo
img: np.ndarray = K.io.read_image_jpeg("dog.jpeg", "rgb")
# write the image to disk (mode, JPEG quality)
K.io.write_image_jpeg("dog_copy.jpeg", img, "rgb", 95)
Image โ PIL-style class with uint8 + uint16 supportkornia_rs.image.Image mirrors PIL's fromarray / save / load / decode
and natively holds uint16 for depth maps and scientific imagery
(lossless via PNG-16):
import io
import numpy as np
from kornia_rs.image import Image
# Bit depth is auto-detected from the numpy dtype.
rgb = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
depth = np.full((480, 640), 1500, dtype=np.uint16) # mm
rgb_img = Image.fromarray(rgb)
depth_img = Image.fromarray(depth)
# In-memory encode for transit (Zenoh / MCAP / gRPC).
png16_bytes = depth_img.encode("png") # lossless on uint16
# Save to disk (format from extension), or to any file-like (PIL parity).
rgb_img.save("dog.png")
buf = io.BytesIO(); rgb_img.save(buf, format="jpeg")
# Decode auto-detects bit depth from the file header.
back = Image.decode(png16_bytes, mode="L")
assert back.dtype == np.uint16
The original ImageEncoder/ImageDecoder pair is still available for
JPEG-only workflows that want the explicit turbojpeg backend object:
import kornia_rs as K
img = K.io.read_image_jpeg("dog.jpeg", "rgb")
image_encoder = K.io.ImageEncoder()
image_encoder.set_quality(95)
img_encoded: list[int] = image_encoder.encode(img)
image_decoder = K.io.ImageDecoder()
decoded_img: np.ndarray = image_decoder.decode(bytes(img_encoded))
Resize an image using the kornia-rs backend with SIMD acceleration:
import kornia_rs as K
# load image with kornia-rs
img = K.io.read_image_jpeg("dog.jpeg", "rgb")
# resize the image
resized_img = K.imgproc.resize(img, (128, 128), interpolation="bilinear")
assert resized_img.shape == (128, 128, 3)
The published wheels are GPU-capable but load CUDA lazily: the same wheel runs on
CPU when no GPU is present and uses the GPU when one is. The GPU path needs an
NVIDIA driver (libcuda) and nvrtc from the CUDA toolkit; without them the CPU
ops keep working.
Device pixels use the same Image type. .device reads "cpu" or "cuda:{id}",
.to_cuda(stream) uploads, .cpu() downloads. Color ops live under
kornia_rs.imgproc and dispatch on residency: a device Image runs the CUDA
kernel, a host Image or numpy array runs the CPU kernel.
import numpy as np
import kornia_rs as K
from kornia_rs.image import Image
from kornia_rs.cuda import Stream
if K.cuda.is_available():
rgb = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
img = Image.from_numpy(rgb).to_cuda(Stream.default()) # -> "cuda:0"
gray = K.imgproc.gray_from_rgb(img) # runs on the GPU
out = gray.cpu().numpy() # -> host, (480, 640, 1)
GPU color conversions (gray_from_rgb, bgr_from_rgb, hsv_from_rgb,
lab_from_rgb, ycbcr_from_rgb, sepia_from_rgb, apply_colormap, โฆ) and the
fused Preprocessor are the GPU entry points. Tensors cross to PyTorch with no
copy through DLPack (torch.from_dlpack) and __cuda_array_interface__.
Preprocessor fuses resize, normalize and HWCโCHW into one CUDA kernel per
frame. It emits a device tensor that feeds an inference engine with no host copy
โ the path for real-time camera pipelines.
import torch
from kornia_rs import Preprocessor, IMAGENET_MEAN, IMAGENET_STD
from kornia_rs.cuda import Stream
# One kernel per frame: NV12 -> normalized fp16 [1, 3, 640, 640] on the GPU.
pre = Preprocessor(mode="letterbox", format="nv12", f16=True,
mean=IMAGENET_MEAN, std=IMAGENET_STD, stream=Stream.default(0))
t = pre.run(nv12_frame, 1920, 1080, 640, 640) # device Tensor
x = torch.from_dlpack(t) # zero-copy handoff to PyTorch
# TensorRT: ctx.set_tensor_address("images", t.data_ptr)
The same one-call-per-residency model holds in Rust โ convert picks CPU or GPU
from where the images live:
let stream = CudaContext::new(0)?.default_stream(); let rgb = Rgb8::from_size_vec(size, data)?.to_cuda(&stream)?; // device image let mut gray = Gray8::zeros_cuda(size, &stream)?; rgb.convert(&mut gray)?; // runs on the GPU
Full pipelines: examples/cuda_camera_preprocess
(V4L2 camera โ fused CUDA preprocess) and
kornia-py/examples/preprocess_to_inference.py
(NV12 โ fused preprocess โ ResNet-18 / TensorRT, GPU-resident end to end).
Before you begin, ensure you have rust and python3 installed on your system.
Install Rust using rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Install pixi for package and environment management:
curl -fsSL https://pixi.sh/install.sh | bash
Clone the repository to your local directory:
git clone https://github.com/kornia/kornia-rs.git
Install dependencies using pixi:
pixi install
You can check all available development commands via pixi task list:
pixi run rust-check # Check Rust compilation (all targets) pixi run rust-clippy # Run clippy (all targets, warnings as errors) pixi run rust-fmt # Format Rust code pixi run rust-fmt-check # Check Rust formatting pixi run rust-lint # Run all Rust lints (fmt + clippy + check) pixi run rust-test # Run Rust tests pixi run rust-test-release # Run Rust tests (release mode) pixi run rust-clean # Clean Rust build artifacts pixi run py-build # Build kornia-py for development pixi run py-build-release # Build kornia-py for release pixi run py-test # Run pytest pixi run cpp-build # Build C++ library (debug) pixi run cpp-test # Build and run C++ tests
This project includes a development container configuration for a consistent development environment across different machines.
Using the Dev Container:
Remote - Containers extension in Visual Studio CodeF1 and select Remote-Containers: Reopen in ContainerThe devcontainer includes all necessary dependencies and tools for building and testing kornia-rs.
Compile the project and run all tests:
pixi run rust-test
To run tests for a specific package:
pixi run rust-test-package <package-name>
To run clippy linting:
pixi run rust-clippy
Build Python wheels using maturin:
pixi run py-build
Run Python tests:
pixi run py-test
We welcome contributions! Please read CONTRIBUTING.md for:
Kornia-rs accepts AI-assisted code but strictly rejects AI-generated contributions where the submitter acts as a proxy. All contributors must be the Sole Responsible Author for every line of code. Please review our AI Policy before submitting pull requests. Key requirements include:
pixi run rust-test or cargo test)kornia-rs utilities instead of reinventing the wheelResult<T, E> for error handling (avoid unwrap()/expect() in library code)Automated AI reviewers (e.g., @copilot) will check PRs against these policies. See AI_POLICY.md for complete details.
This is a child project of Kornia.
If you use kornia-rs in your research, please cite:
@misc{2505.12425,
Author = {Edgar Riba and Jian Shi and Aditya Kumar and Andrew Shen and Gary Bradski},
Title = {Kornia-rs: A Low-Level 3D Computer Vision Library In Rust},
Year = {2025},
Eprint = {arXiv:2505.12425},
}
more like this
Convert Chinese Characters to Single-Line Fonts using Computer Vision
๐ฅ๐ค 8 minimalistic templates for tfjs mediapipe handpose and facemesh
search projects, people, and tags