use ndarray::Array4;
use opencv::{core, dnn, prelude::*};
use std::error::Error;
pub struct OpenCVHelper {
mean: f32,
std: f32,
}
impl OpenCVHelper {
pub fn new(mean: f32, std: f32) -> Self {
Self { mean, std }
}
pub fn prepare_input_tensor(
&self,
image: &Mat,
input_size: (i32, i32),
) -> Result<Array4<f32>, Box<dyn Error>> {
let blob = dnn::blob_from_image(
&image,
1.0 / self.std as f64,
core::Size::new(input_size.0 as i32, input_size.1 as i32),
core::Scalar::new(self.mean as f64, self.mean as f64, self.mean as f64, 0.0),
true,
false,
core::CV_32F,
)?;
let tensor_shape = (1, 3, input_size.1 as usize, input_size.0 as usize);
let tensor_data: Vec<f32> = blob.data_typed()?.to_vec(); let input_tensor = Array4::from_shape_vec(tensor_shape, tensor_data)?;
Ok(input_tensor)
}
pub fn resize_with_aspect_ratio(
&self,
image: &Mat,
target_size: (i32, i32),
) -> Result<(Mat, f32), Box<dyn Error>> {
let orig_width = image.cols() as f32;
let orig_height = image.rows() as f32;
let (input_width, input_height) = target_size;
let im_ratio = orig_height / orig_width;
let model_ratio = input_height as f32 / input_width as f32;
let (new_width, new_height, _, _) = if im_ratio > model_ratio {
let new_height = input_height;
let new_width = ((input_height as f32) / im_ratio).round() as i32;
let x_offset = (input_width - new_width) / 2;
(new_width, new_height, x_offset, 0)
} else {
let new_width = input_width;
let new_height = ((input_width as f32) * im_ratio).round() as i32;
let y_offset = (input_height - new_height) / 2;
(new_width, new_height, 0, y_offset)
};
let det_scale = new_height as f32 / orig_height;
let mut opencv_resized_image = core::Mat::default();
opencv::imgproc::resize(
&image,
&mut opencv_resized_image,
core::Size::new(new_width, new_height),
0.0,
0.0,
opencv::imgproc::INTER_LINEAR,
)?;
let mut det_image = core::Mat::new_rows_cols_with_default(
input_height,
input_width,
core::CV_8UC3,
core::Scalar::all(0.0),
)?;
let mut roi = det_image.roi_mut(core::Rect::new(0, 0, input_height, new_height))?;
opencv_resized_image.copy_to(&mut roi)?;
Ok((det_image, det_scale))
}
pub fn draw_rectangle(&self, image: &mut Mat, rect: core::Rect) -> Result<(), Box<dyn Error>> {
opencv::imgproc::rectangle(
image,
rect,
core::Scalar::new(0.0, 0.0, 255.0, 0.0),
2,
opencv::imgproc::LINE_8,
0,
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use opencv::core::{Mat, Scalar};
#[test]
fn test_new_helper() {
let helper = OpenCVHelper::new(127.5, 128.0);
assert_eq!(helper.mean, 127.5);
assert_eq!(helper.std, 128.0);
}
#[test]
fn test_resize_with_aspect_ratio_landscape() -> Result<(), Box<dyn Error>> {
let test_image = Mat::new_rows_cols_with_default(
50,
100,
core::CV_8UC3,
Scalar::new(255.0, 0.0, 0.0, 0.0),
)?;
let helper = OpenCVHelper::new(127.5, 128.0);
let (resized, scale) = helper.resize_with_aspect_ratio(&test_image, (200, 200))?;
assert_eq!(resized.rows(), 200);
assert_eq!(resized.cols(), 200);
assert!((scale - 2.0).abs() < 0.01);
Ok(())
}
#[test]
fn test_resize_with_aspect_ratio_portrait() -> Result<(), Box<dyn Error>> {
let test_image = Mat::new_rows_cols_with_default(
100,
50,
core::CV_8UC3,
Scalar::new(255.0, 0.0, 0.0, 0.0),
)?;
let helper = OpenCVHelper::new(127.5, 128.0);
let (resized, scale) = helper.resize_with_aspect_ratio(&test_image, (200, 200))?;
assert_eq!(resized.rows(), 200);
assert_eq!(resized.cols(), 200);
assert!((scale - 2.0).abs() < 0.01);
Ok(())
}
#[test]
fn test_prepare_input_tensor() -> Result<(), Box<dyn Error>> {
let test_image = Mat::new_rows_cols_with_default(
100,
100,
core::CV_8UC3,
Scalar::new(255.0, 0.0, 0.0, 0.0),
)?;
let helper = OpenCVHelper::new(127.5, 128.0);
let tensor = helper.prepare_input_tensor(&test_image, (100, 100))?;
assert_eq!(tensor.shape(), &[1, 3, 100, 100]);
let mean = tensor.mean().unwrap();
assert!(mean.abs() < 1.0);
Ok(())
}
#[test]
fn test_prepare_input_tensor_with_different_sizes() -> Result<(), Box<dyn Error>> {
let test_image = Mat::new_rows_cols_with_default(
100,
100,
core::CV_8UC3,
Scalar::new(255.0, 0.0, 0.0, 0.0),
)?;
let helper = OpenCVHelper::new(127.5, 128.0);
let tensor = helper.prepare_input_tensor(&test_image, (200, 200))?;
assert_eq!(tensor.shape(), &[1, 3, 200, 200]);
Ok(())
}
}