Skip to main content

mitsuba-oidn

PyPI Wheels

This package provides unofficial Python bindings for Intel Open Image Denoise (OIDN) for use with Dr.Jit and the Mitsuba renderer.

import mitsuba as mi
import mitsuba_oidn as oidn

mi.set_variant("cuda_ad_rgb")
scene = mi.load_dict(mi.cornell_box())
color = mi.render(scene, spp=16)                   # mi.TensorXf of shape (H, W, 3)
denoised = oidn.denoise(color, hdr=True)           # also an mi.TensorXf, on the GPU

The color argument can be any array type that supports DLPack or the buffer protocol, and the result uses the same type and device.

The package runs on Intel/ARM CPUs, CUDA, and on Apple Metal. It exchanges tensors via the buffer protocol and DLPack for compatibility with Dr.Jit, NumPy, PyTorch, JAX, MLX, etc., and it accesses their memory directly whenever the target device can.

Why another binding?

Several unofficial bindings of OIDN already exist (e.g., pyoidn, which uses cffi to expose the C API directly). This project uses nanobind to create bindings that feel more natural in Python. They automatically commit and release resources and raise errors as Python exceptions. Pixel formats and dimensions are inferred from nd-array signatures.

The bindings are designed to interoperate with Dr.Jit and Mitsuba 3. The copy of OIDN bundled here is modified to use Dr.Jit's nanothread thread pool instead of spinning up another redundant thread pool via oneTBB. For this reason, the package depends on Dr.Jit.

Installation

pip install mitsuba-oidn

Wheels are available for Linux (x86_64, aarch64), Windows (x86_64), and macOS (arm64), matching the platforms supported by Dr.Jit. The Linux x86_64 and Windows wheels include the CUDA device, which activates when an NVIDIA driver is present. The macOS wheel includes the Metal device.

The denoise() function

oidn.denoise(color, albedo=None, normal=None, *, hdr=False, srgb=False,
             clean_aux=False, quality=oidn.Quality.High, input_scale=None,
             filter="RT", device=None, output=None)

Images are arrays of shape (H, W) or (H, W, C) with C <= 4 and dtype float32 or float16. A fourth channel is ignored on input. The result has the same framework and lives on the same device as color: a NumPy array yields a NumPy array, a CUDA tensor yields a CUDA tensor. Its shape is (H, W, min(C, 3)), or (H, W) for two-dimensional input.

  • hdr, srgb, clean_aux, quality, and input_scale map to the parameters of the OIDN RT filter. Set hdr=True for linear radiance values without an upper bound, and clean_aux=True when the albedo and normal images are noise-free. See the OIDN documentation for details.
  • device selects the device. By default, CUDA arrays use a CUDA device with the matching ordinal, Dr.Jit Metal arrays use the Metal device, and host arrays use the fastest physical device in the system, which can be overridden with the OIDN_DEFAULT_DEVICE environment variable (cpu, cuda, metal, or a physical device ID).
  • output supplies a preallocated array that is filled in place and returned. With an RGBA output array, OIDN writes the RGB channels and leaves alpha untouched.
  • Filters are expensive to create, so denoise() caches a few of them, keyed on image size, format, feature set, and parameters. Repeated calls at the same resolution pay only for the actual filtering.

When the device cannot access an input array directly, for example a NumPy array passed to a CUDA device, denoise() copies it into a device buffer. Otherwise no copies are made. Dr.Jit arrays are bound directly on all backends, including Metal, and the result of a Dr.Jit input is filtered straight into a fresh Dr.Jit array.

import numpy as np
import torch
import mitsuba_oidn as oidn

# NumPy, CPU or Metal depending on the fastest available device
out = oidn.denoise(np.asarray(color, dtype=np.float32), hdr=True)

# PyTorch on the GPU: zero-copy in and out
color = torch.rand(1080, 1920, 3, device="cuda")
out = oidn.denoise(color, quality=oidn.Quality.Balanced)
assert out.device == color.device

The object API

The denoise() function covers the common case. The classes below mirror the OIDN object model for applications that need control over devices, memory, and filter lifetime, for instance when denoising many frames or several AOVs that share auxiliary images.

Devices

oidn.physical_devices()               # list of PhysicalDevice: id, name, type, uuid, ...

dev = oidn.Device()                   # fastest physical device
dev = oidn.Device(oidn.DeviceType.CPU)
dev = oidn.Device.from_physical(id)   # also from_uuid(), from_luid(), from_pci_address()
dev = oidn.Device.cuda(device_id=0, stream=torch.cuda.current_stream().cuda_stream)
dev = oidn.Device.metal(command_queue)  # raw id<MTLCommandQueue> pointer

dev.num_threads = 4                   # CPU only: 0 shares the Dr.Jit pool (default),
                                      # a positive value creates a private pool
dev.verbose = 1
dev.type, dev.version, dev.system_memory_supported, dev.managed_memory_supported
dev.sync()                            # wait for asynchronous work

A device commits itself when the first buffer or filter is created. Parameters such as num_threads must be set before that point.

Filters

flt = dev.new_filter("RT")            # or "RTLightmap"

flt.set_image("color", color)         # arrays: layout inferred, zero-copy when possible
flt.set_image("albedo", albedo)
flt.set_image("normal", normal)
flt.set_image("output", output)       # must be writable
flt.hdr = True
flt.clean_aux = True
flt.quality = oidn.Quality.High
flt.input_scale = 0.5                 # None selects automatic scaling
flt.max_memory_mb = 2048
flt.set("cleanAux", True)             # generic access by OIDN parameter name
flt.set_data("weights", blob)         # user-trained weights (bytes or uint8 array)
flt.set_progress_monitor(lambda p: True)  # return False to cancel

