use std::fmt;
use anyhow::Result;
use super::{assemble, planes_8bit};
use crate::frame::VideoFrame;
mod anisotropic;
mod bilateral;
mod gaussian;
mod mean;
mod median;
mod nlmeans;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum DenoiseMethod {
#[default]
Bilateral,
Gaussian,
Median,
Mean,
Nlmeans,
Anisotropic,
}
impl fmt::Display for DenoiseMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
DenoiseMethod::Bilateral => "bilateral",
DenoiseMethod::Gaussian => "gaussian",
DenoiseMethod::Median => "median",
DenoiseMethod::Mean => "mean",
DenoiseMethod::Nlmeans => "nlmeans",
DenoiseMethod::Anisotropic => "anisotropic",
})
}
}
#[cfg(feature = "serde")]
pub(super) fn default_denoise_strength() -> f32 {
0.5
}
pub(super) fn apply(frame: &VideoFrame, method: DenoiseMethod, strength: f32) -> Result<VideoFrame> {
let (yp, up, vp) = planes_8bit(frame, "denoise")?;
let s = strength.clamp(0.0, 1.0);
let (w, h) = (frame.width as usize, frame.height as usize);
let (cw, ch) = (w / 2, h / 2);
Ok(assemble(
frame,
frame.width,
frame.height,
plane(method, &yp, w, h, s),
plane(method, &up, cw, ch, s),
plane(method, &vp, cw, ch, s),
))
}
fn plane(method: DenoiseMethod, src: &[u8], w: usize, h: usize, strength: f32) -> Vec<u8> {
if w == 0 || h == 0 || strength <= 0.0 {
return src.to_vec();
}
let filtered = match method {
DenoiseMethod::Bilateral => bilateral::plane(src, w, h),
DenoiseMethod::Gaussian => gaussian::plane(src, w, h),
DenoiseMethod::Median => median::plane(src, w, h),
DenoiseMethod::Mean => mean::plane(src, w, h),
DenoiseMethod::Nlmeans => nlmeans::plane(src, w, h),
DenoiseMethod::Anisotropic => anisotropic::plane(src, w, h),
};
if strength >= 1.0 {
return filtered;
}
let inv = 1.0 - strength;
src.iter()
.zip(&filtered)
.map(|(&s, &f)| (s as f32 * inv + f as f32 * strength).round().clamp(0.0, 255.0) as u8)
.collect()
}
pub(super) fn clamp_idx(v: isize, hi: usize) -> usize {
v.clamp(0, hi as isize - 1) as usize
}