use crate::{GpuDevice, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResizeFilter {
Nearest,
Bilinear,
Bicubic,
Lanczos,
Area,
}
impl ResizeFilter {
#[must_use]
pub fn to_id(self) -> u32 {
match self {
Self::Nearest => 0,
Self::Bilinear => 1,
Self::Bicubic => 2,
Self::Lanczos => 3,
Self::Area => 4,
}
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::Nearest => "Nearest",
Self::Bilinear => "Bilinear",
Self::Bicubic => "Bicubic",
Self::Lanczos => "Lanczos",
Self::Area => "Area",
}
}
#[must_use]
pub fn kernel_radius(self) -> u32 {
match self {
Self::Nearest => 0,
Self::Bilinear => 1,
Self::Bicubic => 2,
Self::Lanczos => 3,
Self::Area => 1,
}
}
}
pub struct ResizeKernel {
filter: ResizeFilter,
}
impl ResizeKernel {
#[must_use]
pub fn new(filter: ResizeFilter) -> Self {
Self { filter }
}
#[allow(clippy::too_many_arguments)]
pub fn execute(
&self,
device: &GpuDevice,
input: &[u8],
_src_width: u32,
_src_height: u32,
output: &mut [u8],
dst_width: u32,
dst_height: u32,
) -> Result<()> {
crate::ops::ScaleOperation::scale(
device,
input,
_src_width,
_src_height,
output,
dst_width,
dst_height,
self.filter.into(),
)
}
#[must_use]
pub fn filter(&self) -> ResizeFilter {
self.filter
}
#[must_use]
pub fn output_size(dst_width: u32, dst_height: u32, channels: u32) -> usize {
(dst_width * dst_height * channels) as usize
}
#[must_use]
pub fn estimate_flops(
_src_width: u32,
_src_height: u32,
dst_width: u32,
dst_height: u32,
filter: ResizeFilter,
) -> u64 {
let output_pixels = u64::from(dst_width) * u64::from(dst_height);
let kernel_size = u64::from(filter.kernel_radius());
let samples_per_pixel = (kernel_size * 2 + 1) * (kernel_size * 2 + 1);
output_pixels * samples_per_pixel * 6
}
}
impl From<ResizeFilter> for crate::ops::ScaleFilter {
fn from(filter: ResizeFilter) -> Self {
match filter {
ResizeFilter::Nearest => Self::Nearest,
ResizeFilter::Bilinear => Self::Bilinear,
ResizeFilter::Bicubic => Self::Bicubic,
ResizeFilter::Area => Self::Area,
ResizeFilter::Lanczos => Self::Bicubic, }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resize_filter_properties() {
assert_eq!(ResizeFilter::Nearest.to_id(), 0);
assert_eq!(ResizeFilter::Bilinear.to_id(), 1);
assert_eq!(ResizeFilter::Bicubic.to_id(), 2);
assert_eq!(ResizeFilter::Nearest.kernel_radius(), 0);
assert_eq!(ResizeFilter::Bilinear.kernel_radius(), 1);
assert_eq!(ResizeFilter::Bicubic.kernel_radius(), 2);
assert_eq!(ResizeFilter::Lanczos.kernel_radius(), 3);
}
#[test]
fn test_output_size_calculation() {
assert_eq!(ResizeKernel::output_size(1920, 1080, 4), 1920 * 1080 * 4);
assert_eq!(ResizeKernel::output_size(640, 480, 3), 640 * 480 * 3);
}
#[test]
fn test_flops_estimation() {
let flops = ResizeKernel::estimate_flops(1920, 1080, 960, 540, ResizeFilter::Bilinear);
assert!(flops > 0);
let flops_bicubic =
ResizeKernel::estimate_flops(1920, 1080, 960, 540, ResizeFilter::Bicubic);
assert!(flops_bicubic > flops); }
}