mcv-rs 0.1.0

MCV computer-vision primitives
Documentation
use std::{fs, path::Path};

use opencv::{
    core::{Mat, MatTraitConst, Vector},
    imgcodecs,
};

use super::error::{McvError, Result};

#[cfg(feature = "template-matching")]
pub fn read_image_gray(path: impl AsRef<Path>) -> Result<Mat> {
    let bytes = fs::read(path.as_ref())?;
    let data = Vector::<u8>::from_slice(&bytes);
    let image = imgcodecs::imdecode(&data, imgcodecs::IMREAD_GRAYSCALE)?;
    if image.empty() {
        return Err(McvError::InvalidImage(format!(
            "failed to decode {}",
            path.as_ref().display()
        )));
    }
    Ok(image)
}

#[cfg(feature = "multi-color")]
pub fn read_image_bgr(path: impl AsRef<Path>) -> Result<Mat> {
    let bytes = fs::read(path.as_ref())?;
    let data = Vector::<u8>::from_slice(&bytes);
    let image = imgcodecs::imdecode(&data, imgcodecs::IMREAD_COLOR)?;
    if image.empty() {
        return Err(McvError::InvalidImage(format!(
            "failed to decode {}",
            path.as_ref().display()
        )));
    }
    validate_bgr_image(&image)?;
    Ok(image)
}

#[cfg(feature = "multi-color")]
pub(crate) fn validate_bgr_image(image: &Mat) -> Result<()> {
    if image.empty() {
        return Err(McvError::InvalidImage("image is empty".to_string()));
    }
    if image.channels() != 3 {
        return Err(McvError::InvalidImage(format!(
            "image must be 3-channel BGR, got {} channel(s)",
            image.channels()
        )));
    }
    if !image.is_continuous() {
        return Err(McvError::InvalidImage(
            "image must be stored in a continuous buffer".to_string(),
        ));
    }
    Ok(())
}