feat(ndcv-bridge): add ndcv-bridge for ndarray and opencv interaction

This commit is contained in:
uttarayan21
2025-08-22 15:10:41 +05:30
parent 65560825fa
commit aab3d84db0
30 changed files with 3666 additions and 120 deletions

View File

@@ -3,6 +3,8 @@ use std::path::PathBuf;
use crate::facedet::{FaceDetectionConfig, FaceDetector, retinaface};
use crate::faceembed::facenet;
use crate::gui::app::{ComparisonResult, DetectionResult, ExecutorType};
use bounding_box::Aabb2;
use error_stack::ResultExt;
use ndarray_image::ImageToNdarray;
const RETINAFACE_MODEL_MNN: &[u8] = include_bytes!("../../models/retinaface.mnn");
@@ -227,42 +229,6 @@ impl FaceDetectionBridge {
// Extract face crops and generate embeddings
let mut best_similarity = 0.0f32;
for bbox1 in &faces1.bbox {
let crop1 = Self::crop_face_from_image(&img1, bbox1)?;
let crop1_array = ndarray::Array::from_shape_vec(
(1, crop1.height() as usize, crop1.width() as usize, 3),
crop1
.pixels()
.flat_map(|p| [p.0[0], p.0[1], p.0[2]])
.collect(),
)?;
let embedding1 = embedder
.run_models(crop1_array.view())
.map_err(|e| format!("Embedding generation failed: {}", e))?;
for bbox2 in &faces2.bbox {
let crop2 = Self::crop_face_from_image(&img2, bbox2)?;
let crop2_array = ndarray::Array::from_shape_vec(
(1, crop2.height() as usize, crop2.width() as usize, 3),
crop2
.pixels()
.flat_map(|p| [p.0[0], p.0[1], p.0[2]])
.collect(),
)?;
let embedding2 = embedder
.run_models(crop2_array.view())
.map_err(|e| format!("Embedding generation failed: {}", e))?;
let similarity = Self::cosine_similarity(
embedding1.row(0).as_slice().unwrap(),
embedding2.row(0).as_slice().unwrap(),
);
best_similarity = best_similarity.max(similarity);
}
}
(faces1, faces2, best_similarity)
}
ExecutorType::OnnxCpu => {
@@ -287,40 +253,14 @@ impl FaceDetectionBridge {
// Extract face crops and generate embeddings
let mut best_similarity = 0.0f32;
for bbox1 in &faces1.bbox {
let crop1 = Self::crop_face_from_image(&img1, bbox1)?;
let crop1_array = ndarray::Array::from_shape_vec(
(1, crop1.height() as usize, crop1.width() as usize, 3),
crop1
.pixels()
.flat_map(|p| [p.0[0], p.0[1], p.0[2]])
.collect(),
)?;
let embedding1 = embedder
.run_models(crop1_array.view())
.map_err(|e| format!("Embedding generation failed: {}", e))?;
for bbox2 in &faces2.bbox {
let crop2 = Self::crop_face_from_image(&img2, bbox2)?;
let crop2_array = ndarray::Array::from_shape_vec(
(1, crop2.height() as usize, crop2.width() as usize, 3),
crop2
.pixels()
.flat_map(|p| [p.0[0], p.0[1], p.0[2]])
.collect(),
)?;
let embedding2 = embedder
.run_models(crop2_array.view())
.map_err(|e| format!("Embedding generation failed: {}", e))?;
let similarity = Self::cosine_similarity(
embedding1.row(0).as_slice().unwrap(),
embedding2.row(0).as_slice().unwrap(),
);
best_similarity = best_similarity.max(similarity);
}
if faces1.bbox.is_empty() || faces2.bbox.is_empty() {
return Ok((faces1.bbox.len(), faces2.bbox.len(), 0.0));
}
if faces1.bbox.len() != faces2.bbox.len() {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Face count mismatch between images",
)));
}
(faces1, faces2, best_similarity)
@@ -329,39 +269,55 @@ impl FaceDetectionBridge {
Ok((faces1.bbox.len(), faces2.bbox.len(), best_similarity))
}
fn crop_face_from_image(
img: &image::RgbImage,
bbox: &bounding_box::Aabb2<usize>,
) -> Result<image::RgbImage, Box<dyn std::error::Error + Send + Sync>> {
let min_point = bbox.min_vertex();
let size = bbox.size();
let x = min_point.x as u32;
let y = min_point.y as u32;
let width = size.x as u32;
let height = size.y as u32;
// Ensure crop bounds are within image
let img_width = img.width();
let img_height = img.height();
let crop_x = x.min(img_width.saturating_sub(1));
let crop_y = y.min(img_height.saturating_sub(1));
let crop_width = width.min(img_width - crop_x);
let crop_height = height.min(img_height - crop_y);
Ok(image::imageops::crop_imm(img, crop_x, crop_y, crop_width, crop_height).to_image())
}
fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm_a == 0.0 || norm_b == 0.0 {
0.0
} else {
dot_product / (norm_a * norm_b)
}
}
}
// for bbox1 in &faces1.bbox {
// let crop1 = Self::crop_face_from_image(&img1, bbox1)?;
// let crop1_array = ndarray::Array::from_shape_vec(
// (1, crop1.height() as usize, crop1.width() as usize, 3),
// crop1
// .pixels()
// .flat_map(|p| [p.0[0], p.0[1], p.0[2]])
// .collect(),
// )?;
// let embedding1 = embedder
// .run_models(crop1_array.view())
// .map_err(|e| format!("Embedding generation failed: {}", e))?;
// for bbox2 in &faces2.bbox {
// let crop2 = Self::crop_face_from_image(&img2, bbox2)?;
// let crop2_array = ndarray::Array::from_shape_vec(
// (1, crop2.height() as usize, crop2.width() as usize, 3),
// crop2
// .pixels()
// .flat_map(|p| [p.0[0], p.0[1], p.0[2]])
// .collect(),
// )?;
// let embedding2 = embedder
// .run_models(crop2_array.view())
// .map_err(|e| format!("Embedding generation failed: {}", e))?;
// let similarity = Self::cosine_similarity(
// embedding1.row(0).as_slice().unwrap(),
// embedding2.row(0).as_slice().unwrap(),
// );
// best_similarity = best_similarity.max(similarity);
// }
// }
use crate::errors::Error;
pub fn compare_faces(
image1: &[Aabb2<usize>],
image2: &[Aabb2<usize>],
) -> Result<f32, error_stack::Report<crate::errors::Error>> {
use error_stack::Report;
if image1.is_empty() || image2.is_empty() {
Err(Report::new(crate::errors::Error))
.change_context(Report::new(crate::errors::Error))
.attach_printable("One or both images have no detected faces")
}
Ok(0.0)
}