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.2

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.2
File Size Uploaded
mitsuba_oidn-0.1.2.tar.gz 47.3 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for mitsuba-oidn 0.1.2
File
mitsuba_oidn-0.1.2-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
mitsuba_oidn-0.1.2-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
mitsuba_oidn-0.1.2-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.2-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details

Total release size: 237.9 MB

Release files / mitsuba_oidn-0.1.2.tar.gz

Download URL mitsuba_oidn-0.1.2.tar.gz
Size 47.3 MB
Tags Source
SHA-256 checksum
How to use checksums
baa03e305e92709aa62fb27347ce28f00ce683df0f93d58b6056cea18909e506
BLAKE2b-256 checksum
How to use checksums
8a97fb5b464915b5fbc657dc8f692c4f5c825712631a706a2c4f761b19d20c4f
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.2-cp310-abi3-win_amd64.whl

Download URL mitsuba_oidn-0.1.2-cp310-abi3-win_amd64.whl
Size 48.1 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
6cb95999111c746b7e24dc855bf9941de101023116491dd734bb59c9cb8722cb
BLAKE2b-256 checksum
How to use checksums
852eb1c7c9b7e62375389d76cd3f5806fae05142684df3df05865953e3750f3d
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.2-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL mitsuba_oidn-0.1.2-cp310-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 47.8 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
bb80c2ee180a6e9c6246011b7997d727a452b8c5a4c0121356be5a3145bc64ab
BLAKE2b-256 checksum
How to use checksums
407fce7ffa7c11f5345bbc23608be7f370b8d09c45d2bf2876839041f21cb885
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.2-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

Download URL mitsuba_oidn-0.1.2-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
1bafc5f335da7937921d8806bd12e7bdda144b602fa88175693df608a5f617fc
BLAKE2b-256 checksum
How to use checksums
3cad838ccf0be94cd87ce8ed43bd8d0c5b27c1398a2ed9a60fa313e90fcd5daa
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.2-cp310-abi3-macosx_11_0_arm64.whl

Download URL mitsuba_oidn-0.1.2-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
ea2fa256dec6927ce57eb9bafbe01fd5a740005cc505bc63614719437b5d9cb6
BLAKE2b-256 checksum
How to use checksums
61d718e367e4e06ccb8a4f2a08d8a5ed18a87852631fa68e070e6df0904b7b7e
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

This release

0.1.2 This release

5 release files

0.1.1

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