use std::cmp::Ordering; use std::collections::HashSet; use std::f64::consts::PI; use super::Param; use super::DEBUG; use crate::utils::Pt; //use opencv::prelude::MatTraitConst; use opencv::prelude::*; //MatTraitConst; use opencv::core::{add, subtract, Mat, Point as OcvPoint, Point3_, VecN, CV_8UC3}; use opencv::highgui::{self, create_trackbar, named_window, WINDOW_AUTOSIZE}; use opencv::imgproc::{cvt_color, line, COLOR_BGR2GRAY}; use opencv::Result; #[derive(Clone, Copy)] enum Cnt { Beg(usize), End(usize), } opencv::opencv_branch_4! { use opencv::imgproc::LINE_AA; } opencv::not_opencv_branch_4! { use opencv::core::LINE_AA; } use super::param::Treshold; const MAX_TRACKBAR: i32 = 255; #[allow(dead_code)] pub fn draw_histograme_dbg( window_name: &str, histo: &Vec, (from, to): (usize, usize), ) -> Result<()> { let v: VecN = VecN::new(0., 0., 0., 255.); let c1: VecN = VecN::new(128., 128., 128., 255.); let c2: VecN = VecN::new(255., 255., 255., 255.); //let color: VecN = VecN::new(255., 255., 255., 255.); let mut img = Mat::new_rows_cols_with_default(256 * 2, 256 * 2, CV_8UC3, v)?; let mut max = 0.; for i in 0..256 { if histo[i] > max { max = histo[i]; } } let v_log = 10.; for i in 0..255 { let x1 = ((i + 0) * 2) as i32; let x2 = ((i + 1) * 2) as i32; let y1 = ((histo[i + 0] as f64 + 1.).log(v_log) / (max as f64).log(v_log) * 2. * 256.) as i32; let y2 = ((histo[i + 1] as f64 + 1.).log(v_log) / (max as f64).log(v_log) * 2. * 256.) as i32; let color = if i >= from && i <= to { c2 } else { c1 }; let pt1 = OcvPoint::new(x1, y1); let pt2 = OcvPoint::new(x2, y2); line(&mut img, pt1, pt2, color, 1, LINE_AA, 0)?; } highgui::imshow(window_name, &img)?; Ok(()) } #[allow(dead_code)] pub fn draw_histograme_log(window_name: &str, histo: &Vec, is_log: bool) -> Result<()> { let v: VecN = VecN::new(0., 0., 0., 255.); let color: VecN = VecN::new(210., 210., 255., 255.); let mut img = Mat::new_rows_cols_with_default( histo.len() as i32 * 2, histo.len() as i32 * 2, CV_8UC3, v, )?; let mut max = f64::MIN; for i in 0..histo.len() { if histo[i] > max { max = histo[i]; } } for i in 0..(histo.len() - 1) { let x1 = ((i + 0) * 2) as i32; let x2 = ((i + 1) * 2) as i32; let (y1, y2); if is_log { y1 = ((1.-((histo[i + 0] as f64 + 1.).log10() / (max as f64).log10())) * 512.) as i32; y2 = ((1.-((histo[i + 1] as f64 + 1.).log10() / (max as f64).log10())) * 512.) as i32; } else { y1 = ((1. - ((histo[i + 0] as f64) / (max as f64))) * 512.) as i32; y2 = ((1. - ((histo[i + 1] as f64) / (max as f64))) * 512.) as i32; } let pt1 = OcvPoint::new(x1, y1); let pt2 = OcvPoint::new(x2, y2); line(&mut img, pt1, pt2, color, 1, LINE_AA, 0)?; } highgui::imshow(window_name, &img)?; Ok(()) } #[allow(dead_code)] pub fn draw_histograme(window_name: &str, histo: &Vec) -> Result<()> { let v: VecN = VecN::new(0., 0., 0., 255.); let color: VecN = VecN::new(255., 255., 255., 255.); let mut img = Mat::new_rows_cols_with_default( histo.len() as i32 * 2, histo.len() as i32 * 2, CV_8UC3, v, )?; let mut max = 0.; for i in 0..(histo.len() - 1) { if histo[i] > max { max = histo[i]; } } let v_log = 10.; for i in 0..(histo.len() - 1) { let x1 = ((i + 0) * 2) as i32; let x2 = ((i + 1) * 2) as i32; let y1 = ((histo[i + 0] as f64 + 1.).log(v_log) / (max as f64).log(v_log) * 2. * histo.len() as f64) as i32; let y2 = ((histo[i + 1] as f64 + 1.).log(v_log) / (max as f64).log(v_log) * 2. * histo.len() as f64) as i32; let pt1 = OcvPoint::new(x1, y1); let pt2 = OcvPoint::new(x2, y2); line(&mut img, pt1, pt2, color, 1, LINE_AA, 0)?; } highgui::imshow(window_name, &img)?; Ok(()) } #[allow(dead_code)] pub fn draw_histograme_bgr(window_name: &str, histo: &Vec>) -> Result<()> { let v: VecN = VecN::new(0., 0., 0., 255.); let b: VecN = VecN::new(255., 0., 0., 255.); let g: VecN = VecN::new(0., 255., 0., 255.); let r: VecN = VecN::new(0., 0., 255., 255.); let color = vec![b, g, r]; let mut img = Mat::new_rows_cols_with_default(256 * 2, 256 * 2, CV_8UC3, v)?; let mut range = vec![vec![f64::MAX, f64::MIN]; 3]; for j in 0..3 { for i in 0..256 { if histo[j][i] > range[j][1] { range[j][1] = histo[j][i]; } if histo[j][i] < range[j][0] { range[j][0] = histo[j][i]; } } } //let v_log = 10.; for j in 0..3 { for i in 0..255 { let x1 = ((i + 0) * 2) as i32; let x2 = ((i + 1) * 2) as i32; let y1 = ((histo[j][i + 0] + 1.).log10() / range[j][1].log10() * 2. * 256.) as i32; let y2 = ((histo[j][i + 1] + 1.).log10() / range[j][1].log10() * 2. * 256.) as i32; let pt1 = OcvPoint::new(x1, y1); let pt2 = OcvPoint::new(x2, y2); line(&mut img, pt1, pt2, color[j], 1, LINE_AA, 0)?; } } highgui::imshow(window_name, &img)?; Ok(()) } pub fn draw_histograme_bgr_tresh( window_name: &str, histo: &Vec>, tresh: &Treshold, ) -> Result<()> { let v: VecN = VecN::new(0., 0., 0., 255.); let b: VecN = VecN::new(255., 0., 0., 255.); let g: VecN = VecN::new(0., 255., 0., 255.); let r: VecN = VecN::new(0., 0., 255., 255.); let color1 = vec![b, g, r]; let color2 = vec![b / 2., g / 2., r / 2.]; let mut img = Mat::new_rows_cols_with_default(256 * 2, 256 * 2, CV_8UC3, v)?; let mut vmax = vec![f64::MIN; 3]; for j in 0..histo.len() { for i in 0..histo[j].len() { if histo[j][i] > vmax[j] { vmax[j] = histo[j][i]; } } } //let v_log = 10.; let max: Vec = [tresh.max_0 as f64, tresh.max_1 as f64, tresh.max_2 as f64].into(); let min: Vec = [tresh.min_0 as f64, tresh.min_1 as f64, tresh.min_2 as f64].into(); //println!("min: {min:?}\tmax: {max:?}"); for j in 0..3 { for i in 0..255 { let x1 = ((i + 0) * 2) as i32; let x2 = ((i + 1) * 2) as i32; let y1 = ((histo[j][i + 0] + 1.).log10() / vmax[j].log10() * 2. * 256.) as i32; let y2 = ((histo[j][i + 1] + 1.).log10() / vmax[j].log10() * 2. * 256.) as i32; let pt1 = OcvPoint::new(x1, y1); let pt2 = OcvPoint::new(x2, y2); //let val = (histo[j][i] + 1.).log10() / max[j].log10(); let (color, thickness) = if i as f64 >= min[j] && i as f64 <= max[j] { (color1[j], 2) } else { (color2[j], 1) }; line(&mut img, pt1, pt2, color, thickness, LINE_AA, 0)?; } } highgui::imshow(window_name, &img)?; Ok(()) } // limit = 0.35 c'est bien #[allow(dead_code)] pub fn is_same_frame(frame: &Mat, frame_prev: &Mat) -> Result { let nb_liss: i32 = 50; // plus on lisse la courbe plus on attein la limite facilement let limit = 0.45; // plus c'est haut, plus on tolere de changement entre 2 image let d_bgr = image_diff(frame, frame_prev)?; let histo = histogram_1d(&d_bgr, nb_liss)?; let ((_id1, v1), (_id2, v2)) = first_invert(&histo); if DEBUG { // on affiche l'image de la cam highgui::imshow("cam image", frame)?; // on affiche l'image de la cam highgui::imshow("prev image", frame_prev)?; // on affiche la difference highgui::imshow("diff image", &d_bgr)?; // on affiche l'histograme let ids = ((128 - _id2), (128 + _id1)); draw_histograme_dbg("histograme", &histo, ids)?; // -- pour chaque image enregistrer on l'affiche ma ca se fait autre part } if DEBUG { println!("v1[{_id1}]:{v1}\tv2[{_id1}:{v2}"); } if v1 >= limit || v2 >= limit { println!("\t XXX DIFFERENT XXX"); Ok(false) } else { println!("\t :) Same (: "); Ok(true) } } // On cherche des segment regourper par ilot de point. chaque illot a une plage de valeur en y qui // lui est propre, aucun autre ilot aura des point dans une plage de valeurs d'un autre illot. pub fn get_horizontal_segment(m: &Mat) -> Result> { // on va faire un histogram des point selon leur position en y // ca permetera des les differencier // on fait cette histo gramme pour connaitre ces plage de valeur en y let mut seg_pt = HashSet::from([]); let (cols, rows) = (m.cols(), m.rows()); let mut histo_x = vec![0.; cols.max(rows) as usize]; for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; if v.x != 0 && v.y != 0 && v.z != 0 { seg_pt.insert((i, j)); histo_x[i as usize] += 1.; } } } // on determine le debut et la fin de ces plage de valeur en x let mut histo_limit = vec![]; for i in (0..(histo_x.len() - 1)).rev() { if histo_x[i] != 0. && histo_x[i + 1] == 0. { histo_limit.push(Cnt::End(i)); } if histo_x[i] == 0. && histo_x[i + 1] != 0. { histo_limit.push(Cnt::Beg(i + 1)); } } let mut limits = vec![]; for k in 0..(histo_limit.len() / 2) { if let (Cnt::Beg(a), Cnt::End(b)) = (histo_limit[2 * k + 1], histo_limit[2 * k]) { limits.push((a, b)); } } // on regroupe les point par illot. let mut segment_iland = vec![vec![]; limits.len()]; for (x, y) in seg_pt { let id = get_id_groups(&limits, x as usize).unwrap(); segment_iland[id].push((x, y)); } // on transforme chaque point en pt: (f32, f32) -> Pt // toujours avec la meme structure d'ilot. let segment_iland_pt: Vec> = segment_iland .iter() .map(|iland| { iland .iter() .map(|(x, y)| Pt { x: *x as f64, y: *y as f64, }) .collect() }) .collect(); let mut segments = vec![]; for (_i, iland) in segment_iland_pt.iter().enumerate() { let mut center = Pt { x: 0., y: 0. }; for p in iland { center += *p; } center /= iland.len() as f64; let max_deg = 360; let (mut rad_min, mut y_min) = (0., f64::MAX); let mut iland_min = vec![]; for deg in 0..max_deg { let rad = (deg as f64) / (max_deg as f64) * PI * 2.; let y_axis = Pt { x: rad.sin(), y: rad.cos(), }; let x_axis = Pt { x: -y_axis.y, y: y_axis.x, }; let mut tmp_iland = vec![]; let mut y_abs_max = f64::MIN; for pt in iland { let mut p = *pt - center; p = Pt { x: p.cross(&x_axis), y: p.cross(&y_axis), }; tmp_iland.push(p); if y_abs_max < p.y.abs() { y_abs_max = p.y.abs(); } } if y_abs_max < y_min { y_min = y_abs_max; rad_min = rad; iland_min = tmp_iland; } } iland_min.sort_by(|pta, ptb| { if pta.y < ptb.y { std::cmp::Ordering::Greater } else if pta.y == ptb.y { if pta.x.abs() < ptb.x.abs() { std::cmp::Ordering::Greater } else if pta.x.abs() == ptb.x.abs() { std::cmp::Ordering::Equal } else { std::cmp::Ordering::Less } } else { std::cmp::Ordering::Less } }); let id1 = iland_min.len() / 2; let id2 = iland_min.len() - id1; let mean_r = Pt::mean(&iland_min[..id1]); let mean_l = Pt::mean(&iland_min[id2..]); //let mean_r = iland_min[0]; //let mean_l = iland_min.last().unwrap(); let y_axis = Pt { x: rad_min.sin(), y: rad_min.cos(), }; let x_axis = Pt { x: -y_axis.y, y: y_axis.x, }; let pt_r = center + (y_axis * mean_r.y) + (x_axis * mean_r.x); let pt_l = center + (y_axis * mean_l.y) + (x_axis * mean_l.x); //segments.push(((pt_l.x as f32, pt_l.y as f32), (pt_r.x as f32, pt_r.y as f32))); let pt_r_2 = pt_l + (pt_r - pt_l) * 1.5; let pt_l_2 = pt_r + (pt_l - pt_r) * 1.5; segments.push(( ( ((pt_l_2.x as f32, pt_l_2.y as f32)), ((pt_r_2.x as f32, pt_r_2.y as f32)), ), y_min as f32, )); } Ok(segments) } #[derive(Debug)] struct Line { h_lst: HashSet<(i32, i32)>, v_lst: Vec, line: Vec, nb_liss: i32, } impl Line { pub fn new( pt_all: &mut HashSet<(i32, i32)>, y_ordering: bool, beg_lowest: bool, nb_liss: i32, ) -> Self { let mut illand = Line { h_lst: HashSet::new(), v_lst: vec![], line: vec![], nb_liss, }; if pt_all.len() <= 0 { return illand; } //dbg!(&pt_all); illand.populate_hlst(pt_all); illand.define_line(); //println!(""); //illand.define_beg_end(false, beg_lowest); //dbg!(&illand); illand } //fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) { // // on a tout les point ordonner // // on peut les re-ordonner selon un axes interne //} fn define_line(&mut self) { // la ca va etre marant. // let mut last_id = 0; let to_look = [ (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), ]; if self.h_lst.len() == 0 { return; } // search botom-left point. let mut first = (i32::MAX / 2, 0); self.h_lst.iter().for_each(|(x, y)| { if *y > first.1 || (*y == first.1 && *x < first.0) { first = (*x, *y); } }); println!("\nfirst: {first:?}\n"); let mut ll = self.h_lst.clone(); let mut l = vec![first]; let (mut px, mut py) = first; ll.remove(&first); // adding all borders point to a line 'line: loop { for k in 0..to_look.len() { //let id = (k + last_id) % to_look.len(); let (i, j) = to_look[(k + last_id) % to_look.len()]; if ll.get(&(i + px, j + py)).is_some() { l.push((i + px, j + py)); ll.remove(&(i + px, j + py)); (px, py) = (i + px, j + py); last_id = (k + last_id + to_look.len() - 2) % to_look.len(); continue 'line; } } break; } let (mut min_y, mut max_y) = (f64::MAX, f64::MIN); let mut l_tmp: Vec = l .iter() .map(|(x, y)| { min_y = (*y as f64).min(min_y); max_y = (*y as f64).max(max_y); Pt { x: *x as f64, y: *y as f64, } }) .collect(); // lissage des point let raw_pt = l_tmp.clone(); //println!("[1]raw_pt:{:?}", &raw_pt); let nb_liss = self.nb_liss * 2; let (c1, c2) = (1., 48.); for i in 0..nb_liss { let mut nl = vec![]; for i in 0..l_tmp.len() { let (id_prev, id_next) = ((i + l_tmp.len() - 1) % l_tmp.len(), (i + 1) % l_tmp.len()); let (prev, pt, next) = (l_tmp[id_prev], l_tmp[i], l_tmp[id_next]); nl.push((prev * c1 + pt * c2 + next * c1) / (2. * c1 + c2)); } l_tmp = nl; } let mut histo_ang = vec![0.; 256]; let mut length_max = 0.; for i in 0..l_tmp.len() { let next = l_tmp[(i + 1) % l_tmp.len()]; let diff = next - l_tmp[i]; let ang = diff.y.atan2(diff.x); let id = ((((ang + PI) / (2. * PI)) * 255.) as usize) % 256; let length = (diff.x * diff.x + diff.y * diff.y).sqrt(); length_max = length.max(length_max); histo_ang[id] += 1.; } let _ = draw_histograme_log("histo ang", &histo_ang, false); let mut histo_ang_diff = vec![0.; 256]; let mirange = 3; let mut id_out = vec![0]; for i in 0..l_tmp.len() { let next = l_tmp[(i + 1) % l_tmp.len()]; let next2 = l_tmp[(i + 2) % l_tmp.len()]; let diff1 = next - l_tmp[i]; let diff2 = next2 - next; let ang1 = diff1.y.atan2(diff1.x); let ang2 = diff2.y.atan2(diff2.x); let id = (((((ang2 - ang1) + PI) / (2. * PI)) * 255.) as usize) % 256; if !(id >= 128 - mirange && id <= 128 + mirange) { id_out.push(i); //println!("id:{i}"); } histo_ang_diff[id] += 1.; } let _ = draw_histograme_log("histo ang diff", &histo_ang_diff, true); //let mut histo_lenght = vec![0.; 256]; //for i in 0..l_tmp.len() { // let next = l_tmp[(i + 1) % l_tmp.len()]; // let diff = next - l_tmp[i]; // //let ang = diff.y.atan2(diff.x); // //let ang_id = ((ang / (2. * PI) * 256.) as usize) % 256; // let length = (diff.x * diff.x + diff.y * diff.y).sqrt(); // let id = (length / length_max * 255.) as usize % 256; // histo_lenght[id] += 1.; //} // draw histo ang // draw histo dst //let _ = draw_histograme_log("histo length", &histo_lenght, false); // opposite side // Le but c'est de trouver le point le plus proche mais pas un point voisin // On va le dessiner pour une partie des point seulement let mut diff = vec![]; for i in 0..id_out.len()-1 { let d = id_out[i+1] - id_out[i]; diff.push((d, id_out[i], id_out[i+1])); } diff.sort_by(|a, b| { match (a.0, b.0) { (a, b) if a < b => Ordering::Greater, (a, b) if a == b => Ordering::Equal, _ => Ordering::Less, } }); if diff.len() < 2 { return ; } let mut dio = vec![diff[0], diff[1]]; dio.sort_by(|a, b| { match (a.1, b.1) { (a, b) if a > b => Ordering::Greater, (a, b) if a == b => Ordering::Equal, _ => Ordering::Less, } }); //dbg!("diff:", &diff[..3]); //dbg!(&id_out); dbg!(&dio); //* ////println!("[2]raw_pt:{:?}", &raw_pt); //let left: Vec = raw_pt[dio[0].1..=dio[0].2].into(); //let mut right = vec![]; //for i in (dio[1].1..=dio[1].2).rev() { ////for i in (id_out[1]..=id_out[2]).rev() { // right.push(raw_pt[i]); //} //println!("[2]raw_pt:{:?}", &raw_pt); let left: Vec = l_tmp[dio[0].1..=dio[0].2].into(); let mut right = vec![]; for i in (dio[1].1..=dio[1].2).rev() { //for i in (id_out[1]..=id_out[2]).rev() { right.push(l_tmp[i]); } let mut couple = vec!(); let nb_jump = 10; let mut id_last = 0; let range_id = 20; for i in 0..left.len() { //if i % nb_jump == 0 { let mut dist = vec![]; let from = (id_last as i32 - range_id as i32).max(0) as usize; let to = (id_last + range_id).min(right.len()-1); for id in from..=to { let df = right[id] - left[i]; let dst = (df.cross(&df)).sqrt(); dist.push((id, dst)); } dist.sort_by(|a, b| { if a.1 > b.1 { Ordering::Greater } else if a.1 == b.1 { Ordering::Equal } else { Ordering::Less } }); //println!("closest: {:?}", &dist[0]); id_last = dist[0].0; //dbg!(dist); couple.push((left[i], right[id_last])); //} } let l3: Vec = couple.iter().map(|(l, r)| (*l+*r)/2.).collect(); //*/ // //let space = 100; //let dst = 30; //for i in 0..l_tmp.len() { // if i % space == 0 { // // // } //} self.line = l3; println!("rest:{}\t", ll.len()); } /// the function also set: self.v_lst fn populate_hlst(&mut self, pt_all: &mut HashSet<(i32, i32)>) { let first = pt_all.iter().next().unwrap(); let mut added = HashSet::from([(first.0, first.1)]); let to_look = [ (-1, 0), (0, -1), (1, 0), (0, 1), (-1, -1), (-1, 1), (1, 1), (1, -1), ]; self.h_lst.extend(&added); while added.len() > 0 { let mut tmp = HashSet::new(); for (x, y) in added { for (i, j) in to_look { if let Some(pt_new) = pt_all.get(&(x + i, y + j)) { tmp.insert(*pt_new); } } } self.h_lst.extend(tmp.clone()); tmp.iter().for_each(|pt| { pt_all.remove(pt); }); added = tmp; } let mut tmp = HashSet::new(); self.h_lst.iter().for_each(|(x, y)| { for (i, j) in to_look[..4].iter() { if self.h_lst.get(&(x + i, y + j)).is_none() { tmp.insert((*x, *y)); } } }); self.h_lst = tmp; println!("\t\t\t\tfinal selecteted:{}", self.h_lst.len()); //dbg!(a); //println!(I); } } pub fn get_lines( m: &Mat, y_ordering: bool, beg_lowest: bool, nb_liss: i32, ) -> Result>> { // donc on va refaire un algo de detection de segement // * on va chercher des illot: // * chaque illo on lui definie des limit (min, max) sur x et y // * on calcule le centre let mut pt_inside = HashSet::new(); let (cols, rows) = (m.cols(), m.rows()); for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; if v.x != 0 && v.y != 0 && v.z != 0 { pt_inside.insert((i, j)); } } } println!("\tFlifou: {}\t\tpt_inserted:{}", line!(), pt_inside.len()); let mut lines = vec![]; while pt_inside.len() > 0 { lines.push(Line::new(&mut pt_inside, y_ordering, beg_lowest, nb_liss)); } println!("\tFlifou: {}\t\tnb_line:{}", line!(), lines.len()); // pour chaque ligne Ok(lines.iter().map(|line| line.line.clone()).collect()) } #[derive(Debug)] struct Seg { h_lst: HashSet<(i32, i32)>, v_lst: Vec, min: Pt, max: Pt, center: Pt, beg: Pt, end: Pt, } impl Seg { pub fn new(pt_all: &mut HashSet<(i32, i32)>, y_ordering: bool, beg_lowest: bool) -> Self { let mut illand = Seg { min: Pt::from((f64::MAX, f64::MAX)), max: Pt::from((f64::MIN, f64::MIN)), center: Pt::from((0., 0.)), beg: Pt::from((0., 0.)), end: Pt::from((0., 0.)), h_lst: HashSet::new(), v_lst: vec![], }; if pt_all.len() <= 0 { return illand; } //dbg!(&pt_all); illand.populate_hlst(pt_all); //println!(""); illand.define_beg_end(false, beg_lowest); //dbg!(&illand); illand } //fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) { // // on a tout les point ordonner // // on peut les re-ordonner selon un axes interne //} /// the function also set: self.v_lst fn populate_hlst(&mut self, pt_all: &mut HashSet<(i32, i32)>) { let first = pt_all.iter().next().unwrap(); let mut added = HashSet::from([(first.0, first.1)]); let to_look = [ (-1, 0), (0, -1), (1, 0), (0, 1), (-1, -1), (-1, 1), (1, 1), (1, -1), ]; self.h_lst.extend(&added); while added.len() > 0 { let mut tmp = HashSet::new(); for (x, y) in added { for (i, j) in to_look { if let Some(pt_new) = pt_all.get(&(x + i, y + j)) { tmp.insert(*pt_new); } } } self.h_lst.extend(tmp.clone()); tmp.iter().for_each(|pt| { pt_all.remove(pt); }); added = tmp; } // calcule min, max, center self.h_lst.iter().for_each(|(x, y)| { self.min.x = (*x as f64).min(self.min.x); self.max.x = (*x as f64).max(self.max.x); self.min.y = (*y as f64).min(self.min.y); self.max.y = (*y as f64).max(self.max.y); self.center.x += *x as f64; self.center.y += *y as f64; }); self.center /= self.h_lst.len() as f64; self.v_lst = self .h_lst .iter() .map(|(x, y)| Pt { x: (*x as f64), y: (*y as f64), }) .collect(); } /// y_ordering: means we ordering vertically. Else we ordering horizontay /// beg_lowest: means first value are the lowset. Else first value are the biggest fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) { let pts: Vec = self .h_lst .iter() .map(|(x, y)| { Pt { x: (*x as f64), y: (*y as f64), } - self.center }) .collect(); let y_cmp = |pt_a: &Pt, pt_b: &Pt| { if (pt_a.y > pt_b.y) == beg_lowest { Ordering::Greater } else if pt_a.y == pt_b.y { if pt_a.x.abs() > pt_b.x.abs() { Ordering::Greater } else if pt_a.x.abs() == pt_b.x.abs() { Ordering::Equal } else { Ordering::Less } } else { Ordering::Less } }; let x_cmp = |pt_a: &Pt, pt_b: &Pt| { if (pt_a.x > pt_b.x) == beg_lowest { Ordering::Greater } else if pt_a.x == pt_b.x { if pt_a.y.abs() > pt_b.y.abs() { Ordering::Greater } else if pt_a.y.abs() == pt_b.y.abs() { Ordering::Equal } else { Ordering::Less } } else { Ordering::Less } }; //println!("\n\nserching axes:"); // finding by dichotomie // finding best central axes. let mut begin = 0.; let mut len = 8.; let nb_deg = 8; let nb_iter = 20; let mut id_min = 0; let mut v_max_x = vec![]; for _iter in 0..nb_iter { //println!("iter:{_iter}"); v_max_x = vec![]; for i in 0..(nb_deg + 2) { let rad = (i as f64 - 1.) / len * PI + begin; let axes_x = Pt::from((rad.cos(), rad.sin())); let axes_y = Pt::from((-rad.sin(), rad.cos())); let new_base: Vec = pts .iter() .map(|pt| Pt::from((pt.cross(&axes_x), pt.cross(&axes_y)))) .collect(); let mut max_x = 0.; new_base.iter().for_each(|pt| { if pt.x.abs() > max_x { max_x = pt.x.abs(); } }); //println!("\t[i:{i}]rad:{rad:32.26}\tval: {max_x}"); v_max_x.push((rad, max_x)); } id_min = 0; for i in 1..=nb_deg { let (p, n, a) = (v_max_x[i - 1].1, v_max_x[i].1, v_max_x[i + 1].1); let cnd = (y_ordering && n <= p && n <= a) || (!y_ordering && n >= p && n >= a); id_min = if cnd { i - 1 } else { id_min }; } begin = v_max_x[id_min].0; len *= 4.; } let rad = v_max_x[id_min + 1].0; let axes_x = Pt::from((rad.cos(), rad.sin())); let axes_y = Pt::from((-rad.sin(), rad.cos())); let mut npts: Vec = pts .iter() .map(|pt| Pt::from((pt.cross(&axes_x), pt.cross(&axes_y)))) .collect(); if y_ordering { npts.sort_by(y_cmp); } else { npts.sort_by(x_cmp); } let mut beg = Pt::from((0., 0.)); for pt in npts[..(npts.len() / 2)].iter() { beg += *pt; } beg /= (npts.len() / 2) as f64; let mut end = Pt::from((0., 0.)); for pt in npts[(npts.len() / 2)..].iter() { end += *pt; } end /= (npts.len() - (npts.len() / 2)) as f64; let beg_tmp = axes_x * beg.x + axes_y * beg.y + self.center; let end_tmp = axes_x * end.x + axes_y * end.y + self.center; self.beg = (beg_tmp - end_tmp) * 1.5 + end_tmp; self.end = (end_tmp - beg_tmp) * 1.5 + beg_tmp; } } pub fn get_segment( m: &Mat, y_ordering: bool, beg_lowest: bool, ) -> Result> { // donc on va refaire un algo de detection de segement // * on va chercher des illot: // * chaque illo on lui definie des limit (min, max) sur x et y // * on calcule le centre let mut pt_inside = HashSet::new(); let (cols, rows) = (m.cols(), m.rows()); for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; if v.x != 0 && v.y != 0 && v.z != 0 { pt_inside.insert((i, j)); } } } let mut illands = vec![]; while pt_inside.len() > 0 { illands.push(Seg::new(&mut pt_inside, y_ordering, beg_lowest)); } //todo!(); if y_ordering { illands.sort_by(|a, b| { if beg_lowest && a.center.y > b.center.y || !beg_lowest && a.center.y < b.center.y { Ordering::Greater } else if a.center.y == b.center.y { Ordering::Equal } else { Ordering::Less } }); } else { illands.sort_by(|a, b| { if beg_lowest && a.center.x > b.center.x || !beg_lowest && a.center.x < b.center.x { Ordering::Greater } else if a.center.x == b.center.x { Ordering::Equal } else { Ordering::Less } }); } let segments = illands .iter() .map(|illand| ((illand.beg.x, illand.beg.y), (illand.end.x, illand.end.y))) .collect(); Ok(segments) } // On cherche des segment regourper par ilot de point. chaque illot a une plage de valeur en y qui // lui est propre, aucun autre ilot aura des point dans une plage de valeurs d'un autre illot. pub fn get_vertical_segment(m: &Mat) -> Result> { // on va faire un histogram des point selon leur position en y // ca permetera des les differencier // on fait cette histo gramme pour connaitre ces plage de valeur en y let mut seg_pt = HashSet::from([]); let (cols, rows) = (m.cols(), m.rows()); let mut histo_y = vec![0.; cols.max(rows) as usize]; for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; if v.x != 0 && v.y != 0 && v.z != 0 { seg_pt.insert((i, j)); histo_y[j as usize] += 1.; } } } // on determine le debut et la fin de ces palge de l=valeur en y let mut histo_limit = vec![]; for i in (0..(histo_y.len() - 1)).rev() { if histo_y[i] != 0. && histo_y[i + 1] == 0. { histo_limit.push(Cnt::End(i)); } if histo_y[i] == 0. && histo_y[i + 1] != 0. { histo_limit.push(Cnt::Beg(i + 1)); } } let mut limits = vec![]; for k in 0..(histo_limit.len() / 2) { if let (Cnt::Beg(a), Cnt::End(b)) = (histo_limit[2 * k + 1], histo_limit[2 * k]) { limits.push((a, b)); } } // on regroupe les point par illot. let mut segment_iland = vec![vec![]; limits.len()]; for (x, y) in seg_pt { let id = get_id_groups(&limits, y as usize).unwrap(); segment_iland[id].push((x, y)); } // on transforme chaque point en pt: (f32, f32) -> Pt // toujours avec la meme structure d'ilot. let segment_iland_pt: Vec> = segment_iland .iter() .map(|iland| { iland .iter() .map(|(x, y)| Pt { x: *x as f64, y: *y as f64, }) .collect() }) .collect(); // Pour chaque ilot de pixel: on prend le centre, on cherche l'axe qui passe le plus au centre // de l'illot. Pour trouver cet axe, pour chaque pixel de l'ilot, on va calculer l'eccart au // carree avec cet axe. On selectionne l'axe qui a l'erreur la plus faible // TODO: peut etre un meileur algo de recheche de l'axe (dicotomie en partie) // En suite on tris ces pixel et on prend la moiter la plus haute et la moiter la plus basse // part raport a l'axe. On fait la mayenne des ces 2 groupe et on a les extremiter haute et // basse pour cet ilot de pixel. En suite on multiplie par 2 ce segement pour qui soit de la // taille de l'ilots. // // TODO: La selection de l'axe qui passe au centre de l'ilot pourrauiut aussi etre meilleur // au lieux d'utiliser l'arreur, on pourrait regarder la valeur absolue de la coordoner x la plus petit // DONE=> j'ai tester une autre methode mais il y a plus d'erreur... mais // l'orientation des segment est pas mal. En gros l'orientation de l'axe n'est pas // toujours la meme. C'est du a la fonction de tris. La fonction ne s'execute pas dans // le meme ordre sur les valeur, Et quand 2 valeurs sont identique, elle peuvent etre // inter changer. // TODO: La selection des pixel pour chaque illot pourrait etre ameliorer // En fait elle me va bien. C'est vrai que il ne sont pas ouf mais bon... let mut segments = vec![]; for (_i, iland) in segment_iland_pt.iter().enumerate() { let mut center = Pt { x: 0., y: 0. }; for p in iland { center += *p; } center /= iland.len() as f64; let max_deg = 8; let (mut _err_min, mut rad_min, mut x_min) = (f64::MAX, 0., f64::MAX); let mut iland_min = vec![]; for deg in 0..max_deg { let rad = (deg as f64) / (max_deg as f64) * PI * 2.; let y_axis = Pt { x: rad.sin(), y: rad.cos(), }; let x_axis = Pt { x: -y_axis.y, y: y_axis.x, }; let mut tmp_iland = vec![]; let mut x_abs_max = f64::MIN; for pt in iland { let mut p = *pt - center; p = Pt { x: p.cross(&x_axis), y: p.cross(&y_axis), }; tmp_iland.push(p); if x_abs_max < p.x.abs() { x_abs_max = p.x.abs(); } } if x_abs_max < x_min { x_min = x_abs_max; rad_min = rad; iland_min = tmp_iland; } } iland_min.sort_by(|pta, ptb| { if pta.y < ptb.y { std::cmp::Ordering::Greater } else if pta.y == ptb.y { if pta.x.abs() < ptb.x.abs() { std::cmp::Ordering::Greater } else if pta.x.abs() == ptb.x.abs() { std::cmp::Ordering::Equal } else { std::cmp::Ordering::Less } } else { std::cmp::Ordering::Less } }); let id1 = iland_min.len() / 2; let id2 = iland_min.len() - id1; let mean_up = Pt::mean(&iland_min[..id1]); let mean_down = Pt::mean(&iland_min[id2..]); //let mean_up = iland_min[0]; //let mean_down = iland_min.last().unwrap(); let y_axis = Pt { x: rad_min.sin(), y: rad_min.cos(), }; let x_axis = Pt { x: -y_axis.y, y: y_axis.x, }; let pt_up = center + (y_axis * mean_up.y) + (x_axis * mean_up.x); let pt_down = center + (y_axis * mean_down.y) + (x_axis * mean_down.x); //segments.push(((pt_down.x as f32, pt_down.y as f32), (pt_up.x as f32, pt_up.y as f32))); let pt_up_2 = pt_down + (pt_up - pt_down) * 1.5; let pt_down_2 = pt_up + (pt_down - pt_up) * 1.5; segments.push(( ( (pt_down_2.x as f32, pt_down_2.y as f32), (pt_up_2.x as f32, pt_up_2.y as f32), ), x_min as f32, )); } Ok(segments) } #[allow(dead_code)] fn average_pt_i32(vals: &[(i32, i32)]) -> (f32, f32) { let (mut mean_x, mut mean_y) = (0., 0.); let len = vals.len() as f32; for (x, y) in vals { mean_x += *x as f32; mean_y += *y as f32; } (mean_x / len, mean_y / len) } fn get_id_groups(limits: &Vec<(usize, usize)>, id: usize) -> Option { for (id_seg, (min, max)) in limits.iter().enumerate() { if id >= *min && id <= *max { return Some(id_seg); } } None //return usize::MAX; // im lazy to have Option return... } #[allow(dead_code)] pub fn annalyse_segment(m: &Mat) -> Result>> { // on recupere les coordoner des point selectioner let mut seg_pt = HashSet::from([]); let (cols, rows) = (m.cols(), m.rows()); for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; if v.x != 0 && v.y != 0 && v.z != 0 { seg_pt.insert((i, j)); } } } // on garde que ceux qui sont frontiere //let around_all = [(-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1)]; let around_all = [(-1, 0), (0, 1), (1, 0), (0, -1)]; let mut selected: HashSet<(i32, i32)> = seg_pt .iter() .filter_map(|(x, y)| { for (_k, (i, j)) in around_all.iter().enumerate() { if seg_pt.get(&(*x + i, *y + j)).is_none() { return Some((*x, *y)); } } None }) .collect(); //let around = [(-1, 0), (0, -1), (1, 0), (0, 1), (-1, -1), (1, -1), (1, 1), (-1, 1)]; let around = [ (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), ]; let mut lines = vec![]; while selected.len() > 0 { let mut outed: HashSet<(i32, i32)> = HashSet::from([]); let (x, y) = selected.iter().next().unwrap(); let mut line = vec![(*x, *y)]; outed.insert((*x, *y)); let mut last = 0; 'line: loop { let (x, y) = line[line.len() - 1]; for k in 0..around.len() { let (i, j) = around[(k + last) % around.len()]; if seg_pt.get(&(x + i, y + j)).is_some() && outed.get(&(x + i, y + j)).is_none() { line.push((x + i, y + j)); outed.insert((x + i, y + j)); last = k + last + around.len() - 2; // ici on pourrait cleaner le rest //for l in (k+1)..around.len() { // let (i, j) = around[(l+last)%around.len()]; // outed.insert((x+i, y+j)); // // //} continue 'line; } } break; } lines.push(line); for (x, y) in outed { selected.remove(&(x, y)); } } println!("\nseg: {}", lines.len()); Ok(lines) } #[allow(dead_code)] pub fn image_mean(frames: &[Mat]) -> Result { /* * Il faudrait pouvoir changer les matrice de type pour avoir des valeur plus grande * */ let mut frames_big: Vec = vec![]; let len = frames.len() as i16; for frame in frames { let mut tmp = Mat::default(); frame.convert_to(&mut tmp, 19, 1., 0.)?; // 19 is for: CV_16SC3 frames_big.push(tmp); } let mut img_sum: Mat = frames_big[0].clone(); let mask = Mat::default(); for frame in frames_big[1..].iter() { let mut tmp = Mat::default(); add(&img_sum, &frame, &mut tmp, &mask, -1)?; img_sum = tmp; } let (cols, rows) = (img_sum.cols(), img_sum.rows()); for j in 0..rows { for i in 0..cols { let v: &mut Point3_ = img_sum.at_2d_mut(j, i)?; v.x /= len; v.y /= len; v.z /= len; } } let mut mean = Mat::default(); img_sum.convert_to(&mut mean, 16, 1., 0.)?; // 16 is for: CV_8UC3 Ok(mean) } pub fn image_diff(frame: &Mat, frame_prev: &Mat) -> Result { let mut diff_bgr = Mat::default(); let mut diff_bgr_2 = Mat::default(); let mut d_bgr = Mat::default(); let (row, col) = (frame.rows(), frame.cols()); let mask = Mat::default(); let v: VecN = VecN::new(128., 128., 128., 128.); let mid: Mat = Mat::new_rows_cols_with_default(row, col, CV_8UC3, v)?; // ca parait etonant d'enlever la difference dans l'autre sens mais paradoxalement, ca permet // d'avoir toutes les valeur, pck a chaque fois les valeur negative sont mise a 0 dans // l'operation de soustraction subtract(frame, frame_prev, &mut diff_bgr, &mask, -1)?; add(&diff_bgr, &mid, &mut diff_bgr_2, &mask, -1)?; subtract(frame_prev, frame, &mut diff_bgr, &mask, -1)?; subtract(&diff_bgr_2, &diff_bgr, &mut d_bgr, &mask, -1)?; Ok(d_bgr) } pub fn histogram_3d(m: &Mat, nb_liss: i32) -> Result>> { let (cols, rows) = (m.cols(), m.rows()); let mut histo = vec![vec![0.; 256]; 3]; // on calcule l'histograme for j in 0..rows { for i in 0..cols { let v: &Point3_ = m.at_2d(j, i)?; let (b, g, r) = (v.x as usize, v.y as usize, v.z as usize); histo[2][r] += 1.; histo[1][g] += 1.; histo[0][b] += 1.; } } // on lisse l'histograme for j in 0..3 { let mut tmp = histo[j].clone(); for _ in 0..nb_liss { for i in 1..(tmp.len() - 1) { histo[j][i] = (tmp[i - 1] + 1. * tmp[i] + tmp[i + 1]) / 3.; } tmp = histo[j].clone(); } } Ok(histo) } pub fn histogram_1d(m: &Mat, nb_liss: i32) -> Result> { let (cols, rows) = (m.cols(), m.rows()); let mut histo = vec![0; 256]; let mut m_gray = Mat::default(); // on convertie en gris cvt_color(m, &mut m_gray, COLOR_BGR2GRAY, 0)?; // on calcule l'histograme for j in 0..rows { for i in 0..cols { let v: &u8 = m_gray.at_2d(j, i)?; let id = *v as usize; histo[id] += 1; } } // on lisse l'histograme let mut histo: Vec = histo.iter().map(|x| *x as f64).collect(); let mut tmp = histo.clone(); for _ in 0..nb_liss { for i in 1..(histo.len() - 1) { histo[i] = (tmp[i - 1] + 2. * tmp[i] + tmp[i + 1]) / 4.; } tmp = histo.clone(); } Ok(histo) } pub fn first_invert(histo: &Vec) -> ((usize, f64), (usize, f64)) { // on applique un log puis on normalise mar le log du max let mut normalised = vec![0.; histo.len()]; let mut p1 = vec![0.; histo.len() / 2]; let mut p2 = vec![0.; histo.len() / 2]; let mut dp1 = vec![0.; histo.len() / 2]; let mut dp2 = vec![0.; histo.len() / 2]; let mid = (histo.len() + 1) / 2; let max = (histo[mid] as f64).log10(); // on par du principe que le max est au centre for i in 0..histo.len() { normalised[i] = (histo[i] as f64 + 1.).log10() / max; } for i in (mid)..(histo.len() - 1) { p1[i - mid] = mid as f64 * ((normalised[mid] - normalised[i + 1]) / (i - mid + 2) as f64); } for i in (1..mid).rev() { p2[mid - i - 1] = mid as f64 * ((normalised[mid] - normalised[i]) / (mid - i) as f64); } for i in 0..(mid - 1) { dp1[i] = p1[i + 1] - p1[i]; dp2[i] = p2[i + 1] - p2[i]; } let mut dist_1 = 0; for (i, v) in dp1.iter().enumerate() { if v < &0. { dist_1 = i; break; } } let mut dist_2 = 0; for (i, v) in dp2.iter().enumerate() { if v < &0. { dist_2 = i; break; } } ( (dist_1, normalised[mid + dist_1]), (dist_2, normalised[mid - dist_2]), ) } #[allow(dead_code)] pub fn trackbar_init_param(mem: &mut Param, winname: &str) -> Result<()> { named_window(winname, WINDOW_AUTOSIZE)?; highgui::move_window(winname, 20, 20)?; let v: VecN = VecN::new(0., 0., 0., 255.); let m = Mat::new_rows_cols_with_default(1, 1024, CV_8UC3, v)?; highgui::imshow(winname, &m)?; create_trackbar("nb_all", winname, Some(&mut mem.nb_all), 400, None)?; create_trackbar("nb_visible", winname, Some(&mut mem.nb_visible), 400, None)?; create_trackbar("nb_wait", winname, Some(&mut mem.nb_wait), 40, None)?; create_trackbar("r", winname, Some(&mut mem.r), MAX_TRACKBAR, None)?; create_trackbar("g", winname, Some(&mut mem.g), MAX_TRACKBAR, None)?; create_trackbar("b", winname, Some(&mut mem.b), MAX_TRACKBAR, None)?; Ok(()) } #[allow(dead_code)] pub fn trackbar_line_segment(mem: &mut Param, winname: &str) -> Result<()> { //highgui let winname = format!("{}: {}", winname, 0); //"bord selected: 0"; named_window(winname.as_str(), WINDOW_AUTOSIZE)?; highgui::move_window(winname.as_str(), 20, 520)?; //highgui::move_window(winname, 20, 20)?; let v: VecN = VecN::new(0., 0., 0., 255.); let m = Mat::new_rows_cols_with_default(1, 1800, CV_8UC3, v)?; highgui::imshow(winname.as_str(), &m)?; // create_trackbar( "canny min", winname.as_str(), Some(&mut mem.canny_v1), MAX_TRACKBAR, None, )?; create_trackbar( "canny max", winname.as_str(), Some(&mut mem.canny_v2), MAX_TRACKBAR, None, )?; create_trackbar( "rho : ", winname.as_str(), Some(&mut mem.hough_param.rho), 1000, None, )?; create_trackbar( "theta : ", winname.as_str(), Some(&mut mem.hough_param.theta), 1000, None, )?; create_trackbar( "treshold: ", winname.as_str(), Some(&mut mem.hough_param.treshold), 255, None, )?; create_trackbar( "min_leng: ", winname.as_str(), Some(&mut mem.hough_param.min_length), 1000, None, )?; create_trackbar( "max_gap : ", winname.as_str(), Some(&mut mem.hough_param.max_line_gap), 50000, None, )?; Ok(()) } #[allow(dead_code)] pub fn line_pos(mem: &mut Param, winname: &str) -> Result<()> { named_window(winname, WINDOW_AUTOSIZE)?; highgui::move_window(winname, 20, 20)?; let v: VecN = VecN::new(0., 0., 0., 255.); let m = Mat::new_rows_cols_with_default(1, 1024, CV_8UC3, v)?; highgui::imshow(winname, &m)?; for i in 0..mem.line_pos.len() { create_trackbar( format!("pt[{i}]:\t").as_str(), winname, Some(&mut mem.line_pos[i]), 4095, None, )?; } Ok(()) } pub fn adding_trackbar(mem: &mut Param, _winname: &str) -> Result<()> { //println!("winname: {winname}"); //line_pos(&mut mem, "Play Line")?; trackbar_init_param(mem, "init_param")?; named_window("histo bgr", WINDOW_AUTOSIZE)?; associate_trackbar("histo bgr", &mut mem.tresh)?; create_trackbar( "nb_liss", "histo bgr", Some(&mut mem.nb_liss), MAX_TRACKBAR, None, )?; //trackbar_line_segment(mem, "line detector")?; Ok(()) } pub fn associate_trackbar(winname: &str, tresh: &mut Treshold) -> Result<()> { create_trackbar( "blue min: ", winname, Some(&mut tresh.min_0), MAX_TRACKBAR, None, )?; create_trackbar( "blue max: ", winname, Some(&mut tresh.max_0), MAX_TRACKBAR, None, )?; create_trackbar( "green min: ", winname, Some(&mut tresh.min_1), MAX_TRACKBAR, None, )?; create_trackbar( "green max: ", winname, Some(&mut tresh.max_1), MAX_TRACKBAR, None, )?; create_trackbar( "red min: ", winname, Some(&mut tresh.min_2), MAX_TRACKBAR, None, )?; create_trackbar( "red max: ", winname, Some(&mut tresh.max_2), MAX_TRACKBAR, None, )?; Ok(()) }