use crate::{Result, VisionError};
use image::DynamicImage;
use torsh_tensor::{creation, creation::zeros_mut, Tensor};
pub fn resize_image(
image: &DynamicImage,
width: u32,
height: u32,
filter: image::imageops::FilterType,
) -> DynamicImage {
image.resize(width, height, filter)
}
pub fn normalize_tensor(tensor: &Tensor<f32>) -> Result<Tensor<f32>> {
let mut normalized = tensor.clone();
normalized.div_scalar_(255.0)?;
Ok(normalized)
}
pub fn denormalize_tensor(tensor: &Tensor<f32>) -> Result<Tensor<f32>> {
let mut denormalized = tensor.clone();
denormalized.mul_scalar_(255.0)?;
Ok(denormalized)
}
pub fn validate_image_tensor_shape(tensor: &Tensor<f32>, expected_dims: usize) -> Result<()> {
let shape = tensor.shape();
if shape.dims().len() != expected_dims {
return Err(VisionError::InvalidShape(format!(
"Expected {}D tensor, got {}D",
expected_dims,
shape.dims().len()
)));
}
Ok(())
}
pub fn clamp_tensor(tensor: &Tensor<f32>, min_val: f32, max_val: f32) -> Result<Tensor<f32>> {
let clamped = tensor.clamp(min_val, max_val)?;
Ok(clamped)
}
pub fn rgb_to_grayscale(rgb_tensor: &Tensor<f32>) -> Result<Tensor<f32>> {
validate_image_tensor_shape(rgb_tensor, 3)?;
let shape = rgb_tensor.shape();
if shape.dims()[0] != 3 {
return Err(VisionError::InvalidShape(
"Expected RGB tensor with 3 channels".to_string(),
));
}
let height = shape.dims()[1];
let width = shape.dims()[2];
let grayscale = zeros_mut(&[1, height, width]);
let r_weight = 0.2126;
let g_weight = 0.7152;
let b_weight = 0.0722;
for h in 0..height {
for w in 0..width {
let r = rgb_tensor.get(&[0, h, w])?;
let g = rgb_tensor.get(&[1, h, w])?;
let b = rgb_tensor.get(&[2, h, w])?;
let gray_val = r * r_weight + g * g_weight + b * b_weight;
grayscale.set(&[0, h, w], gray_val)?;
}
}
Ok(grayscale)
}