zoomvtools 1.1.0

Video motion vector analysis utilities in pure Rust
Documentation
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
mod avx2;
#[cfg(all(target_arch = "x86_64", feature = "avx512"))]
mod avx512;
mod rust;

#[cfg(test)]
mod tests;

use std::num::{NonZeroU8, NonZeroUsize};

use crate::util::Pixel;

/// Performs horizontal Wiener filtering for sub-pixel motion estimation refinement.
///
/// This function applies a Wiener filter horizontally to create high-quality sub-pixel
/// samples between existing pixels. The Wiener filter uses a 6-tap kernel with optimized
/// coefficients that provide excellent interpolation quality by minimizing reconstruction
/// error while preserving image details.
///
/// Edge pixels use simple averaging due to insufficient neighbors for the full kernel.
/// The Wiener filter is particularly effective for maintaining sharpness during
/// sub-pixel interpolation in motion estimation applications.
///
/// # Parameters
/// - `src`: Source image buffer
/// - `dest`: Destination buffer for interpolated results
/// - `pitch`: Number of pixels per row in both buffers
/// - `width`: Width of the image in pixels
/// - `height`: Height of the image in pixels
/// - `bits_per_sample`: Bit depth of the pixel format for clamping
#[inline]
pub fn refine_horizontal_wiener<T: Pixel>(
    dest: &mut [T],
    src: &[T],
    pitch: NonZeroUsize,
    width: NonZeroUsize,
    height: NonZeroUsize,
    bits_per_sample: NonZeroU8,
) {
    debug_assert!(
        bits_per_sample.get() as usize > (size_of::<T>() - 1) * 8
            && (bits_per_sample.get() as usize <= size_of::<T>() * 8)
    );

    #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
    if crate::util::has_avx512_znver5() {
        // PERF(znver5): 10% faster than AVX2 for 8-bit
        // PERF(znver5): 20% faster than AVX2 for 9-15-bit
        // PERF(znver5): 20% faster than AVX2 for 16-bit
        // SAFETY: We check for AVX-512 first
        unsafe {
            avx512::refine_horizontal_wiener(dest, src, pitch, width, height, bits_per_sample);
        }
        return;
    }

    #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
    if crate::util::has_avx512_skylake() {
        cfg_select! {
            feature = "experimental" => {
                // SAFETY: We check for AVX-512 first
                unsafe {
                    avx512::refine_horizontal_wiener(dest, src, pitch, width, height, bits_per_sample);
                }
                return;
            }
            _ => {
                // PERF(znver4): 3% faster than AVX2 for 8-bit
                // PERF(znver4): 5% faster than AVX2 for 9-15-bit
                // TODO(znver4): 2-5% slower than AVX2 for 16-bit
                if size_of::<T>() == 1 || bits_per_sample.get() < 16 {
                    // SAFETY: We check for AVX-512 first
                    unsafe {
                        avx512::refine_horizontal_wiener(dest, src, pitch, width, height, bits_per_sample);
                    }
                    return;
                }
            }
        }
    }

    #[cfg(all(target_arch = "x86_64", feature = "avx2"))]
    if crate::util::has_avx2() {
        // PERF: 80% faster than scalar for 8-bit
        // PERF: 60% faster than scalar for 9-15-bit
        // PERF: 50% faster than scalar for 16-bit
        // SAFETY: We check for AVX2 first
        unsafe {
            avx2::refine_horizontal_wiener(dest, src, pitch, width, height, bits_per_sample);
        }
        return;
    }

    rust::refine_horizontal_wiener(dest, src, pitch, width, height, bits_per_sample);
}

/// Performs vertical Wiener filtering for sub-pixel motion estimation refinement.
///
/// This function applies a Wiener filter vertically to create high-quality sub-pixel
/// samples between existing pixels. The Wiener filter uses a 6-tap kernel with optimized
/// coefficients that provide excellent interpolation quality by minimizing reconstruction
/// error while preserving image details.
///
/// Edge rows use simple averaging due to insufficient neighbors for the full kernel,
/// and the last row is copied directly from the source. The Wiener filter is
/// particularly effective for maintaining sharpness during sub-pixel interpolation.
///
/// # Parameters
/// - `src`: Source image buffer
/// - `dest`: Destination buffer for interpolated results
/// - `pitch`: Number of pixels per row in both buffers
/// - `width`: Width of the image in pixels
/// - `height`: Height of the image in pixels
/// - `bits_per_sample`: Bit depth of the pixel format for clamping
#[inline]
pub fn refine_vertical_wiener<T: Pixel>(
    dest: &mut [T],
    src: &[T],
    pitch: NonZeroUsize,
    width: NonZeroUsize,
    height: NonZeroUsize,
    bits_per_sample: NonZeroU8,
) {
    debug_assert!(
        bits_per_sample.get() as usize > (size_of::<T>() - 1) * 8
            && (bits_per_sample.get() as usize <= size_of::<T>() * 8)
    );

    #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
    if crate::util::has_avx512_znver5() {
        // PERF(znver5): 30% faster than AVX2 for 8-bit
        // PERF(znver5): 30% faster than AVX2 for 9-15-bit
        // PERF(znver5): 40% faster than AVX2 for 16-bit
        // SAFETY: We check for AVX-512 first
        unsafe {
            avx512::refine_vertical_wiener(dest, src, pitch, width, height, bits_per_sample);
        }
        return;
    }

    #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
    if crate::util::has_avx512_skylake() {
        cfg_select! {
            feature = "experimental" => {
                // SAFETY: We check for AVX-512 first
                unsafe {
                    avx512::refine_vertical_wiener(dest, src, pitch, width, height, bits_per_sample);
                }
                return;
            }
            _ => {
                // PERF(znver4): 3% faster than AVX2 for 8-bit
                // PERF(znver4): 10% faster than AVX2 for 9-15-bit
                // TODO(znver4): 10% slower than AVX2 for 16-bit
                if size_of::<T>() == 1 || bits_per_sample.get() < 16 {
                    // SAFETY: We check for AVX-512 first
                    unsafe {
                        avx512::refine_vertical_wiener(dest, src, pitch, width, height, bits_per_sample);
                    }
                    return;
                }
            }
        }
    }

    #[cfg(all(target_arch = "x86_64", feature = "avx2"))]
    if crate::util::has_avx2() {
        // PERF: 75% faster than scalar for 8-bit
        // PERF: 40% faster than scalar for 9-15-bit
        // PERF: 20% faster than scalar for 16-bit
        // SAFETY: We check for AVX2 first
        unsafe {
            avx2::refine_vertical_wiener(dest, src, pitch, width, height, bits_per_sample);
        }
        return;
    }

    rust::refine_vertical_wiener(dest, src, pitch, width, height, bits_per_sample);
}