flt.execute()                         # commits pending changes, runs, and waits
flt.execute_async(); dev.sync()

set_image() accepts arrays with the layout rules of denoise(). Row and pixel strides are passed to OIDN, so slices of larger arrays work as long as the channel dimension stays contiguous. Whether an array can be bound without a copy depends on the device:

Device Bound without copy
CPU any host array, including CUDA pinned and managed memory
CUDA arrays on the same CUDA device, pinned host memory, and host memory if the GPU supports pageable memory access
Metal any host array, Dr.Jit Metal arrays, and views of buffers created on the device

Use dev.can_share(array) to test this in advance. When binding is not possible, set_image() raises a TypeError that points at the buffer API.

Filters can also take a Buffer with an explicit description:

flt.set_image("color", buf, format=oidn.Format.Float3, width=w, height=h,
              byte_offset=0, pixel_stride=0, row_stride=0)

Buffers

Buffers are memory allocations made by a device. They are the way to work with memory that the host cannot address, such as dedicated GPU memory, and they provide zero-copy views on unified-memory systems.

buf = dev.new_buffer(nbytes)                          # host and device accessible
buf = dev.new_buffer(nbytes, oidn.Storage.Device)     # device memory only
buf = dev.new_shared_buffer(array)                    # wrap device-accessible memory

buf.size, buf.storage, buf.device, buf.data_ptr

view = buf.view("float32", (h, w, 3))     # DLPack and buffer-protocol object
img = np.from_dlpack(view)                # or torch.from_dlpack(view), ...

buf.write(host_array); buf.read(host_array)           # copies through the host
buf.write_async(src); buf.read_async(dst); dev.sync()

Arrays created from buf.view() are recognized by set_image() and bound through the underlying buffer. On Apple silicon, rendering into such a view and denoising it involves no copies at all. On a CUDA device, a device-storage buffer viewed through torch.from_dlpack() gives a tensor that OIDN wrote directly.

Errors

All OIDN errors raise oidn.Error, whose code attribute is an oidn.ErrorCode. Cancellation through a progress monitor raises oidn.Error with ErrorCode.Cancelled. An exception raised inside the progress monitor cancels the filter and propagates unchanged.

Building from source

The build needs CMake 3.21 or newer, a C++17 compiler, and a binary release of ISPC unpacked into ext/ispc, so that ext/ispc/bin/ispc exists (a symlink to an installed ispc binary works as well). Metal support requires Xcode 15 or newer. CUDA support requires CUDA 12.8 or newer and is enabled automatically when the toolkit is found.

git clone --recursive https://github.com/mitsuba-renderer/mitsuba-oidn
cd mitsuba-oidn
pip install nanobind==3.0.1 scikit-build-core
pip install --no-build-isolation -ve .
pytest

The OIDN weights are stored with git-lfs, which must be installed before cloning.

License

mitsuba-oidn is licensed under the BSD 3-Clause license. It bundles Intel Open Image Denoise, which is licensed under the Apache License 2.0.

Metadata

Release files for mitsuba-oidn 0.1.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 mitsuba-oidn 0.1.1
File Size Uploaded
mitsuba_oidn-0.1.1.tar.gz 47.3 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for mitsuba-oidn 0.1.1
File
mitsuba_oidn-0.1.1-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.27+ ARM64, Linux glibc 2.28+ ARM64 Details
mitsuba_oidn-0.1.1-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details

Total release size: 237.0 MB

Release files / mitsuba_oidn-0.1.1.tar.gz

Download URL mitsuba_oidn-0.1.1.tar.gz
Size 47.3 MB
Tags Source
SHA-256 checksum
How to use checksums
b05b013b6253f7669fe99f2e0993491dc715da2dca2751f843d88af29d1323e2
BLAKE2b-256 checksum
How to use checksums
c808cfb610c43d9094147d9b069fb726a05043dfda096785b75e0510628f293e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / mitsuba_oidn-0.1.1-cp310-abi3-win_amd64.whl

Download URL mitsuba_oidn-0.1.1-cp310-abi3-win_amd64.whl
Size 47.6 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
38efd04be609b9c657fe06d802a13bac64b8513c5495acd7cff9a56b41285119
BLAKE2b-256 checksum
How to use checksums
79a37df2e6f7e52259953fbb379641e6e9c663527bb0a0d4f3a5275b243c7476
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 47.3 MB
Tags CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
34e91872d7a3e89dec9587a6700e9d6541a1691a5e5d0807d77c60bb926b4fc2
BLAKE2b-256 checksum
How to use checksums
1d898a9ce07dba15fa09cf78fd9ca90c044b6652c57a703846f5b230f0176ddf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

Download URL mitsuba_oidn-0.1.1-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Size 47.2 MB
Tags CPython 3.10 Linux glibc 2.27+ ARM64 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
2e898b1b8bffa7473114e402207dfa5cd8443ab5d0aac5b6fffd74463da81fb9
BLAKE2b-256 checksum
How to use checksums
dcb04baf747e175d4d876f2396044a6a5f37e2ce40e11fca229b19920a96e8a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / mitsuba_oidn-0.1.1-cp310-abi3-macosx_11_0_arm64.whl

Download URL mitsuba_oidn-0.1.1-cp310-abi3-macosx_11_0_arm64.whl
Size 47.6 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
071070b09b1959e60e0e0ec0ed002b70f62148593fd2b2b8206ee9159875661a
BLAKE2b-256 checksum
How to use checksums
08509d877a35d569ea7e1ecb03d8f7cbd3ae68e5805c9f984d36ae15d37f4398
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release history Release notifications | RSS feed

0.1.2

5 release files

This release

0.1.1 This release

5 release files

0.1.0

5 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