use super::{EccMatchParameters, StackerError};
use opencv::core::{AlgorithmHint, Mat, MatTrait, MatTraitConst};
use opencv::features2d::ORB;
use opencv::prelude::Feature2DTrait;
use opencv::{imgcodecs, imgproc};
use std::path;
pub trait MatExt {
fn convert(&self, rtype: i32, alpha: f64, beta: f64) -> Result<Mat, StackerError>;
}
impl MatExt for Mat {
fn convert(&self, rtype: i32, alpha: f64, beta: f64) -> Result<Mat, StackerError> {
let mut dst = Mat::default();
self.convert_to(&mut dst, rtype, alpha, beta)?;
Ok(dst)
}
}
pub(super) struct UnsafeVectorKeyPointSyncWrapper(
pub(super) opencv::core::Vector<opencv::core::KeyPoint>,
);
unsafe impl Sync for UnsafeVectorKeyPointSyncWrapper {}
pub(super) struct UnsafeMatSyncWrapper(pub(super) Mat);
unsafe impl Sync for UnsafeMatSyncWrapper {}
pub trait SetMValue {
fn set_2d<T: opencv::prelude::DataType>(
&mut self,
row: i32,
col: i32,
value: T,
) -> Result<(), StackerError>;
}
impl SetMValue for Mat {
#[inline]
fn set_2d<T: opencv::prelude::DataType>(
&mut self,
row: i32,
col: i32,
value: T,
) -> Result<(), StackerError> {
let v = self.at_2d_mut::<T>(row, col)?;
*v = value;
Ok(())
}
}
#[inline(always)]
pub fn imread<P: AsRef<std::path::Path>>(path: P, imread_flags: i32) -> Result<Mat, StackerError> {
let path_str = path
.as_ref()
.to_str()
.ok_or_else(|| StackerError::InvalidPathEncoding(path.as_ref().to_path_buf()))?;
Ok(imgcodecs::imread(path_str, imread_flags)?)
}
pub(super) fn read_grey_and_f32(
filename: &path::Path,
imread_flags: i32,
) -> Result<(Mat, Mat), StackerError> {
let img = imgcodecs::imread(filename.to_str().unwrap(), imread_flags)?;
let img_f32 = img.convert(opencv::core::CV_32F, 1.0 / 255.0, 0.0)?;
let mut img_grey = Mat::default();
imgproc::cvt_color(
&img,
&mut img_grey,
imgproc::COLOR_BGR2GRAY,
0,
AlgorithmHint::ALGO_HINT_DEFAULT,
)?;
Ok((img_grey, img_f32))
}
impl From<EccMatchParameters> for Result<opencv::core::TermCriteria, StackerError> {
fn from(r: EccMatchParameters) -> Result<opencv::core::TermCriteria, StackerError> {
let mut rv = opencv::core::TermCriteria::default()?;
if let Some(max_count) = r.max_count {
rv.typ |= opencv::core::TermCriteria_Type::COUNT as i32;
rv.max_count = max_count;
}
if let Some(epsilon) = r.epsilon {
rv.typ |= opencv::core::TermCriteria_Type::EPS as i32;
rv.epsilon = epsilon;
}
Ok(rv)
}
}
pub(super) fn orb_detect_and_compute(
img: &Mat,
) -> Result<(opencv::core::Vector<opencv::core::KeyPoint>, Mat), StackerError> {
let mut orb = ORB::create_def()?;
let mut kp = opencv::core::Vector::<opencv::core::KeyPoint>::new();
let mut des = Mat::default();
orb.detect_and_compute(img, &Mat::default(), &mut kp, &mut des, false)?;
Ok((kp, des))
}
pub(super) fn scale_image(img: &Mat, scale_down: f32) -> Result<Mat, StackerError> {
let size = img.size()?;
let width = size.width;
let height = size.height;
let scaling_factor = if width < height {
scale_down as f64 / width as f64
} else {
scale_down as f64 / height as f64
};
let new_width = (width as f64 * scaling_factor) as i32;
let new_height = (height as f64 * scaling_factor) as i32;
let mut resized = Mat::default();
imgproc::resize(
img,
&mut resized,
opencv::core::Size::new(new_width, new_height),
0.0,
0.0,
imgproc::INTER_AREA,
)?;
Ok(resized)
}
macro_rules! impl_adjust_homography_for_scale {
($name:ident, $type:ty) => {
pub(super) fn $name(
h_small: &Mat,
img_small: &Mat,
img_original: &Mat,
) -> Result<Mat, StackerError> {
let small_size = img_small.size()?;
let orig_size = img_original.size()?;
let scale_x = orig_size.width as f64 / small_size.width as f64;
let scale_y = orig_size.height as f64 / small_size.height as f64;
let mut h_adjusted = h_small.clone();
*h_adjusted.at_2d_mut::<$type>(0, 2)? *= scale_x as $type;
*h_adjusted.at_2d_mut::<$type>(1, 2)? *= scale_y as $type;
*h_adjusted.at_2d_mut::<$type>(2, 0)? /= scale_x as $type;
*h_adjusted.at_2d_mut::<$type>(2, 1)? /= scale_y as $type;
Ok(h_adjusted)
}
};
}
impl_adjust_homography_for_scale!(adjust_homography_for_scale_f64, f64);
impl_adjust_homography_for_scale!(adjust_homography_for_scale_f32, f32);
impl Default for crate::KeyPointMatchParameters {
fn default() -> Self {
Self {
method: opencv::calib3d::RANSAC,
ransac_reproj_threshold: 3.0,
match_keep_ratio: 0.75,
match_ratio: 0.8,
border_mode: opencv::core::BORDER_CONSTANT,
border_value: opencv::core::Scalar::default(),
}
}
}