Added new resize algorythm ResizeAlg::Interpolation (#32).

This commit is contained in:
Kirill Kuzminykh
2024-07-18 23:33:17 +03:00
parent f82b42725e
commit 49bad1bed0
12 changed files with 370 additions and 274 deletions
+10
View File
@@ -1,3 +1,13 @@
## [Unreleased] - ReleaseDate
### Added
- Added new resize algorythm `ResizeAlg::Interpolation` (#32).
It is like `ResizeAlg::Convolution` but with fixed kernel size.
This algorithm can be useful if you want to get a result similar
to `OpenCV` (except `INTER_AREA` interpolation).
## [4.1.0] - 2024-07-14
### Added
Generated
+48 -48
View File
@@ -113,7 +113,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -171,9 +171,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bitstream-io"
version = "2.4.2"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "415f8399438eb5e4b2f73ed3152a3448b98149dda642a957ee704e1daa5cf1d8"
checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499"
[[package]]
name = "block-buffer"
@@ -196,9 +196,9 @@ dependencies = [
[[package]]
name = "built"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6a6c0b39c38fd754ac338b00a88066436389c0f029da5d37d1e01091d9b7c17"
checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4"
[[package]]
name = "bumpalo"
@@ -232,13 +232,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.104"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
@@ -326,9 +325,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.8"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [
"clap_builder",
"clap_derive",
@@ -346,9 +345,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.8"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [
"anstream",
"anstyle",
@@ -365,7 +364,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -503,9 +502,9 @@ dependencies = [
[[package]]
name = "document-features"
version = "0.2.8"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0"
dependencies = [
"litrs",
]
@@ -576,6 +575,7 @@ dependencies = [
"nix",
"num-traits",
"png",
"rayon",
"resize",
"rgb",
"serde",
@@ -776,12 +776,12 @@ dependencies = [
[[package]]
name = "image-webp"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d"
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904"
dependencies = [
"byteorder-lite",
"thiserror",
"quick-error",
]
[[package]]
@@ -808,7 +808,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1056,7 +1056,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1096,9 +1096,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
version = "11.1.3"
version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "parse-zoneinfo"
@@ -1152,7 +1152,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1254,7 +1254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
dependencies = [
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1348,9 +1348,9 @@ dependencies = [
[[package]]
name = "ravif"
version = "0.11.7"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67376f469e7e7840d0040bbf4b9b3334005bb167f814621326e4c7ab8cd6e944"
checksum = "c6ba61c28ba24c0cf8406e025cb29a742637e3f70776e61c27a8a8b72a042d12"
dependencies = [
"avif-serialize",
"imgref",
@@ -1435,9 +1435,9 @@ dependencies = [
[[package]]
name = "rgb"
version = "0.8.40"
version = "0.8.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523"
dependencies = [
"bytemuck",
]
@@ -1465,22 +1465,22 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.203"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1579,9 +1579,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.68"
version = "2.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
dependencies = [
"proc-macro2",
"quote",
@@ -1603,9 +1603,9 @@ dependencies = [
[[package]]
name = "target-lexicon"
version = "0.12.14"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2"
[[package]]
name = "tera"
@@ -1639,22 +1639,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
]
[[package]]
@@ -1680,9 +1680,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
dependencies = [
"serde",
"serde_spanned",
@@ -1701,9 +1701,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.14"
version = "0.22.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
dependencies = [
"indexmap",
"serde",
@@ -1846,7 +1846,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
"wasm-bindgen-shared",
]
@@ -1868,7 +1868,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
"syn 2.0.71",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
+4 -3
View File
@@ -23,7 +23,8 @@ exclude = ["/data"]
cfg-if = "1.0"
num-traits = "0.2.19"
thiserror = "1.0"
document-features = "0.2.8"
document-features = "0.2.10"
rayon = "1.10.0"
# Optional dependencies
image = { version = "0.25.1", optional = true }
bytemuck = { version = "1.16", optional = true }
@@ -35,13 +36,13 @@ bytemuck = { version = "1.16", optional = true }
## type from the `image` crate.
image = ["dep:image", "dep:bytemuck"]
for_testing = ["image"]
only_u8x4 = [] # This can be used to experiment with the crate's code.
only_u8x4 = ["testing/only_u8x4"] # This can be used to experiment with the crate's code.
[dev-dependencies]
fast_image_resize = { path = ".", features = ["for_testing"] }
resize = { version = "0.8.4", default-features = false, features = ["std"] }
rgb = "0.8.37"
rgb = "0.8.45"
png = "0.17.13"
serde = { version = "1.0", features = ["serde_derive"] }
serde_json = "1.0"
+1 -1
View File
@@ -21,7 +21,7 @@ Supported pixel formats and available optimizations:
| U16x3 | Three `u16` components per pixel (e.g. RGB16) | + | + | + | + |
| U16x4 | Four `u16` components per pixel (e.g. RGBA16, RGBx16, CMYK16) | + | + | + | + |
| I32 | One `i32` component per pixel (e.g. L) | - | - | - | - |
| F32 | One `f32` component per pixel (e.g. L) | + | + | - | - |
| F32 | One `f32` component per pixel (e.g. L32F) | + | + | - | - |
| F32x2 | Two `f32` components per pixel (e.g. LA32F) | + | + | - | - |
| F32x3 | Three `f32` components per pixel (e.g. RGB32F) | + | + | - | - |
| F32x4 | Four `f32` components per pixel (e.g. RGBA32F) | + | + | - | - |
+7
View File
@@ -31,6 +31,7 @@ pub fn change_type_of_pixel_components(
use PixelType as PT;
#[cfg(not(feature = "only_u8x4"))]
match src_pixel_type {
PixelType::U8 => map_dst!(
U8,
@@ -128,6 +129,12 @@ pub fn change_type_of_pixel_components(
(PT::F32x4, F32x4)
),
}
#[cfg(feature = "only_u8x4")]
match src_pixel_type {
PixelType::U8x4 => map_dst!(U8x4, dst_pixel_type, (PT::U8x4, U8x4)),
_ => Err(MappingError::UnsupportedCombinationOfImageTypes),
}
}
#[inline(always)]
+3 -3
View File
@@ -82,7 +82,7 @@ pub enum FilterType {
///
/// Minimal kernel size 1x1 px.
Box,
/// Bilinear filter calculate the output pixel value using linear
/// Bilinear filter calculates the output pixel value using linear
/// interpolation on all pixels that may contribute to the output value.
///
/// Minimal kernel size 2x2 px.
@@ -91,11 +91,11 @@ pub enum FilterType {
/// providing the image downscaling quality comparable to bicubic
/// (`CatmulRom` or `Mitchell`). Produces a sharper image than `Bilinear`,
/// doesn't have dislocations on local level like with `Box`.
/// The filter don’t show good quality for the image upscaling.
/// The filter doesn't show good quality for the image upscaling.
///
/// Minimal kernel size 2x2 px.
Hamming,
/// Catmull-Rom bicubic filter calculate the output pixel value using
/// Catmull-Rom bicubic filter calculates the output pixel value using
/// cubic interpolation on all pixels that may contribute to the output
/// value.
///
+12 -6
View File
@@ -85,11 +85,12 @@ impl Coefficients {
pub(crate) fn precompute_coefficients(
in_size: u32,
in0: f64, // Left border for cropping
in1: f64, // Right border for cropping
in0: f64, // Left/top border for cropping
in1: f64, // Right/bottom border for cropping
out_size: u32,
filter: fn(f64) -> f64,
filter_support: f64,
adaptive_kernel_size: bool,
) -> Coefficients {
if in_size == 0 || out_size == 0 {
return Coefficients::default();
@@ -98,7 +99,11 @@ pub(crate) fn precompute_coefficients(
if scale <= 0. {
return Coefficients::default();
}
let filter_scale = scale.max(1.0);
let filter_scale = if adaptive_kernel_size {
scale.max(1.0)
} else {
1.0
};
// Determine filter radius size (length of resampling filter)
let filter_radius = filter_support * filter_scale;
@@ -113,7 +118,7 @@ pub(crate) fn precompute_coefficients(
let mut bounds: Vec<Bound> = Vec::with_capacity(out_size as usize);
for out_x in 0..out_size {
// Find the point in the input image corresponding to the centre
// Find the point in the input image corresponding to the center
// of the current pixel in the output image.
let in_center = in0 + (out_x as f64 + 0.5) * scale;
@@ -131,14 +136,15 @@ pub(crate) fn precompute_coefficients(
// (x + 0.5) - in_center => x - (in_center - 0.5) => x - center
let center = in_center - 0.5;
// Calculate the weight of each input pixel from the given x-range.
for x in x_min..x_max {
let w: f64 = filter((x as f64 - center) * recip_filter_scale);
coeffs.push(w);
ww += w;
}
if ww != 0.0 {
// Normalise values of coefficients.
// Sum of coefficients must be equal to 1.0.
// Normalise values of weights.
// The sum of weights must be equal to 1.0.
coeffs[cur_index..].iter_mut().for_each(|w| *w /= ww);
}
// Remaining values should stay empty if they are used despite x_max.
+60 -25
View File
@@ -12,6 +12,11 @@ use crate::{
pub enum ResizeAlg {
Nearest,
Convolution(FilterType),
/// It is like `Convolution` but with fixed kernel size.
///
/// This algorithm can be useful if you want to get a result
/// similar to `OpenCV` (except `INTER_AREA` interpolation).
Interpolation(FilterType),
SuperSampling(FilterType, u8),
}
@@ -101,6 +106,29 @@ impl ResizeOptions {
options.mul_div_alpha = v;
options
}
fn get_crop_box<P: PixelTrait>(
&self,
src_view: &impl ImageView<Pixel = P>,
dst_view: &impl ImageView<Pixel = P>,
) -> CropBox {
match self.cropping {
SrcCropping::None => CropBox {
left: 0.,
top: 0.,
width: src_view.width() as _,
height: src_view.height() as _,
},
SrcCropping::Crop(crop_box) => crop_box,
SrcCropping::FitIntoDestination(centering) => CropBox::fit_src_into_dst_size(
src_view.width(),
src_view.height(),
dst_view.width(),
dst_view.height(),
Some(centering),
),
}
}
}
/// Methods of this structure used to resize images.
@@ -199,24 +227,7 @@ impl Resizer {
let default_options = ResizeOptions::default();
let options = options.into().unwrap_or(&default_options);
let crop_box = match options.cropping {
SrcCropping::None => CropBox {
left: 0.,
top: 0.,
width: src_view.width() as _,
height: src_view.height() as _,
},
SrcCropping::Crop(crop_box) => crop_box,
SrcCropping::FitIntoDestination(centering) => CropBox::fit_src_into_dst_size(
src_view.width(),
src_view.height(),
dst_view.width(),
dst_view.height(),
Some(centering),
),
};
let cropped_src_view = CroppedSrcImageView::cropped(src_view, crop_box)?;
let crop_box = options.get_crop_box(src_view, dst_view);
if crop_box.width == 0.
|| crop_box.height == 0.
|| dst_view.width() == 0
@@ -226,6 +237,8 @@ impl Resizer {
return Ok(());
}
let cropped_src_view = CroppedSrcImageView::cropped(src_view, crop_box)?;
if copy_image(&cropped_src_view, dst_view).is_ok() {
// If `copy_image()` returns `Ok` it means that
// the size of the destination image is equal to
@@ -240,6 +253,14 @@ impl Resizer {
&cropped_src_view,
dst_view,
filter_type,
true,
options.mul_div_alpha,
),
ResizeAlg::Interpolation(filter_type) => self.resample_convolution(
&cropped_src_view,
dst_view,
filter_type,
false,
options.mul_div_alpha,
),
ResizeAlg::SuperSampling(filter_type, multiplicity) => self.resample_super_sampling(
@@ -294,6 +315,7 @@ impl Resizer {
cropped_src_view: &CroppedSrcImageView<impl ImageView<Pixel = P>>,
dst_view: &mut impl ImageViewMut<Pixel = P>,
filter_type: FilterType,
adaptive_kernel_size: bool,
use_alpha: bool,
) {
if use_alpha && self.mul_div.is_supported(P::pixel_type()) {
@@ -315,7 +337,12 @@ impl Resizer {
cropped_src_view.crop_box(),
)
};
self.do_convolution(&cropped_premultiplied_src, dst_view, filter_type);
self.do_convolution(
&cropped_premultiplied_src,
dst_view,
filter_type,
adaptive_kernel_size,
);
self.mul_div.divide_alpha_inplace_typed(dst_view).unwrap();
self.alpha_buffer = alpha_buffer;
return;
@@ -323,7 +350,12 @@ impl Resizer {
self.alpha_buffer = alpha_buffer;
}
self.do_convolution(cropped_src_view, dst_view, filter_type);
self.do_convolution(
cropped_src_view,
dst_view,
filter_type,
adaptive_kernel_size,
);
}
fn do_convolution<P: PixelTrait>(
@@ -331,6 +363,7 @@ impl Resizer {
cropped_src_view: &CroppedSrcImageView<impl ImageView<Pixel = P>>,
dst_view: &mut impl ImageViewMut<Pixel = P>,
filter_type: FilterType,
adaptive_kernel_size: bool,
) {
let src_view = cropped_src_view.image_view();
let crop_box = cropped_src_view.crop_box();
@@ -352,6 +385,7 @@ impl Resizer {
dst_width,
filter_fn,
filter_support,
adaptive_kernel_size,
)
});
@@ -366,13 +400,14 @@ impl Resizer {
dst_height,
filter_fn,
filter_support,
adaptive_kernel_size,
)
});
match (horiz_coeffs, vert_coeffs) {
(Some(mut horiz_coeffs), Some(mut vert_coeffs)) => {
if P::components_is_u8() {
// For an u8-based images it is faster to do the vertical pass first
// For u8-based images, it is faster to do the vertical pass first
// instead of the horizontal.
let x_first = horiz_coeffs.bounds[0].start;
// Last used col in the source image
@@ -475,7 +510,7 @@ impl Resizer {
// size is greater than the required size by multiplicity times.
let factor = width_scale.min(height_scale) / multiplicity as f64;
if factor > 1.2 {
// First step is resizing the source image by fastest algorithm.
// The first step is resizing the source image by the fastest algorithm.
// The temporary image will be about ``multiplicity`` times larger
// than required.
let tmp_width = (crop_box.width / factor).round() as u32;
@@ -485,14 +520,14 @@ impl Resizer {
let mut tmp_img =
get_temp_image_from_buffer(&mut super_sampling_buffer, tmp_width, tmp_height);
resample_nearest(cropped_src_view, &mut tmp_img);
// Second step is resizing the temporary image with a convolution.
// The second step is resizing the temporary image with a convolution.
let cropped_tmp_img = CroppedSrcImageView::new(&tmp_img);
self.resample_convolution(&cropped_tmp_img, dst_view, filter_type, use_alpha);
self.resample_convolution(&cropped_tmp_img, dst_view, filter_type, true, use_alpha);
self.super_sampling_buffer = super_sampling_buffer;
} else {
// There is no point in doing the resizing in two steps.
// We immediately resize the original image with a convolution.
self.resample_convolution(cropped_src_view, dst_view, filter_type, use_alpha);
self.resample_convolution(cropped_src_view, dst_view, filter_type, true, use_alpha);
}
}
}
+4
View File
@@ -9,5 +9,9 @@ fast_image_resize = { path = "..", features = ["for_testing"] }
image = "0.25.1"
[features]
only_u8x4 = [] # This can be used to experiment with the crate's code.
[package.metadata.release]
publish = false
+193 -188
View File
@@ -163,48 +163,210 @@ pub trait PixelTestingExt: PixelTrait {
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8>;
}
impl PixelTestingExt for U8 {
type ImagePixel = image::Luma<u8>;
type Container = Vec<u8>;
#[cfg(not(feature = "only_u8x4"))]
pub mod not_u8x4 {
use super::*;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma8()
impl PixelTestingExt for U8 {
type ImagePixel = image::Luma<u8>;
type Container = Vec<u8>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma8()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
}
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
}
}
impl PixelTestingExt for U8x2 {
type ImagePixel = image::LumaA<u8>;
type Container = Vec<u8>;
impl PixelTestingExt for U8x2 {
type ImagePixel = image::LumaA<u8>;
type Container = Vec<u8>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha8()
}
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha8()
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
}
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
}
}
impl PixelTestingExt for U8x3 {
type ImagePixel = image::Rgb<u8>;
type Container = Vec<u8>;
impl PixelTestingExt for U8x3 {
type ImagePixel = image::Rgb<u8>;
type Container = Vec<u8>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb8()
}
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb8()
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
}
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.into_raw()
impl PixelTestingExt for U16 {
type ImagePixel = image::Luma<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
// img.as_raw()
// .iter()
// .enumerate()
// .flat_map(|(i, &c)| ((i & 0xffff) as u16).to_le_bytes())
// .collect()
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x2 {
type ImagePixel = image::LumaA<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x3 {
type ImagePixel = image::Rgb<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x4 {
type ImagePixel = image::Rgba<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgba16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for I32 {
type ImagePixel = image::Luma<i32>;
type Container = Vec<i32>;
fn cpu_extensions() -> Vec<CpuExtensions> {
vec![CpuExtensions::None]
}
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
let image_u16 = img_reader.decode().unwrap().to_luma32f();
ImageBuffer::from_fn(image_u16.width(), image_u16.height(), |x, y| {
let pixel = image_u16.get_pixel(x, y);
image::Luma::from([(pixel.0[0] * i32::MAX as f32).round() as i32])
})
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32 {
type ImagePixel = image::Luma<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32x2 {
type ImagePixel = image::LumaA<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32x3 {
type ImagePixel = image::Rgb<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for F32x4 {
type ImagePixel = image::Rgba<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgba32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
}
@@ -223,163 +385,6 @@ impl PixelTestingExt for U8x4 {
}
}
impl PixelTestingExt for U16 {
type ImagePixel = image::Luma<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
// img.as_raw()
// .iter()
// .enumerate()
// .flat_map(|(i, &c)| ((i & 0xffff) as u16).to_le_bytes())
// .collect()
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x2 {
type ImagePixel = image::LumaA<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x3 {
type ImagePixel = image::Rgb<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for U16x4 {
type ImagePixel = image::Rgba<u16>;
type Container = Vec<u16>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgba16()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for I32 {
type ImagePixel = image::Luma<i32>;
type Container = Vec<i32>;
fn cpu_extensions() -> Vec<CpuExtensions> {
vec![CpuExtensions::None]
}
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
let image_u16 = img_reader.decode().unwrap().to_luma32f();
ImageBuffer::from_fn(image_u16.width(), image_u16.height(), |x, y| {
let pixel = image_u16.get_pixel(x, y);
image::Luma::from([(pixel.0[0] * i32::MAX as f32).round() as i32])
})
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32 {
type ImagePixel = image::Luma<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32x2 {
type ImagePixel = image::LumaA<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_luma_alpha32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw()
.iter()
.flat_map(|val| val.to_le_bytes())
.collect()
}
}
impl PixelTestingExt for F32x3 {
type ImagePixel = image::Rgb<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgb32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
impl PixelTestingExt for F32x4 {
type ImagePixel = image::Rgba<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgba32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
pub fn save_result(image: &Image, name: &str) {
if std::env::var("SAVE_RESULT").unwrap_or_else(|_| "".to_owned()) == "" {
return;
+2
View File
@@ -2,6 +2,7 @@ use fast_image_resize as fr;
use fast_image_resize::images::Image;
use fast_image_resize::pixels::*;
#[cfg(not(feature = "only_u8x4"))]
mod gamma_tests {
use super::*;
@@ -78,6 +79,7 @@ mod gamma_tests {
}
}
#[cfg(not(feature = "only_u8x4"))]
mod srgb_tests {
use super::*;
+26
View File
@@ -311,6 +311,16 @@ where
FilterType::Lanczos3 => "lanczos3",
_ => "unknown",
},
ResizeAlg::Interpolation(filter) => match filter {
FilterType::Box => "inter_box",
FilterType::Bilinear => "inter_bilinear",
FilterType::Hamming => "inter_hamming",
FilterType::Mitchell => "inter_mitchell",
FilterType::CatmullRom => "inter_catmullrom",
FilterType::Gaussian => "inter_gaussian",
FilterType::Lanczos3 => "inter_lanczos3",
_ => "inter_unknown",
},
ResizeAlg::SuperSampling(_, _) => "supersampling",
_ => "unknown",
};
@@ -1026,3 +1036,19 @@ mod u8x4 {
}
}
}
#[cfg(not(feature = "only_u8x4"))]
#[test]
fn resize_u8x3_interpolation() {
U8x3::downscale_test(
ResizeAlg::Interpolation(FilterType::Bilinear),
CpuExtensions::None,
[2938733, 2946338, 2883813],
);
U8x3::upscale_test(
ResizeAlg::Interpolation(FilterType::Bilinear),
CpuExtensions::None,
[1156013474, 1158419787, 1135090328],
);
}