Skip to content

Commit 64022dc

Browse files
committed
Handle generic image
1 parent eceee18 commit 64022dc

File tree

3 files changed

+207
-48
lines changed

3 files changed

+207
-48
lines changed

src/imageops/filter_1d.rs

+20
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,26 @@ pub(crate) fn filter_1d_rgba(
929929
filter_1d::<u8, f32, i32, 4>(image, destination, image_size, row_kernel, column_kernel)
930930
}
931931

932+
pub(crate) fn filter_1d_la_f32(
933+
image: &[f32],
934+
destination: &mut [f32],
935+
image_size: FilterImageSize,
936+
row_kernel: &[f32],
937+
column_kernel: &[f32],
938+
) -> Result<(), ImageError> {
939+
filter_1d::<f32, f32, f32, 2>(image, destination, image_size, row_kernel, column_kernel)
940+
}
941+
942+
pub(crate) fn filter_1d_plane_f32(
943+
image: &[f32],
944+
destination: &mut [f32],
945+
image_size: FilterImageSize,
946+
row_kernel: &[f32],
947+
column_kernel: &[f32],
948+
) -> Result<(), ImageError> {
949+
filter_1d::<f32, f32, f32, 1>(image, destination, image_size, row_kernel, column_kernel)
950+
}
951+
932952
pub(crate) fn filter_1d_rgb_f32(
933953
image: &[f32],
934954
destination: &mut [f32],

src/imageops/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ pub use self::affine::{
1717

1818
/// Image sampling
1919
pub use self::sample::{
20-
blur, filter3x3, gaussian_blur_dyn_image, interpolate_bilinear, interpolate_nearest, resize,
20+
blur, filter3x3, interpolate_bilinear, interpolate_nearest, resize,
2121
sample_bilinear, sample_nearest, thumbnail, unsharpen,
2222
};
23+
pub(crate) use self::sample::gaussian_blur_dyn_image;
2324

2425
/// Color operations
2526
pub use self::colorops::{

src/imageops/sample.rs

+185-47
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use std::ops::Mul;
1010
use crate::buffer_::{Gray16Image, GrayAlpha16Image, Rgb16Image, Rgba16Image};
1111
use crate::image::{GenericImage, GenericImageView};
1212
use crate::imageops::filter_1d::{
13-
filter_1d_la, filter_1d_la_u16, filter_1d_plane, filter_1d_plane_u16, filter_1d_rgb,
14-
filter_1d_rgb_f32, filter_1d_rgb_u16, filter_1d_rgba, filter_1d_rgba_f32, filter_1d_rgba_u16,
15-
FilterImageSize,
13+
filter_1d_la, filter_1d_la_f32, filter_1d_la_u16, filter_1d_plane, filter_1d_plane_f32,
14+
filter_1d_plane_u16, filter_1d_rgb, filter_1d_rgb_f32, filter_1d_rgb_u16, filter_1d_rgba,
15+
filter_1d_rgba_f32, filter_1d_rgba_u16, FilterImageSize,
1616
};
1717
use crate::traits::{Enlargeable, Pixel, Primitive};
1818
use crate::utils::clamp;
@@ -1002,58 +1002,53 @@ pub fn blur<I: GenericImageView>(
10021002
where
10031003
I::Pixel: 'static,
10041004
{
1005-
let sigma = if sigma <= 0.0 { 1.0 } else { sigma };
1005+
// let sigma = if sigma <= 0.0 { 1.0 } else { sigma };
1006+
//
1007+
// let mut method = Filter {
1008+
// kernel: Box::new(|x| gaussian(x, sigma)),
1009+
// support: 2.0 * sigma,
1010+
// };
1011+
//
1012+
// let (width, height) = image.dimensions();
1013+
// let is_empty = width == 0 || height == 0;
1014+
//
1015+
// if is_empty {
1016+
// return ImageBuffer::new(width, height);
1017+
// }
1018+
//
1019+
// // Keep width and height the same for horizontal and
1020+
// // vertical sampling.
1021+
// // Note: tmp is not necessarily actually Rgba
1022+
// let tmp: Rgba32FImage = vertical_sample(image, height, &mut method);
1023+
// horizontal_sample(&tmp, width, &mut method)
1024+
gaussian_blur_indirect(image, sigma)
1025+
}
10061026

1007-
let mut method = Filter {
1008-
kernel: Box::new(|x| gaussian(x, sigma)),
1009-
support: 2.0 * sigma,
1010-
};
1027+
fn get_gaussian_kernel_1d(width: usize, sigma: f32) -> Vec<f32> {
1028+
let mut sum_norm: f32 = 0f32;
1029+
let mut kernel = vec![0f32; width];
1030+
let scale = 1f32 / (f32::sqrt(2f32 * f32::consts::PI) * sigma);
1031+
let mean = (width / 2) as f32;
10111032

1012-
let (width, height) = image.dimensions();
1013-
let is_empty = width == 0 || height == 0;
1033+
for (x, weight) in kernel.iter_mut().enumerate() {
1034+
let new_weight = f32::exp(-0.5f32 * f32::powf((x as f32 - mean) / sigma, 2.0f32)) * scale;
1035+
*weight = new_weight;
1036+
sum_norm += new_weight;
1037+
}
10141038

1015-
if is_empty {
1016-
return ImageBuffer::new(width, height);
1039+
if sum_norm != 0f32 {
1040+
let sum_scale = 1f32 / sum_norm;
1041+
for weight in kernel.iter_mut() {
1042+
*weight = weight.mul(sum_scale);
1043+
}
10171044
}
10181045

1019-
// Keep width and height the same for horizontal and
1020-
// vertical sampling.
1021-
// Note: tmp is not necessarily actually Rgba
1022-
let tmp: Rgba32FImage = vertical_sample(image, height, &mut method);
1023-
horizontal_sample(&tmp, width, &mut method)
1046+
kernel
10241047
}
10251048

10261049
/// In previous implementation sigma means radius, which is not the same one
1027-
pub fn gaussian_blur_dyn_image(image: &DynamicImage, radius: f32) -> DynamicImage {
1028-
fn get_sigma_size(kernel_size: usize) -> f32 {
1029-
0.3f32 * ((kernel_size as f32 - 1.) * 0.5f32 - 1f32) + 0.8f32
1030-
}
1031-
1032-
fn get_gaussian_kernel_1d(width: usize, sigma: f32) -> Vec<f32> {
1033-
let mut sum_norm: f32 = 0f32;
1034-
let mut kernel = vec![0f32; width];
1035-
let scale = 1f32 / (f32::sqrt(2f32 * f32::consts::PI) * sigma);
1036-
let mean = (width / 2) as f32;
1037-
1038-
for (x, weight) in kernel.iter_mut().enumerate() {
1039-
let new_weight =
1040-
f32::exp(-0.5f32 * f32::powf((x as f32 - mean) / sigma, 2.0f32)) * scale;
1041-
*weight = new_weight;
1042-
sum_norm += new_weight;
1043-
}
1044-
1045-
if sum_norm != 0f32 {
1046-
let sum_scale = 1f32 / sum_norm;
1047-
for weight in kernel.iter_mut() {
1048-
*weight = weight.mul(sum_scale);
1049-
}
1050-
}
1051-
1052-
kernel
1053-
}
1054-
1055-
let kernel_size = radius.max(1.) as usize * 2 + 1;
1056-
let sigma = get_sigma_size(kernel_size);
1050+
pub(crate) fn gaussian_blur_dyn_image(image: &DynamicImage, sigma: f32) -> DynamicImage {
1051+
let kernel_size = sigma.max(1.) as usize * 2 + 1;
10571052
let gaussian_kernel = get_gaussian_kernel_1d(kernel_size, sigma);
10581053

10591054
let filter_image_size = FilterImageSize {
@@ -1205,6 +1200,149 @@ pub fn gaussian_blur_dyn_image(image: &DynamicImage, radius: f32) -> DynamicImag
12051200
}
12061201
}
12071202

1203+
pub(crate) fn gaussian_blur_indirect<I: GenericImageView>(
1204+
image: &I,
1205+
sigma: f32,
1206+
) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
1207+
where
1208+
I::Pixel: 'static,
1209+
{
1210+
match I::Pixel::CHANNEL_COUNT {
1211+
1 => gaussian_blur_indirect_impl::<I, 1>(image, sigma),
1212+
2 => gaussian_blur_indirect_impl::<I, 2>(image, sigma),
1213+
3 => gaussian_blur_indirect_impl::<I, 3>(image, sigma),
1214+
4 => gaussian_blur_indirect_impl::<I, 4>(image, sigma),
1215+
_ => unimplemented!(),
1216+
}
1217+
}
1218+
1219+
fn gaussian_blur_indirect_impl<I: GenericImageView, const CN: usize>(
1220+
image: &I,
1221+
sigma: f32,
1222+
) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
1223+
where
1224+
I::Pixel: 'static,
1225+
{
1226+
let mut transient = vec![0f32; image.width() as usize * image.height() as usize * CN];
1227+
for (pixel, dst) in image.pixels().zip(transient.chunks_exact_mut(CN)) {
1228+
let px = pixel.2.channels();
1229+
match CN {
1230+
1 => {
1231+
dst[0] = NumCast::from(px[0]).unwrap();
1232+
}
1233+
2 => {
1234+
dst[0] = NumCast::from(px[0]).unwrap();
1235+
dst[1] = NumCast::from(px[1]).unwrap();
1236+
}
1237+
3 => {
1238+
dst[0] = NumCast::from(px[0]).unwrap();
1239+
dst[1] = NumCast::from(px[1]).unwrap();
1240+
dst[2] = NumCast::from(px[2]).unwrap();
1241+
}
1242+
4 => {
1243+
dst[0] = NumCast::from(px[0]).unwrap();
1244+
dst[1] = NumCast::from(px[1]).unwrap();
1245+
dst[2] = NumCast::from(px[2]).unwrap();
1246+
dst[3] = NumCast::from(px[3]).unwrap();
1247+
}
1248+
_ => unreachable!(),
1249+
}
1250+
}
1251+
1252+
let mut transient_dst = vec![0f32; image.width() as usize * image.height() as usize * CN];
1253+
1254+
let kernel_size = sigma.max(1.) as usize * 2 + 1;
1255+
let gaussian_kernel = get_gaussian_kernel_1d(kernel_size, sigma);
1256+
1257+
let filter_image_size = FilterImageSize {
1258+
width: image.width() as usize,
1259+
height: image.height() as usize,
1260+
};
1261+
1262+
match CN {
1263+
1 => {
1264+
filter_1d_plane_f32(
1265+
&transient,
1266+
&mut transient_dst,
1267+
filter_image_size,
1268+
&gaussian_kernel,
1269+
&gaussian_kernel,
1270+
)
1271+
.unwrap();
1272+
}
1273+
2 => {
1274+
filter_1d_la_f32(
1275+
&transient,
1276+
&mut transient_dst,
1277+
filter_image_size,
1278+
&gaussian_kernel,
1279+
&gaussian_kernel,
1280+
)
1281+
.unwrap();
1282+
}
1283+
3 => {
1284+
filter_1d_rgb_f32(
1285+
&transient,
1286+
&mut transient_dst,
1287+
filter_image_size,
1288+
&gaussian_kernel,
1289+
&gaussian_kernel,
1290+
)
1291+
.unwrap();
1292+
}
1293+
4 => {
1294+
filter_1d_rgba_f32(
1295+
&transient,
1296+
&mut transient_dst,
1297+
filter_image_size,
1298+
&gaussian_kernel,
1299+
&gaussian_kernel,
1300+
)
1301+
.unwrap();
1302+
}
1303+
_ => unreachable!(),
1304+
}
1305+
1306+
let mut out = ImageBuffer::new(image.width(), image.height());
1307+
for (dst, src) in out.pixels_mut().zip(transient_dst.chunks_exact_mut(CN)) {
1308+
match CN {
1309+
1 => {
1310+
let v0 = NumCast::from(FloatNearest(src[0])).unwrap();
1311+
#[allow(deprecated)]
1312+
let t = Pixel::from_channels(v0, v0, v0, v0);
1313+
*dst = t;
1314+
}
1315+
2 => {
1316+
let v0 = NumCast::from(FloatNearest(src[0])).unwrap();
1317+
let v1 = NumCast::from(FloatNearest(src[1])).unwrap();
1318+
#[allow(deprecated)]
1319+
let t = Pixel::from_channels(v0, v1, v0, v0);
1320+
*dst = t;
1321+
}
1322+
3 => {
1323+
let v0 = NumCast::from(FloatNearest(src[0])).unwrap();
1324+
let v1 = NumCast::from(FloatNearest(src[1])).unwrap();
1325+
let v2 = NumCast::from(FloatNearest(src[2])).unwrap();
1326+
#[allow(deprecated)]
1327+
let t = Pixel::from_channels(v0, v1, v2, v0);
1328+
*dst = t;
1329+
}
1330+
4 => {
1331+
let v0 = NumCast::from(FloatNearest(src[0])).unwrap();
1332+
let v1 = NumCast::from(FloatNearest(src[1])).unwrap();
1333+
let v2 = NumCast::from(FloatNearest(src[2])).unwrap();
1334+
let v3 = NumCast::from(FloatNearest(src[3])).unwrap();
1335+
#[allow(deprecated)]
1336+
let t = Pixel::from_channels(v0, v1, v2, v3);
1337+
*dst = t;
1338+
}
1339+
_ => unreachable!(),
1340+
}
1341+
}
1342+
1343+
out
1344+
}
1345+
12081346
/// Performs an unsharpen mask on the supplied image.
12091347
/// ```sigma``` is the amount to blur the image by.
12101348
/// ```threshold``` is the threshold for minimal brightness change that will be sharpened.

0 commit comments

Comments
 (0)