-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crate/imageprocs: Add multithreaded resize
- Loading branch information
1 parent
b1ff285
commit 9f5165f
Showing
3 changed files
with
247 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#![allow(dead_code)] | ||
|
||
use crate::traits::NumOps; | ||
pub fn resize_image_bicubic<T>( | ||
pixels: &[T], output: &mut [T], width: usize, height: usize, new_width: usize, | ||
new_height: usize | ||
) where | ||
T: Copy + NumOps<T>, | ||
f32: std::convert::From<T> | ||
{ | ||
// Filter coefficients for bicubic interpolation with Mitchell-Netravali kernel | ||
#[rustfmt::skip] | ||
let filter_coefficients = [ | ||
-0.772, 0.270, -0.024, 0.006, | ||
0.826, -0.688, 0.491, -0.090, | ||
-0.254, 0.870, 0.647, -0.166, | ||
0.064, -0.703, 0.728, 0.319 | ||
]; | ||
|
||
for y in 0..new_height { | ||
for x in 0..new_width { | ||
let new_x = x as f32 / new_width as f32 * width as f32; | ||
let new_y = y as f32 / new_height as f32 * height as f32; | ||
|
||
let x0 = (new_x - 1.0).floor() as usize; | ||
let x1 = x0 + 1; | ||
let x2 = x1 + 1; | ||
let x3 = x2 + 1; | ||
|
||
let y0 = (new_y - 1.0).floor() as usize; | ||
let y1 = y0 + 1; | ||
let y2 = y1 + 1; | ||
let y3 = y2 + 1; | ||
|
||
// Clamp pixel indices to image boundaries | ||
let x0 = x0.min(width - 1); | ||
let x3 = x3.min(width - 1); | ||
let y3 = y3.min(height - 1); | ||
|
||
// Calculate cubic coefficients | ||
let mut a_coeffs = [0.0; 4]; | ||
for i in 0..4 { | ||
a_coeffs[i] = | ||
calculate_cubic_coefficient(new_x - x0 as f32, filter_coefficients[i * 4]); | ||
} | ||
|
||
// Interpolate pixel values | ||
let mut a = 0.0; | ||
for i in 0..4 { | ||
for j in 0..4 { | ||
let offset = (y3 - i) * width + (x3 - j); | ||
a += f32::from(pixels[offset]) * a_coeffs[i] * a_coeffs[j]; | ||
} | ||
} | ||
|
||
output[y * new_width + x] = T::from_f32(a); | ||
} | ||
} | ||
} | ||
|
||
fn calculate_cubic_coefficient(x: f32, a: f32) -> f32 { | ||
return if x < 1.0 { | ||
a * x * x * x + (a + 2.0) * x * x + (a + 1.0) * x | ||
} else if x < 2.0 { | ||
-a * x * x * x + (5.0 * a + 2.0) * x * x - (8.0 * a + 4.0) * x + (4.0 * a + 6.0) | ||
} else { | ||
0.0 | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,61 @@ | ||
use crate::traits::NumOps; | ||
|
||
/// Bilinear interpolation of a single channel, this interpolates a single channel, but not an image | ||
/// | ||
/// | ||
#[allow( | ||
clippy::cast_precision_loss, | ||
clippy::cast_sign_loss, | ||
clippy::cast_possible_truncation | ||
)] | ||
pub fn bilinear_impl<T>( | ||
_in_image: &[T], _out_image: &mut [T], _in_width: usize, _in_height: usize, _out_width: usize, | ||
_out_height: usize | ||
in_channel: &[T], out_channel: &mut [T], in_width: usize, in_height: usize, out_width: usize, | ||
out_height: usize | ||
) where | ||
T: Copy + NumOps<T>, | ||
f64: std::convert::From<T> | ||
f32: std::convert::From<T> | ||
{ | ||
// stump | ||
return; | ||
let w_ratio = 1.0 / out_width as f32 * in_width as f32; | ||
let h_ratio = 1.0 / out_height as f32 * in_height as f32; | ||
|
||
let smaller_image_to_larger = w_ratio < 1.0 && h_ratio < 1.0; | ||
|
||
for y in 0..out_height { | ||
for x in 0..out_width { | ||
let new_x = x as f32 * w_ratio; | ||
let new_y = y as f32 * h_ratio; | ||
// floor and truncate are slow due to handling overflow and such, so avoid them here | ||
let mut x0 = new_x.floor() as usize; | ||
let mut y0 = new_y.floor() as usize; | ||
let mut x1 = x0 + 1; | ||
let mut y1 = y0 + 1; | ||
|
||
// PS: I'm not sure about the impact, but it cuts down on code executed | ||
// the branch is deterministic hence the CPU should have an easy time predicting it | ||
if smaller_image_to_larger { | ||
// in case of result image being greater than source image, it may happen that | ||
// the above go beyond picture dimensions, so clamp them here if they do | ||
// clamp to image width and height | ||
y1 = y1.min(in_height - 1); | ||
y0 = y0.min(in_height - 1); | ||
x1 = x1.min(in_width - 1); | ||
x0 = x0.min(in_width - 1); | ||
} | ||
|
||
let a = new_x - x0 as f32; | ||
let b = new_y - y0 as f32; | ||
|
||
let p00 = f32::from(in_channel[y0 * in_width + x0]); | ||
let p10 = f32::from(in_channel[y0 * in_width + x1]); | ||
let p01 = f32::from(in_channel[y1 * in_width + x0]); | ||
let p11 = f32::from(in_channel[y1 * in_width + x1]); | ||
|
||
let interpolated_pixel = p00 * (1.0 - a) * (1.0 - b) | ||
+ p10 * a * (1.0 - b) | ||
+ p01 * (1.0 - a) * b | ||
+ p11 * a * b; | ||
|
||
out_channel[y * out_width + x] = T::from_f32(interpolated_pixel); | ||
} | ||
} | ||
} |