Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/opencv_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ndarray::{Array3, ArrayView3};
#[cfg(feature = "opencv")]
use opencv::{
core::{AlgorithmHint, Mat},
imgproc::{cvt_color, resize, COLOR_RGB2GRAY, INTER_LANCZOS4},
imgproc::{cvt_color, resize, COLOR_RGB2GRAY, INTER_LANCZOS4, INTER_LINEAR},
prelude::*,
};

Expand All @@ -30,6 +30,15 @@ impl OpenCVBatchProcessor {
&self,
images: &[ArrayView3<u8>],
target_sizes: &[(u32, u32)], // (width, height)
) -> Result<Vec<Array3<u8>>> {
self.batch_resize_images_with_interpolation(images, target_sizes, INTER_LINEAR)
}

fn batch_resize_images_with_interpolation(
&self,
images: &[ArrayView3<u8>],
target_sizes: &[(u32, u32)], // (width, height)
interpolation: i32,
) -> Result<Vec<Array3<u8>>> {
if images.len() != target_sizes.len() {
anyhow::bail!("Number of images and target sizes must match");
Expand All @@ -39,7 +48,7 @@ impl OpenCVBatchProcessor {
.iter()
.zip(target_sizes.iter())
.map(|(image, &(target_width, target_height))| {
self.resize_single_opencv(image, target_width, target_height)
self.resize_single_opencv(image, target_width, target_height, interpolation)
})
.collect::<Result<Vec<_>>>()?;

Expand Down Expand Up @@ -111,8 +120,7 @@ impl OpenCVBatchProcessor {
images: &[ArrayView3<u8>],
target_sizes: &[(u32, u32)], // (width, height)
) -> Result<Vec<Array3<u8>>> {
// Use the same implementation as batch_resize_images but with Lanczos4
self.batch_resize_images(images, target_sizes)
self.batch_resize_images_with_interpolation(images, target_sizes, INTER_LANCZOS4)
}

/// Single image resize using OpenCV
Expand All @@ -121,6 +129,7 @@ impl OpenCVBatchProcessor {
image: &ArrayView3<u8>,
target_width: u32,
target_height: u32,
interpolation: i32,
) -> Result<Array3<u8>> {
let (_height, _width, channels) = image.dim();

Expand All @@ -139,7 +148,7 @@ impl OpenCVBatchProcessor {
opencv::core::Size::new(target_width as i32, target_height as i32),
0.0,
0.0,
INTER_LANCZOS4,
interpolation,
)?;

// Convert back to ndarray
Expand Down Expand Up @@ -243,7 +252,7 @@ pub fn resize_bilinear_opencv(
target_height: u32,
) -> Result<Array3<u8>> {
let processor = OpenCVBatchProcessor::new();
processor.resize_single_opencv(image, target_width, target_height)
processor.resize_single_opencv(image, target_width, target_height, INTER_LINEAR)
}

#[cfg(feature = "opencv")]
Expand All @@ -253,7 +262,7 @@ pub fn resize_lanczos4_opencv(
target_height: u32,
) -> Result<Array3<u8>> {
let processor = OpenCVBatchProcessor::new();
processor.resize_single_opencv(image, target_width, target_height)
processor.resize_single_opencv(image, target_width, target_height, INTER_LANCZOS4)
}

#[cfg(not(feature = "opencv"))]
Expand Down
21 changes: 12 additions & 9 deletions src/python_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,17 +952,20 @@ pub fn batch_random_crop_images<'py>(

#[cfg(feature = "python-bindings")]
#[pyfunction]
pub fn batch_calculate_luminance(images: Vec<PyReadonlyArray3<u8>>) -> PyResult<Vec<f64>> {
// Keep original sequential implementation for fair comparison
let mut luminances = Vec::with_capacity(images.len());
pub fn batch_calculate_luminance(
py: Python<'_>,
images: Vec<PyReadonlyArray3<u8>>,
) -> PyResult<Vec<f64>> {
use rayon::prelude::*;

for image in images.iter() {
let img_view = image.as_array();
let luminance = crate::luminance::calculate_luminance_array(&img_view);
luminances.push(luminance);
}
let image_views: Vec<_> = images.iter().map(|image| image.as_array()).collect();

Ok(luminances)
Ok(py.allow_threads(|| {
image_views
.par_iter()
.map(crate::luminance::calculate_luminance_array_sequential)
.collect()
}))
Comment thread
bghira marked this conversation as resolved.
}

// TSR FORMAT CONVERSION OPERATIONS (BENCHMARK WINNERS)
Expand Down
36 changes: 35 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ mod opencv_tests {
use super::*;

#[cfg(feature = "opencv")]
use crate::opencv_ops::OpenCVBatchProcessor;
use crate::opencv_ops::{resize_bilinear_opencv, resize_lanczos4_opencv, OpenCVBatchProcessor};

#[test]
#[cfg(feature = "opencv")]
Expand All @@ -356,6 +356,40 @@ mod opencv_tests {
assert_eq!(resized[0].dim(), (128, 128, 3));
}

#[test]
#[cfg(feature = "opencv")]
fn test_opencv_resize_interpolation_contract() {
let processor = OpenCVBatchProcessor::new();
let high_frequency =
Array3::from_shape_fn(
(4, 4, 3),
|(y, x, c)| {
if (x + y + c) % 2 == 0 {
0
} else {
255
}
},
);

let bilinear = resize_bilinear_opencv(&high_frequency.view(), 7, 7).unwrap();
let lanczos = resize_lanczos4_opencv(&high_frequency.view(), 7, 7).unwrap();
assert_ne!(
bilinear, lanczos,
"bilinear and lanczos paths should use different OpenCV interpolation modes"
);

let batch_linear = processor
.batch_resize_images(&[high_frequency.view()], &[(7, 7)])
.unwrap();
let batch_lanczos = processor
.batch_resize_lanczos4(&[high_frequency.view()], &[(7, 7)])
.unwrap();

assert_eq!(batch_linear[0], bilinear);
assert_eq!(batch_lanczos[0], lanczos);
}

#[test]
#[cfg(feature = "opencv")]
fn test_opencv_batch_luminance() {
Expand Down
Loading