diff --git a/src/main.rs b/src/main.rs index cc39b18..011300e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,7 +91,7 @@ fn run_all() -> Result<(), Box> { ///////////////// let key = highgui::wait_key(1)?; //if key != -1 { - qualibration.key = key; + qualibration.key = key; //} if key == 27 { // esc in my case @@ -112,7 +112,16 @@ fn run_all() -> Result<(), Box> { qualibration.run_step()?; - if qualibration.capture_mode && (qualibration.id != Some(Sequence::WaitSpace) || qualibration.id != Some(Sequence::PlayLineDotted)) { + let q_id = qualibration.id.clone(); + let mut n = 65534; + if let Sequence::TakeMultiple(m) = q_id.clone().unwrap_or(Sequence::Finish) { + n = m; + }; + if qualibration.capture_mode + && (q_id != Some(Sequence::WaitSpace) + || q_id != Some(Sequence::PlayLineDotted) + || n != 65534) + { let millis = std::time::Duration::from_millis(400); // TODO: find solution to know when change has been done std::thread::sleep(millis); } diff --git a/src/qualibration.rs b/src/qualibration.rs index d68358f..f8b0ca3 100644 --- a/src/qualibration.rs +++ b/src/qualibration.rs @@ -1,10 +1,13 @@ pub mod annalyse; pub mod borders; -use std::time::Instant; -use annalyse::{image_diff, is_same_frame}; +use annalyse::{ + annalyse_segment, draw_histograme_bgr_tresh, get_vertical_segment, histogram_3d, image_diff, + image_mean, is_same_frame, +}; // mean dans le sans moyenne des image use borders::{bord_mult, get_extermities, get_intersection, mix_borders, probabilistic_hough}; use std::env::args; +use std::time::Instant; use crate::draw; use crate::point::{Color, Point}; @@ -35,6 +38,7 @@ use opencv::{ opencv::opencv_branch_4! { use opencv::imgproc::LINE_AA; + use opencv::imgproc::LINE_8; } opencv::not_opencv_branch_4! { use opencv::core::LINE_AA; @@ -55,10 +59,11 @@ pub enum Sequence { ReadDir, ComputeArea, PlayLineDotted, + TakeMultiple(u16), + TakeMultipleEmpty(u16), + ComputeLineDotted, Finish, - - LinearConstSpeed, // [multiple test] JumpFromTo, @@ -76,6 +81,7 @@ pub enum Sequence { #[derive(Debug)] pub struct Qualibration { pub begin: Instant, + pub dst_size: i32, pub cam: VideoCapture, pub r: i32, pub g: i32, @@ -98,6 +104,7 @@ pub struct Qualibration { pub homography: Mat, pub h_size: Size_, pub line_pos: Vec, + pub multiple: u16, // le nombre de fois qu'une photo est prise pour certaine sequence } impl Qualibration { @@ -119,6 +126,7 @@ impl Qualibration { //let now = std::time::Instant::now(); Ok(Qualibration { begin: std::time::Instant::now(), + dst_size: 900, cam, r: 150, g: 0, @@ -131,7 +139,7 @@ impl Qualibration { nb_all: 120, nb_visible: 40, nb_liss: 10, - tresh: Treshold::new("histogram: 0", 0, 255)?, + tresh: Treshold::new("histogram: 0", 150, 255)?, dir_name: dir_name.clone(), key: 10, canny_v1: 150, @@ -147,6 +155,7 @@ impl Qualibration { homography: Mat::default(), h_size: Size::default(), line_pos: vec![4095; 34], + multiple: 20, }) } @@ -182,7 +191,7 @@ impl Qualibration { pub fn draw_sequence(&self) -> Result, Box> { if !self.capture_mode { - return Ok(vec![]) + return Ok(vec![]); } let seq = self.id; let mut pl = vec![]; @@ -213,62 +222,103 @@ impl Qualibration { y: 4095., color, }; - let p4 = Point { x: 0., y: 1000., - color: Color {r: self.r as u8, g:0, b:0}, + color: Color { + r: self.r as u8, + g: 0, + b: 0, + }, }; let p5 = Point { x: 4095., y: 1000., - color: Color {r: self.r as u8, g:0, b:0}, + color: Color { + r: self.r as u8, + g: 0, + b: 0, + }, }; let p6 = Point { x: 0., y: 2000., - color: Color {r: 0, g:self.g as u8, b:0}, + color: Color { + r: 0, + g: self.g as u8, + b: 0, + }, }; let p7 = Point { x: 4095., y: 2000., - color: Color {r: 0, g:self.g as u8, b:0}, + color: Color { + r: 0, + g: self.g as u8, + b: 0, + }, }; - let p8 = Point { x: 0., y: 3000., - color: Color {r: 0, g:0, b:self.b as u8}, + color: Color { + r: 0, + g: 0, + b: self.b as u8, + }, }; let p9 = Point { x: 4095., y: 3000., - color: Color {r: 0, g:0, b:self.b as u8}, + color: Color { + r: 0, + g: 0, + b: self.b as u8, + }, }; + let nb_all = self.nb_all as usize; let nb_visible = self.nb_visible as usize; - let nb_wait = 20; + let nb_wait = 30; // ca permet de prendre de la vitess en y. Et donc ca permet de + // mieux voir les segment qui bouge peut au debut. if seq.is_some() { match seq.unwrap() { - Sequence::PlayLineDotted => { + Sequence::PlayLineDotted + | Sequence::TakeMultiple(_) + | Sequence::ComputeLineDotted => { // la on va faire une ligne qu'on peut observer pl = vec![]; - let black = Color{r: 0, g: 0, b: 0}; + let black = Color { r: 0, g: 0, b: 0 }; for _ in 0..nb_all { - pl.push(Point{x: 0., y: 0., color: black}); + pl.push(Point { + x: 0., + y: 0., + color: black, + }); } - let len = (2*self.line_pos.len() + nb_wait) as f32; + let len = (2 * self.line_pos.len() + nb_wait) as f32; for i in 0..nb_wait { let y = i as f32 * 4095. / len; - pl.push(Point{x: 0., y, color: black}); + pl.push(Point { + x: 0., + y, + color: black, + }); } for i in 0..(self.line_pos.len() * 2) { let y = (i + nb_wait) as f32 * 4095. / len; - let c = if (i + nb_wait) % 2 == 0 && i < nb_visible {color} else {black}; - pl.push(Point{x: self.line_pos[i/2] as f32, y, color: c}); + let c = if (i + nb_wait) % 2 == 0 && i < nb_visible { + color + } else { + black + }; + pl.push(Point { + x: self.line_pos[i / 2] as f32, + y, + color: c, + }); } - } Sequence::WaitSpace => { pl = draw::draw_line(&p0, &p1, nb_all, nb_visible)?; @@ -343,6 +393,21 @@ impl Qualibration { match self.id.unwrap() { //Sequence::Finish => Some(Sequence::Finish), Sequence::Finish => None, + Sequence::ComputeLineDotted => Some(Sequence::ComputeLineDotted), + Sequence::TakeMultiple(n) => { + if n > self.multiple { + next(&Sequence::TakeMultiple(u16::MAX)) + } else { + next(&Sequence::TakeMultiple(n)) + } + } + Sequence::TakeMultipleEmpty(n) => { + if n > self.multiple { + next(&Sequence::TakeMultipleEmpty(u16::MAX)) + } else { + next(&Sequence::TakeMultipleEmpty(n)) + } + } Sequence::SelectNbAll(n) => { if n == 0 { Some(Sequence::SelectNbAll(2 - 1)) @@ -354,7 +419,7 @@ impl Qualibration { } Sequence::WaitSpace => { //println!("key: {}", self.key); - if self.key == 32 || !self.capture_mode{ + if self.key == 32 || !self.capture_mode { next(&Sequence::WaitSpace) } else { Some(Sequence::WaitSpace) @@ -362,7 +427,7 @@ impl Qualibration { } Sequence::PlayLineDotted => { //println!("key: {}", self.key); - if self.key == 32 { + if self.key == 32 || !self.capture_mode { next(&Sequence::PlayLineDotted) } else { Some(Sequence::PlayLineDotted) @@ -384,6 +449,64 @@ impl Qualibration { pub fn compute_sequence(&mut self) -> Result<(), Box> { if self.id.is_some() { match self.id.unwrap() { + Sequence::ComputeLineDotted => { + let backgrounds = self.img[7..30].to_owned(); + let lines_dots = self.img[30..52].to_owned(); + + let background = image_mean(&backgrounds)?; + let line_dot = image_mean(&lines_dots)?; + let diff = image_diff(&background, &line_dot)?; + + let mut warped_image = Mat::default(); + imgproc::warp_perspective( + &diff, + &mut warped_image, + &self.homography, + self.h_size, + imgproc::INTER_LINEAR, // I dont see difference with INTER_CUBIC + core::BORDER_CONSTANT, + Scalar::default(), + )?; + //highgui::imshow("Warped Image", &warped_image)?; + + let histo = histogram_3d(&warped_image, self.nb_liss)?; + draw_histograme_bgr_tresh("histo bgr", &histo, &self.tresh)?; + + let (t1, s1, l1) = ( + self.tresh.min_0 as f64, + self.tresh.min_1 as f64, + self.tresh.min_2 as f64, + ); + let (t2, s2, l2) = ( + self.tresh.max_0 as f64, + self.tresh.max_1 as f64, + self.tresh.max_2 as f64, + ); + let min = Mat::from_slice(&[t1, s1, l1])?; + let max = Mat::from_slice(&[t2, s2, l2])?; + let mut color_selected = Mat::default(); + let _ = in_range(&warped_image, &min, &max, &mut color_selected); + let mut bord_treshed = Mat::default(); + bitwise_and( + &warped_image, + &warped_image, + &mut bord_treshed, + &color_selected, + )?; + //highgui::imshow(format!("warped_image & mask").as_str(), &bord_treshed)?; + + let segments = get_vertical_segment(&bord_treshed)?; + for (i, ((x0, y0), (x1, y1))) in segments.iter().enumerate() { + let blue = (i as f64 / segments.len() as f64) * 255.; + let color: VecN = VecN::new(blue, 128., 0., 255.); + let pa = VecN::from_array([*x0 as i32, *y0 as i32]); + let pb = VecN::from_array([*x1 as i32, *y1 as i32]); + let a = OcvPoint::from_vec2(pa); + let b = OcvPoint::from_vec2(pb); + line(&mut bord_treshed, a, b, color, 1, LINE_8, 0)?; + } + highgui::imshow("segemnt detector", &bord_treshed)?; + } Sequence::ComputeSelectNbAll => { let background: Mat; let steps: Vec; @@ -448,14 +571,16 @@ impl Qualibration { } highgui::imshow("mixed bored", &mixed)?; + let size = self.dst_size; // ici on va requadrer la partie de la projection laser de l'image - let warped_image_size = Size::new(1024, 1024); + let warped_image_size = Size::new(size, size); let roi_corners: Vec = self .border_pt .iter() .map(|(x, y)| OcvPoint::new(*x as i32, *y as i32)) .collect(); - let dst = [(0, 0), (0, 1024), (1024, 1024), (1024, 0)]; + //let dst = [(0, 0), (0, size), (size, size), (size, 0)]; // in: laser repere + let dst = [(0, size), (0, 0), (size, 0), (size, size)]; let dst_corners: Vec = dst.iter().map(|(x, y)| OcvPoint::new(*x, *y)).collect(); let roi_corners_mat = Mat::from_slice(&roi_corners[..])?; @@ -468,12 +593,14 @@ impl Qualibration { 3., )?; //get homography let mut warped_image = Mat::default(); + self.homography = h.clone(); + self.h_size = warped_image_size.clone(); imgproc::warp_perspective( &mixed, &mut warped_image, &h, warped_image_size, - imgproc::INTER_LINEAR, // I dont see difference with INTER_CUBIC + imgproc::INTER_CUBIC, // I dont see difference with INTER_CUBIC core::BORDER_CONSTANT, Scalar::default(), )?; // do perspective transformation @@ -502,7 +629,11 @@ impl Qualibration { let now = self.begin; let name = format!("image/"); create_dir(&name).unwrap_or(()); - let name = format!("image/{:0>6?}_{:0>9?}/", now.elapsed().as_secs(), now.elapsed().as_nanos()); + let name = format!( + "image/{:0>6?}_{:0>9?}/", + now.elapsed().as_secs(), + now.elapsed().as_nanos() + ); create_dir(&name).unwrap_or(()); //name.push_str(format!("image/{}_{}/", now.elapsed().as_secs(), now.elapsed().as_nanos()).as_str()); //let name = format!("image/{now:?}/"); @@ -526,8 +657,8 @@ impl Qualibration { let dir = entry?; let path = dir.path(); let c: Vec<&str> = path.to_str().unwrap().split("/").collect(); - let d: Vec<_> = c[c.len()-1].chars().collect(); - let e: String = d[4..d.len()-4].iter().collect(); + let d: Vec<_> = c[c.len() - 1].chars().collect(); + let e: String = d[4..d.len() - 4].iter().collect(); let img_id: i32 = e.parse()?; //println!("c: {c:?}"); //let a: Vec<_> = path.to_str().unwrap().to_string().chars().collect(); @@ -571,7 +702,6 @@ impl Qualibration { id: usize, ) -> Result<((f64, f64), (f64, f64))> { let diff: Mat = image_diff(bord, background)?; - //let (t1, s1, l1) = ( // self.tresh.min_0 as f64, diff --git a/src/qualibration/annalyse.rs b/src/qualibration/annalyse.rs index 090d04f..0cfc2ed 100644 --- a/src/qualibration/annalyse.rs +++ b/src/qualibration/annalyse.rs @@ -1,5 +1,9 @@ +use std::collections::HashSet; +use std::f64::consts::PI; + use super::Qualibration; use super::DEBUG; +use crate::utils::Pt; //use opencv::prelude::MatTraitConst; use opencv::prelude::*; //MatTraitConst; @@ -8,6 +12,12 @@ 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; } @@ -18,7 +28,7 @@ opencv::not_opencv_branch_4! { use super::Treshold; const MAX_TRACKBAR: i32 = 255; -fn draw_histograme_dbg( +pub fn draw_histograme_dbg( window_name: &str, histo: &Vec, (from, to): (usize, usize), @@ -56,13 +66,18 @@ fn draw_histograme_dbg( Ok(()) } -fn draw_histograme(window_name: &str, histo: &Vec) -> Result<()> { +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(256 * 2, 256 * 2, CV_8UC3, v)?; + 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..256 { + for i in 0..(histo.len() - 1) { if histo[i] > max { max = histo[i]; } @@ -70,13 +85,15 @@ fn draw_histograme(window_name: &str, histo: &Vec) -> Result<()> { let v_log = 10.; - for i in 0..255 { + 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. * 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 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)?; @@ -87,7 +104,7 @@ fn draw_histograme(window_name: &str, histo: &Vec) -> Result<()> { Ok(()) } -fn draw_histograme_bgr(window_name: &str, histo: &Vec>) -> Result<()> { +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.); @@ -126,7 +143,7 @@ fn draw_histograme_bgr(window_name: &str, histo: &Vec>) -> Result<()> { Ok(()) } -fn draw_histograme_bgr_tresh( +pub fn draw_histograme_bgr_tresh( window_name: &str, histo: &Vec>, tresh: &Treshold, @@ -213,6 +230,290 @@ pub fn is_same_frame(frame: &Mat, frame_prev: &Mat) -> Result { } } +// 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 = 360; + 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 err = 0.; + 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)}; + err += p.x * p.x; + 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; + } + //if err < err_min { + // err_min = err; + // 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))); + } + + Ok(segments) +} + +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... +} + +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) +} + +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(); @@ -233,7 +534,7 @@ pub fn image_diff(frame: &Mat, frame_prev: &Mat) -> Result { Ok(d_bgr) } -fn histogram_3d(m: &Mat, nb_liss: i32) -> Result>> { +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]; @@ -262,7 +563,7 @@ fn histogram_3d(m: &Mat, nb_liss: i32) -> Result>> { Ok(histo) } -fn histogram_1d(m: &Mat, nb_liss: i32) -> Result> { +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(); @@ -291,7 +592,7 @@ fn histogram_1d(m: &Mat, nb_liss: i32) -> Result> { Ok(histo) } -fn first_invert(histo: &Vec) -> ((usize, f64), (usize, f64)) { +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]; @@ -356,7 +657,11 @@ pub fn trackbar_line_segment(mem: &mut Qualibration, 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, 20)?; + 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", @@ -405,7 +710,7 @@ pub fn trackbar_line_segment(mem: &mut Qualibration, winname: &str) -> Result<() "max_gap : ", winname.as_str(), Some(&mut mem.hough_param.max_line_gap), - 500000, + 50000, None, )?; Ok(()) @@ -433,23 +738,25 @@ pub fn line_pos(mem: &mut Qualibration, winname: &str) -> Result<()> { pub fn adding_trackbar(mut mem: &mut Qualibration, winname: &str) -> Result<()> { //println!("winname: {winname}"); - line_pos(&mut mem , "Play Line")?; - trackbar_init_param(mem, "init_param")?; - //named_window(winname, WINDOW_AUTOSIZE)?; - //associate_trackbar(winname, &mut mem.tresh)?; - //create_trackbar( - // "nb_liss", - // winname, - // Some(&mut mem.nb_liss), - // MAX_TRACKBAR, - // None, - //)?; - //trackbar_line_segment(mem, 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(()) } -fn associate_trackbar(winname: &str, tresh: &mut Treshold) -> Result<()> { +pub fn associate_trackbar(winname: &str, tresh: &mut Treshold) -> Result<()> { create_trackbar( "blue min: ", winname, diff --git a/src/utils.rs b/src/utils.rs index 65f339b..0bfe6b1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,9 +2,13 @@ use crate::point::Point; static NEAR_ZERO: f64 = 0.000001; use std::ops::Add; +use std::ops::AddAssign; use std::ops::Mul; use std::ops::MulAssign; use std::ops::Sub; +use std::ops::Div; +use std::ops::DivAssign; +//use std::ops::BitXor impl Add for Pt { type Output = Self; @@ -17,6 +21,14 @@ impl Add for Pt { } } +impl AddAssign for Pt { + fn add_assign(&mut self, other: Self) { + self.x += other.x; + self.y += other.y; + } +} + + impl Sub for Pt { type Output = Self; @@ -46,6 +58,24 @@ impl Mul for Pt { } } +impl Div for Pt { + type Output = Self; + + fn div(self, rhs: f64) -> Self { + Pt { + x: self.x / rhs, + y: self.y / rhs, + } + } +} + +impl DivAssign for Pt { + fn div_assign(&mut self, rhs: f64) { + self.x /= rhs; + self.y /= rhs; + } +} + #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub struct Pt { pub x: f64, @@ -75,9 +105,16 @@ impl Pt { Self { x, y } } - pub fn mul_assign(&mut self, rhs: f64) { - self.x *= rhs; - self.y *= rhs; + pub fn cross(&self, other: &Pt) -> f64 { + self.x * other.x + self.y * other.y + } + + pub fn mean(pts: &[Pt]) -> Pt { + let mut mean = Pt{x: 0., y: 0.}; + for pt in pts { + mean += *pt; + } + mean / (pts.len() as f64) } }