Skip to content

Commit 657b4fd

Browse files
committed
Merge branch 'main' of ssh://github.com/bghira/TrainingSample into bugfix/fix-imdecode-conversion
2 parents d0a6507 + 62f1d6d commit 657b4fd

4 files changed

Lines changed: 80 additions & 19 deletions

File tree

src/cv_compat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ pub mod cvtcolor {
230230
let h = if delta == 0.0 {
231231
0.0
232232
} else if max == r {
233-
60.0 * (((g - b) / delta) % 6.0)
233+
60.0 * (((g - b) / delta).rem_euclid(6.0))
234234
} else if max == g {
235235
60.0 * ((b - r) / delta + 2.0)
236236
} else {

src/opencv_ops.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ndarray::{Array3, ArrayView3};
44
#[cfg(feature = "opencv")]
55
use opencv::{
66
core::{AlgorithmHint, Mat},
7-
imgproc::{cvt_color, resize, COLOR_RGB2GRAY, INTER_LANCZOS4},
7+
imgproc::{cvt_color, resize, COLOR_RGB2GRAY, INTER_LANCZOS4, INTER_LINEAR},
88
prelude::*,
99
};
1010

@@ -30,6 +30,15 @@ impl OpenCVBatchProcessor {
3030
&self,
3131
images: &[ArrayView3<u8>],
3232
target_sizes: &[(u32, u32)], // (width, height)
33+
) -> Result<Vec<Array3<u8>>> {
34+
self.batch_resize_images_with_interpolation(images, target_sizes, INTER_LINEAR)
35+
}
36+
37+
fn batch_resize_images_with_interpolation(
38+
&self,
39+
images: &[ArrayView3<u8>],
40+
target_sizes: &[(u32, u32)], // (width, height)
41+
interpolation: i32,
3342
) -> Result<Vec<Array3<u8>>> {
3443
if images.len() != target_sizes.len() {
3544
anyhow::bail!("Number of images and target sizes must match");
@@ -39,7 +48,7 @@ impl OpenCVBatchProcessor {
3948
.iter()
4049
.zip(target_sizes.iter())
4150
.map(|(image, &(target_width, target_height))| {
42-
self.resize_single_opencv(image, target_width, target_height)
51+
self.resize_single_opencv(image, target_width, target_height, interpolation)
4352
})
4453
.collect::<Result<Vec<_>>>()?;
4554

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

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

@@ -139,7 +148,7 @@ impl OpenCVBatchProcessor {
139148
opencv::core::Size::new(target_width as i32, target_height as i32),
140149
0.0,
141150
0.0,
142-
INTER_LANCZOS4,
151+
interpolation,
143152
)?;
144153

145154
// Convert back to ndarray
@@ -243,7 +252,7 @@ pub fn resize_bilinear_opencv(
243252
target_height: u32,
244253
) -> Result<Array3<u8>> {
245254
let processor = OpenCVBatchProcessor::new();
246-
processor.resize_single_opencv(image, target_width, target_height)
255+
processor.resize_single_opencv(image, target_width, target_height, INTER_LINEAR)
247256
}
248257

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

259268
#[cfg(not(feature = "opencv"))]

src/python_bindings.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -952,17 +952,20 @@ pub fn batch_random_crop_images<'py>(
952952

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

959-
for image in images.iter() {
960-
let img_view = image.as_array();
961-
let luminance = crate::luminance::calculate_luminance_array(&img_view);
962-
luminances.push(luminance);
963-
}
961+
let image_views: Vec<_> = images.iter().map(|image| image.as_array()).collect();
964962

965-
Ok(luminances)
963+
Ok(py.allow_threads(|| {
964+
image_views
965+
.par_iter()
966+
.map(crate::luminance::calculate_luminance_array_sequential)
967+
.collect()
968+
}))
966969
}
967970

968971
// TSR FORMAT CONVERSION OPERATIONS (BENCHMARK WINNERS)

src/tests.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,9 @@ mod edge_case_tests {
332332

333333
#[cfg(test)]
334334
mod cv_compat_tests {
335-
use crate::cv_compat::{imdecode, ImreadFlags};
335+
use crate::cv_compat::{cvt_color, imdecode, ColorConversionCode, ImreadFlags};
336336
use image::{DynamicImage, ImageBuffer, ImageFormat, Luma, LumaA, Rgb, Rgba};
337+
use ndarray::Array3;
337338
use std::io::Cursor;
338339

339340
#[test]
@@ -420,6 +421,20 @@ mod cv_compat_tests {
420421
assert_eq!(decoded[[1, 1, 0]], 23);
421422
assert_eq!(decoded[[1, 1, 1]], 32);
422423
}
424+
425+
#[test]
426+
fn test_rgb_to_hsv_wraps_negative_red_sector_hues() {
427+
let src =
428+
Array3::from_shape_vec((1, 1, 3), vec![255, 0, 128]).expect("shape should be valid");
429+
430+
let hsv = cvt_color(&src.view(), ColorConversionCode::ColorRgb2Hsv).unwrap();
431+
432+
// This color sits in the red sector with g < b, so hue should wrap near 330 degrees.
433+
// OpenCV stores hue in [0, 179], so we expect approximately 165 instead of clamping to 0.
434+
assert!(hsv[[0, 0, 0]] >= 160);
435+
assert_eq!(hsv[[0, 0, 1]], 255);
436+
assert_eq!(hsv[[0, 0, 2]], 255);
437+
}
423438
}
424439

425440
// OpenCV specific tests
@@ -429,7 +444,7 @@ mod opencv_tests {
429444
use super::*;
430445

431446
#[cfg(feature = "opencv")]
432-
use crate::opencv_ops::OpenCVBatchProcessor;
447+
use crate::opencv_ops::{resize_bilinear_opencv, resize_lanczos4_opencv, OpenCVBatchProcessor};
433448

434449
#[test]
435450
#[cfg(feature = "opencv")]
@@ -448,6 +463,40 @@ mod opencv_tests {
448463
assert_eq!(resized[0].dim(), (128, 128, 3));
449464
}
450465

466+
#[test]
467+
#[cfg(feature = "opencv")]
468+
fn test_opencv_resize_interpolation_contract() {
469+
let processor = OpenCVBatchProcessor::new();
470+
let high_frequency =
471+
Array3::from_shape_fn(
472+
(4, 4, 3),
473+
|(y, x, c)| {
474+
if (x + y + c) % 2 == 0 {
475+
0
476+
} else {
477+
255
478+
}
479+
},
480+
);
481+
482+
let bilinear = resize_bilinear_opencv(&high_frequency.view(), 7, 7).unwrap();
483+
let lanczos = resize_lanczos4_opencv(&high_frequency.view(), 7, 7).unwrap();
484+
assert_ne!(
485+
bilinear, lanczos,
486+
"bilinear and lanczos paths should use different OpenCV interpolation modes"
487+
);
488+
489+
let batch_linear = processor
490+
.batch_resize_images(&[high_frequency.view()], &[(7, 7)])
491+
.unwrap();
492+
let batch_lanczos = processor
493+
.batch_resize_lanczos4(&[high_frequency.view()], &[(7, 7)])
494+
.unwrap();
495+
496+
assert_eq!(batch_linear[0], bilinear);
497+
assert_eq!(batch_lanczos[0], lanczos);
498+
}
499+
451500
#[test]
452501
#[cfg(feature = "opencv")]
453502
fn test_opencv_batch_luminance() {

0 commit comments

Comments
 (0)