pub struct Denoiser { /* private fields */ }Expand description
A stateful denoiser that cleans a stream of frames.
Push frames in order with push_frame and
collect the cleaned ones with recv_frame or
try_recv_frame.
At the end of the stream call flush to drain
whatever temporal context is left.
Frames are f32 values in [0, 1], laid out as
width * height * channels.
use av_denoise::accelerate::Accelerator;
use av_denoise::{ChannelMode, Denoiser, DenoiserOptions, DenoisingMode, Device};
let options = DenoiserOptions::builder()
.channel_mode(ChannelMode::Luma)
.mode(DenoisingMode::Temporal { radius: 2 })
.build();
let mut denoiser = Denoiser::create(
&[Accelerator::Vulkan],
&Device::Default,
1920,
1080,
options,
)?;
let frames: Vec<Vec<f32>> = read_my_frames();
let mut cleaned: Vec<Vec<f32>> = Vec::new();
for frame in &frames {
denoiser.push_frame(frame)?;
// Temporal denoising runs a few frames behind the input, so
// there is not always one ready to collect.
if let Some(out) = denoiser.recv_frame()? {
cleaned.push(out);
}
}
// Drain the frames still inside the temporal window.
denoiser.flush(|out| cleaned.push(out))?;Implementations§
Source§impl Denoiser
impl Denoiser
Sourcepub fn create(
accelerators: &[Accelerator],
device: &Device,
width: u32,
height: u32,
options: DenoiserOptions,
) -> Result<Self, DenoiserError>
pub fn create( accelerators: &[Accelerator], device: &Device, width: u32, height: u32, options: DenoiserOptions, ) -> Result<Self, DenoiserError>
Tries each accelerator in accelerators in order and builds a
denoiser on the first one that works.
device picks a non-default device on the chosen runtime.
§Thread stack size
cubecl spawns its own per-device worker thread, named
DS{U,D}-…, and runs GPU kernel codegen on it. That thread gets
Rust’s default stack, which is RUST_MIN_STACK or 2 MiB when
that is unset.
The windowed NLM kernels unroll their body
(2 * search_radius + 1)^2 times, so a search_radius of about
5 or more can overflow the 2 MiB default and abort the process.
Callers using a search_radius above 4 should set
RUST_MIN_STACK to at least 16 MiB before any cubecl thread
spawns, usually right at the top of main.
if std::env::var_os("RUST_MIN_STACK").is_none() {
// SAFETY: single-threaded at startup.
unsafe { std::env::set_var("RUST_MIN_STACK", "16777216") };
}Sourcepub fn selected_accelerator(&self) -> Accelerator
pub fn selected_accelerator(&self) -> Accelerator
The accelerator sniff_best_accelerator picked.
Sourcepub fn push_frame(&mut self, frame: &[f32]) -> Result<(), DenoiserError>
pub fn push_frame(&mut self, frame: &[f32]) -> Result<(), DenoiserError>
Uploads one frame into the temporal window.
frame holds width * height * channels f32 values in
[0, 1].
Once the window is full and the pipeline has room, this also starts the kernels for the next denoised frame.
Up to MAX_PENDING outputs can be in flight at once, so the GPU
runs one frame’s kernels while the previous frame’s readback is
still travelling. At that ceiling this returns
DenoiserError::QueueFull, and the caller has to drain a frame
with Self::recv_frame before pushing more.
Sourcepub fn recv_frame(&mut self) -> Result<Option<Vec<f32>>, DenoiserError>
pub fn recv_frame(&mut self) -> Result<Option<Vec<f32>>, DenoiserError>
Blocks until the in-flight denoise finishes and returns the cleaned frame.
Returns Ok(None) when nothing is in flight, which happens while
the temporal window is still filling up.
Sourcepub fn try_recv_frame(&mut self) -> Result<Option<Vec<f32>>, DenoiserError>
pub fn try_recv_frame(&mut self) -> Result<Option<Vec<f32>>, DenoiserError>
Collects the in-flight denoise if one is ready.
This can still block for a moment while the runtime confirms the readback has landed. When the kernels have already finished the wait is effectively nothing.
Sourcepub fn flush(&mut self, sink: impl FnMut(Vec<f32>)) -> Result<(), DenoiserError>
pub fn flush(&mut self, sink: impl FnMut(Vec<f32>)) -> Result<(), DenoiserError>
Drains the in-flight frames and the trailing temporal tail,
handing each frame it produces to sink.
The tail is padded by repeating the last pushed frame.
On success the denoiser is ready for a fresh, unrelated stream of the same size and parameters. Pushing again after a flush starts a new temporal window from scratch, and flushing more than once is fine.
If flush returns Err the denoiser is in an undefined state
and should be dropped rather than reused.