#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct FacialSymmetryMap {
pub pairs: Vec<(usize, usize)>,
}
#[allow(dead_code)]
pub fn new_symmetry_map() -> FacialSymmetryMap {
FacialSymmetryMap { pairs: Vec::new() }
}
#[allow(dead_code)]
pub fn add_pair(map: &mut FacialSymmetryMap, left: usize, right: usize) {
map.pairs.push((left, right));
}
#[allow(dead_code)]
pub fn enforce_symmetry(weights: &mut [f32], map: &FacialSymmetryMap, strength: f32) {
let s = strength.clamp(0.0, 1.0);
for &(l, r) in &map.pairs {
if l < weights.len() && r < weights.len() {
let avg = (weights[l] + weights[r]) * 0.5;
weights[l] = weights[l] + (avg - weights[l]) * s;
weights[r] = weights[r] + (avg - weights[r]) * s;
}
}
}
#[allow(dead_code)]
pub fn symmetry_error(weights: &[f32], map: &FacialSymmetryMap) -> f32 {
map.pairs
.iter()
.filter_map(|&(l, r)| {
if l < weights.len() && r < weights.len() {
Some((weights[l] - weights[r]).abs())
} else {
None
}
})
.sum()
}
#[allow(dead_code)]
pub fn pair_count(map: &FacialSymmetryMap) -> usize {
map.pairs.len()
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct FacialSymmetry {
pub asymmetry_weight: f32,
pub midline_offset: f32,
pub symmetry_blend: f32,
}
#[allow(dead_code)]
pub fn compute_symmetry_error(left: &[f32], right: &[f32]) -> f32 {
let n = left.len().min(right.len());
if n == 0 {
return 0.0;
}
let sum: f32 = (0..n).map(|i| (left[i] - right[i]).abs()).sum();
sum / n as f32
}
#[allow(dead_code)]
pub fn symmetrize_params(left: &mut [f32], right: &[f32]) {
let n = left.len().min(right.len());
left[..n].copy_from_slice(&right[..n]);
}
#[allow(dead_code)]
pub fn asymmetry_weight(fs: &FacialSymmetry) -> f32 {
fs.asymmetry_weight
}
#[allow(dead_code)]
pub fn facial_midline_offset(fs: &FacialSymmetry) -> f32 {
fs.midline_offset
}
#[allow(dead_code)]
pub fn apply_symmetry_correction(left: &mut [f32], right: &[f32], strength: f32) {
let n = left.len().min(right.len());
let s = strength.clamp(0.0, 1.0);
for i in 0..n {
left[i] = left[i] + (right[i] - left[i]) * s;
}
}
#[allow(dead_code)]
pub fn symmetry_score(left: &[f32], right: &[f32]) -> f32 {
let err = compute_symmetry_error(left, right);
1.0 / (1.0 + err)
}
#[allow(dead_code)]
pub fn left_right_delta(left: &[f32], right: &[f32]) -> Vec<f32> {
let n = left.len().min(right.len());
(0..n).map(|i| left[i] - right[i]).collect()
}
#[allow(dead_code)]
pub fn symmetry_blend(left: &mut [f32], right: &mut [f32], t: f32) {
let n = left.len().min(right.len());
let t = t.clamp(0.0, 1.0);
for i in 0..n {
let avg = (left[i] + right[i]) * 0.5;
left[i] = left[i] + (avg - left[i]) * t;
right[i] = right[i] + (avg - right[i]) * t;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_symmetry_map_empty() {
let m = new_symmetry_map();
assert_eq!(pair_count(&m), 0);
}
#[test]
fn add_pair_increments_count() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
assert_eq!(pair_count(&m), 1);
}
#[test]
fn enforce_symmetry_full_strength() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
let mut weights = vec![0.0, 1.0, 0.5];
enforce_symmetry(&mut weights, &m, 1.0);
assert!((weights[0] - 0.5).abs() < 1e-5);
assert!((weights[1] - 0.5).abs() < 1e-5);
}
#[test]
fn enforce_symmetry_zero_strength_no_change() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
let mut weights = vec![0.0, 1.0];
enforce_symmetry(&mut weights, &m, 0.0);
assert!((weights[0] - 0.0).abs() < 1e-5);
assert!((weights[1] - 1.0).abs() < 1e-5);
}
#[test]
fn symmetry_error_symmetric_weights() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
let weights = vec![0.5, 0.5];
assert!((symmetry_error(&weights, &m)).abs() < 1e-6);
}
#[test]
fn symmetry_error_asymmetric() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
let weights = vec![0.0, 1.0];
assert!((symmetry_error(&weights, &m) - 1.0).abs() < 1e-5);
}
#[test]
fn out_of_range_pairs_skipped() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 100); let mut weights = vec![0.5];
enforce_symmetry(&mut weights, &m, 1.0); assert!((weights[0] - 0.5).abs() < 1e-5);
}
#[test]
fn multiple_pairs() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 3);
add_pair(&mut m, 1, 4);
add_pair(&mut m, 2, 5);
assert_eq!(pair_count(&m), 3);
}
#[test]
fn enforce_symmetry_partial_strength() {
let mut m = new_symmetry_map();
add_pair(&mut m, 0, 1);
let mut weights = vec![0.0, 1.0];
enforce_symmetry(&mut weights, &m, 0.5);
assert!((weights[0] - 0.25).abs() < 1e-5);
assert!((weights[1] - 0.75).abs() < 1e-5);
}
#[test]
fn symmetry_error_empty_map() {
let m = new_symmetry_map();
let weights = vec![0.1, 0.9];
assert!((symmetry_error(&weights, &m)).abs() < 1e-9);
}
#[test]
fn test_compute_symmetry_error_symmetric() {
let left = vec![0.5, 0.5];
let right = vec![0.5, 0.5];
assert!((compute_symmetry_error(&left, &right)).abs() < 1e-6);
}
#[test]
fn test_compute_symmetry_error_asymmetric() {
let left = vec![0.0, 0.0];
let right = vec![1.0, 1.0];
let e = compute_symmetry_error(&left, &right);
assert!((e - 1.0).abs() < 1e-5);
}
#[test]
fn test_symmetrize_params() {
let mut left = vec![0.0, 0.0];
let right = vec![1.0, 0.5];
symmetrize_params(&mut left, &right);
assert!((left[0] - 1.0).abs() < 1e-6);
}
#[test]
fn test_asymmetry_weight_default() {
let fs = FacialSymmetry::default();
assert_eq!(asymmetry_weight(&fs), 0.0);
}
#[test]
fn test_symmetry_score_perfect() {
let v = vec![0.5, 0.5];
let score = symmetry_score(&v, &v);
assert!((score - 1.0).abs() < 1e-5);
}
#[test]
fn test_left_right_delta() {
let left = vec![1.0, 0.5];
let right = vec![0.5, 0.5];
let d = left_right_delta(&left, &right);
assert!((d[0] - 0.5).abs() < 1e-6);
assert!(d[1].abs() < 1e-6);
}
#[test]
fn test_symmetry_blend_midpoint() {
let mut l = vec![0.0, 0.0];
let mut r = vec![1.0, 1.0];
symmetry_blend(&mut l, &mut r, 1.0);
assert!((l[0] - 0.5).abs() < 1e-5);
assert!((r[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_apply_symmetry_correction_full() {
let mut left = vec![0.0, 0.0];
let right = vec![1.0, 0.5];
apply_symmetry_correction(&mut left, &right, 1.0);
assert!((left[0] - 1.0).abs() < 1e-6);
}
}