mirror of
https://github.com/Cykooz/fast_image_resize.git
synced 2026-10-08 01:11:09 +00:00
Added Custom variant for FilterType enum and corresponding Filter structure.
This commit is contained in:
+6
-1
@@ -1,14 +1,19 @@
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
### Added
|
||||
|
||||
- Added `Custom` variant for `FilterType` enum and corresponding `Filter` structure.
|
||||
|
||||
### Changed
|
||||
|
||||
- Slightly improved (about 3%) speed of `AVX2` implementation of `Convolution` trait
|
||||
for `U8x3` and `U8x4` images.
|
||||
- **BREAKING**: Changed internal data type for `U8x4` structure.
|
||||
Now it is `[u8; 4]` instead of `u32`.
|
||||
- Significantly improved (4.5 times on `x86_64`) speed of vertical convolution pass implemented
|
||||
in native Rust for `U8`, `U8x2`, `U8x3` and `U8x4` images.
|
||||
- Changed order of convolution passes for `U8`, `U8x2`, `U8x3` and `U8x4` images.
|
||||
Now the vertical pass is the first and the horizontal pass is the second.
|
||||
- **BREAKING**: Changed internal data type for `U8x4` structure. Not it is `[u8; 4]` instead of `u32`.
|
||||
|
||||
## [2.7.3] - 2023-05-07
|
||||
|
||||
|
||||
@@ -1,7 +1,77 @@
|
||||
use std::f64::consts::PI;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type FilterFn<'a> = &'a dyn Fn(f64) -> f64;
|
||||
|
||||
/// Description of custom filter for image convolution.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Filter<'f> {
|
||||
/// Name of filter
|
||||
name: &'static str,
|
||||
/// Filter function
|
||||
func: FilterFn<'f>,
|
||||
/// Minimal "radius" of kernel in pixels
|
||||
support: f64,
|
||||
}
|
||||
|
||||
impl<'f> PartialEq for Filter<'f> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.support == other.support && self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f> Eq for Filter<'f> {}
|
||||
|
||||
impl<'f> Debug for Filter<'f> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Filter")
|
||||
.field("name", &self.name)
|
||||
.field("support", &self.support)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CreateFilterError {
|
||||
/// Value of 'support' argument must be finite and greater than 0.0
|
||||
#[error("Value of 'support' argument must be finite and greater than 0.0")]
|
||||
InvalidSupport,
|
||||
}
|
||||
|
||||
impl<'f> Filter<'f> {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - Name of filter
|
||||
/// * `func` - Filter function
|
||||
/// * `support` - Minimal "radius" of kernel in pixels
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
func: FilterFn<'f>,
|
||||
support: f64,
|
||||
) -> Result<Self, CreateFilterError> {
|
||||
if support.is_finite() && support > 0.0 {
|
||||
Ok(Self {
|
||||
name,
|
||||
func,
|
||||
support,
|
||||
})
|
||||
} else {
|
||||
Err(CreateFilterError::InvalidSupport)
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of filter
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Minimal "radius" of kernel in pixels
|
||||
pub fn support(&self) -> f64 {
|
||||
self.support
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum FilterType {
|
||||
@@ -43,11 +113,13 @@ pub enum FilterType {
|
||||
/// Minimal kernel size 6x6 px.
|
||||
#[default]
|
||||
Lanczos3,
|
||||
/// Custom filter function.
|
||||
Custom(Filter<'static>),
|
||||
}
|
||||
|
||||
/// Returns reference to filter function and value of `filter_support`.
|
||||
#[inline]
|
||||
pub fn get_filter_func(filter_type: FilterType) -> (FilterFn<'static>, f64) {
|
||||
pub(crate) fn get_filter_func(filter_type: FilterType) -> (FilterFn<'static>, f64) {
|
||||
match filter_type {
|
||||
FilterType::Box => (&box_filter, 0.5),
|
||||
FilterType::Bilinear => (&bilinear_filter, 1.0),
|
||||
@@ -55,6 +127,7 @@ pub fn get_filter_func(filter_type: FilterType) -> (FilterFn<'static>, f64) {
|
||||
FilterType::CatmullRom => (&catmul_filter, 2.0),
|
||||
FilterType::Mitchell => (&mitchell_filter, 2.0),
|
||||
FilterType::Lanczos3 => (&lanczos_filter, 3.0),
|
||||
FilterType::Custom(custom) => (custom.func, custom.support),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
pub use filters::{get_filter_func, FilterType};
|
||||
pub use filters::*;
|
||||
|
||||
use crate::pixels::PixelExt;
|
||||
use crate::CpuExtensions;
|
||||
@@ -50,20 +50,20 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Bound {
|
||||
pub(crate) struct Bound {
|
||||
pub start: u32,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Coefficients {
|
||||
pub(crate) struct Coefficients {
|
||||
pub values: Vec<f64>,
|
||||
pub window_size: usize,
|
||||
pub bounds: Vec<Bound>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CoefficientsChunk<'a> {
|
||||
pub(crate) struct CoefficientsChunk<'a> {
|
||||
pub start: u32,
|
||||
pub values: &'a [f64],
|
||||
}
|
||||
@@ -85,7 +85,7 @@ impl Coefficients {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn precompute_coefficients(
|
||||
pub(crate) fn precompute_coefficients(
|
||||
in_size: NonZeroU32,
|
||||
in0: f64, // Left border for cropping
|
||||
in1: f64, // Right border for cropping
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
pub use alpha::errors::*;
|
||||
pub use color::mappers::*;
|
||||
pub use color::PixelComponentMapper;
|
||||
pub use convolution::FilterType;
|
||||
pub use convolution::*;
|
||||
pub use dynamic_image_view::{
|
||||
change_type_of_pixel_components_dyn, DynamicImageView, DynamicImageViewMut,
|
||||
};
|
||||
|
||||
+71
-11
@@ -407,7 +407,7 @@ mod not_u8x4 {
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[2923557],
|
||||
[2923555],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -435,7 +435,7 @@ mod not_u8x4 {
|
||||
P::upscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[1148811406],
|
||||
[1148811829],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -463,7 +463,7 @@ mod not_u8x4 {
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[2923557, 6122818],
|
||||
[2923555, 6122718],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -495,7 +495,7 @@ mod not_u8x4 {
|
||||
P::upscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[1146283728, 2364890194],
|
||||
[1146284886, 2364890085],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -527,7 +527,7 @@ mod not_u8x4 {
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[2942479, 2947850, 2885072],
|
||||
[2942547, 2947799, 2885025],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -559,7 +559,7 @@ mod not_u8x4 {
|
||||
P::upscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[1156107005, 1158443335, 1135101759],
|
||||
[1156107445, 1158443938, 1135102297],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -814,11 +814,16 @@ mod not_u8x4 {
|
||||
}
|
||||
|
||||
mod u8x4 {
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use fast_image_resize::Filter;
|
||||
|
||||
use super::*;
|
||||
|
||||
type P = U8x4;
|
||||
|
||||
#[test]
|
||||
fn downscale_u8x4() {
|
||||
type P = U8x4;
|
||||
P::downscale_test(
|
||||
ResizeAlg::Nearest,
|
||||
CpuExtensions::None,
|
||||
@@ -843,20 +848,19 @@ mod u8x4 {
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[2942479, 2947850, 2885072, 6122818],
|
||||
[2942547, 2947799, 2885025, 6122718],
|
||||
);
|
||||
|
||||
P::downscale_test(
|
||||
ResizeAlg::SuperSampling(FilterType::Lanczos3, 2),
|
||||
cpu_extensions,
|
||||
[2942546, 2947627, 2884866, 6123158],
|
||||
[2942426, 2947750, 2884861, 6123019],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upscale_u8x4() {
|
||||
type P = U8x4;
|
||||
P::upscale_test(
|
||||
ResizeAlg::Nearest,
|
||||
CpuExtensions::None,
|
||||
@@ -881,8 +885,64 @@ mod u8x4 {
|
||||
P::upscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
cpu_extensions,
|
||||
[1155201788, 1152688479, 1123328716, 2364890194],
|
||||
[1155201879, 1152689565, 1123329272, 2364890085],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_filter_u8x4() {
|
||||
std::env::set_var("DONT_SAVE_RESULT", "1");
|
||||
const LANCZOS3_RESULT: [u64; 4] = [2942547, 2947799, 2885025, 6122718];
|
||||
const LANCZOS4_RESULT: [u64; 4] = [2943083, 2948315, 2885436, 6122629];
|
||||
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
CpuExtensions::None,
|
||||
LANCZOS3_RESULT,
|
||||
);
|
||||
|
||||
fn sinc_filter(mut x: f64) -> f64 {
|
||||
if x == 0.0 {
|
||||
1.0
|
||||
} else {
|
||||
x *= PI;
|
||||
x.sin() / x
|
||||
}
|
||||
}
|
||||
|
||||
fn lanczos3_filter(x: f64) -> f64 {
|
||||
if (-3.0..3.0).contains(&x) {
|
||||
sinc_filter(x) * sinc_filter(x / 3.)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
for bad_support in [0.0, -1.0, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
|
||||
assert!(Filter::new("bad_support", &lanczos3_filter, bad_support).is_err());
|
||||
}
|
||||
|
||||
let my_lanczos3 = Filter::new("MyLanczos3", &lanczos3_filter, 3.0).unwrap();
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Custom(my_lanczos3)),
|
||||
CpuExtensions::None,
|
||||
LANCZOS3_RESULT,
|
||||
);
|
||||
|
||||
fn lanczos4_filter(x: f64) -> f64 {
|
||||
if (-4.0..4.0).contains(&x) {
|
||||
sinc_filter(x) * sinc_filter(x / 4.)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
let my_lanczos4 = Filter::new("MyLanczos4", &lanczos4_filter, 4.0).unwrap();
|
||||
P::downscale_test(
|
||||
ResizeAlg::Convolution(FilterType::Custom(my_lanczos4)),
|
||||
CpuExtensions::None,
|
||||
LANCZOS4_RESULT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user