zoomvtools 1.1.1

Video motion vector analysis utilities in pure Rust
Documentation
#![allow(clippy::unwrap_used, reason = "allow in test files")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "allow in test files")]
#![allow(clippy::indexing_slicing, reason = "allow in test files")]
#![allow(unused_unsafe)]

use std::num::NonZeroUsize;

use pastey::paste;

const DEGRAIN_SIZES: &[(usize, usize)] = &[
    (2, 2),
    (2, 4),
    (4, 2),
    (4, 4),
    (4, 8),
    (8, 1),
    (8, 2),
    (8, 4),
    (8, 8),
    (8, 16),
    (16, 1),
    (16, 2),
    (16, 4),
    (16, 8),
    (16, 16),
    (16, 32),
    (32, 8),
    (32, 16),
    (32, 32),
    (32, 64),
    (64, 16),
    (64, 32),
    (64, 64),
    (64, 128),
    (128, 32),
    (128, 64),
    (128, 128),
];

macro_rules! degrain_tests {
    ($module:ident) => {
        paste! {
            #[test]
            fn [<degrain_uniform_u8_ $module>]() {
                for radius in 1..=6 {
                    for &(w, h) in DEGRAIN_SIZES {
                        let pitch = w + 8;
                        let mut dest = vec![0_u8; pitch * h];
                        let mut src = vec![0_u8; pitch * h];
                        for y in 0..h {
                            for x in 0..w {
                                src[y * pitch + x] = 37;
                            }
                        }

                        let refs_data = vec![vec![0_u8; pitch * h]; radius * 2];
                        let refs = refs_data.iter().map(std::vec::Vec::as_slice).collect::<Vec<_>>();
                        let refs_pitch = vec![NonZeroUsize::new(pitch).unwrap(); radius * 2];
                        let w_refs = vec![0_i32; radius * 2];

                        verify_asm!($module, degrain_test(
                            &mut dest,
                            NonZeroUsize::new(w).unwrap(),
                            NonZeroUsize::new(h).unwrap(),
                            &src,
                            NonZeroUsize::new(pitch).unwrap(),
                            &refs,
                            &refs_pitch,
                            256,
                            &w_refs,
                        ));

                        for y in 0..h {
                            for x in 0..w {
                                assert_eq!(dest[y * pitch + x], 37, "radius={radius} {w}x{h}");
                            }
                        }
                    }
                }
            }

            #[test]
            fn [<degrain_uniform_u16_ $module>]() {
                for radius in 1..=6 {
                    for &(w, h) in DEGRAIN_SIZES {
                        let pitch = w + 8;
                        let mut dest = vec![0_u16; pitch * h];
                        let mut src = vec![0_u16; pitch * h];
                        for y in 0..h {
                            for x in 0..w {
                                src[y * pitch + x] = 503;
                            }
                        }

                        let refs_data = vec![vec![0_u16; pitch * h]; radius * 2];
                        let refs = refs_data.iter().map(std::vec::Vec::as_slice).collect::<Vec<_>>();
                        let refs_pitch = vec![NonZeroUsize::new(pitch).unwrap(); radius * 2];
                        let w_refs = vec![0_i32; radius * 2];

                        verify_asm!($module, degrain_test(
                            &mut dest,
                            NonZeroUsize::new(w).unwrap(),
                            NonZeroUsize::new(h).unwrap(),
                            &src,
                            NonZeroUsize::new(pitch).unwrap(),
                            &refs,
                            &refs_pitch,
                            256,
                            &w_refs,
                        ));

                        for y in 0..h {
                            for x in 0..w {
                                assert_eq!(dest[y * pitch + x], 503, "radius={radius} {w}x{h}");
                            }
                        }
                    }
                }
            }

            #[test]
            fn [<degrain_weighted_u8_ $module>]() {
                let radius = 2;
                for &(w, h) in DEGRAIN_SIZES {
                    let pitch = w + 5;
                    let mut dest = vec![0_u8; pitch * h];
                    let mut src = vec![0_u8; pitch * h];
                    let mut refs_data = vec![vec![0_u8; pitch * h]; radius * 2];

                    for y in 0..h {
                        for x in 0..w {
                            src[y * pitch + x] = ((x * 3 + y * 5 + 11) & 0xff) as u8;
                            refs_data[0][y * pitch + x] = ((x * 7 + y * 2 + 13) & 0xff) as u8;
                            refs_data[1][y * pitch + x] = ((x * 9 + y * 3 + 17) & 0xff) as u8;
                            refs_data[2][y * pitch + x] = ((x * 5 + y * 11 + 19) & 0xff) as u8;
                            refs_data[3][y * pitch + x] = ((x * 13 + y * 7 + 23) & 0xff) as u8;
                        }
                    }

                    let refs = refs_data.iter().map(std::vec::Vec::as_slice).collect::<Vec<_>>();
                    let refs_pitch = vec![NonZeroUsize::new(pitch).unwrap(); radius * 2];
                    let w_refs = vec![16, 24, 32, 40];

                    verify_asm!($module, degrain_test(
                        &mut dest,
                        NonZeroUsize::new(w).unwrap(),
                        NonZeroUsize::new(h).unwrap(),
                        &src,
                        NonZeroUsize::new(pitch).unwrap(),
                        &refs,
                        &refs_pitch,
                        144,
                        &w_refs,
                    ));
                }
            }

            #[test]
            fn [<degrain_weighted_u16_ $module>]() {
                let radius = 3;
                for &(w, h) in DEGRAIN_SIZES {
                    let pitch = w + 7;
                    let mut dest = vec![0_u16; pitch * h];
                    let mut src = vec![0_u16; pitch * h];
                    let mut refs_data = vec![vec![0_u16; pitch * h]; radius * 2];

                    for y in 0..h {
                        for x in 0..w {
                            src[y * pitch + x] = ((x * 113 + y * 71 + 97) & 0xffff) as u16;
                            refs_data[0][y * pitch + x] = ((x * 31 + y * 17 + 101) & 0xffff) as u16;
                            refs_data[1][y * pitch + x] = ((x * 43 + y * 29 + 103) & 0xffff) as u16;
                            refs_data[2][y * pitch + x] = ((x * 59 + y * 37 + 107) & 0xffff) as u16;
                            refs_data[3][y * pitch + x] = ((x * 67 + y * 41 + 109) & 0xffff) as u16;
                            refs_data[4][y * pitch + x] = ((x * 73 + y * 47 + 113) & 0xffff) as u16;
                            refs_data[5][y * pitch + x] = ((x * 79 + y * 53 + 127) & 0xffff) as u16;
                        }
                    }

                    let refs = refs_data.iter().map(std::vec::Vec::as_slice).collect::<Vec<_>>();
                    let refs_pitch = vec![NonZeroUsize::new(pitch).unwrap(); radius * 2];
                    let w_refs = vec![8, 12, 16, 20, 24, 28];

                    verify_asm!($module, degrain_test(
                        &mut dest,
                        NonZeroUsize::new(w).unwrap(),
                        NonZeroUsize::new(h).unwrap(),
                        &src,
                        NonZeroUsize::new(pitch).unwrap(),
                        &refs,
                        &refs_pitch,
                        148,
                        &w_refs,
                    ));
                }
            }
        }
    };
}

degrain_tests!(rust);
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
degrain_tests!(avx2);