diff --git a/src/main.rs b/src/main.rs index 98c290e..c9e6f12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -//mod qualib_refacto; -//use qualib_refacto; -//use qualib_refacto::{self, Qualibration as Qualib}; - /// /// Configure udev: /// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md @@ -12,10 +8,10 @@ mod framerate; mod logs; mod point; -mod qualib_refacto; +mod qualibration; mod utils; -use qualib_refacto::{annalyse::adding_trackbar, Qualibration, Sequence}; +use qualibration::{annalyse::adding_trackbar, Qualibration}; use conf::Conf; use log::{/*debug, warn, */ error, info}; @@ -61,7 +57,7 @@ fn run_all() -> Result<(), Box> { let client = Client::open(config.redis_url.clone())?; let mut con: Connection = client.get_connection()?; - let mut framerate_handler = framerate::Framerate::new(config.framerate)?; + let mut _framerate_handler = framerate::Framerate::new(config.framerate)?; // Setup handler for interrupt Signals let running = Arc::new(AtomicBool::new(true)); @@ -91,7 +87,7 @@ fn run_all() -> Result<(), Box> { )?; while running.load(Ordering::SeqCst) { - //let _t = framerate_handler.handle_time()?; + //let _t = _framerate_handler.handle_time()?; ///////////////// let key = highgui::wait_key(1)?; qualibration.param.key = key; diff --git a/src/qualib_refacto.rs b/src/qualib_refacto.rs deleted file mode 100644 index 1e40c2d..0000000 --- a/src/qualib_refacto.rs +++ /dev/null @@ -1,127 +0,0 @@ -//use opencv::Result; -//use opencv::core::{self, Mat}; - -static DEBUG: bool = true; - -pub mod annalyse; -pub mod borders; -pub mod compute_image; - -use std::env::args; -use std::time::Instant; - -use crate::draw::{draw_line, draw_line_dotted}; -use crate::point::{Color, Point}; - -use opencv::core::Mat; -use opencv::Result; -//use std::f64::consts::PI; - -use opencv::prelude::*; -use opencv::{ - highgui, - videoio::{self, VideoCapture}, -}; - -mod param; -use param::Param; - -mod init_border; -mod load_image; -mod save_image; -mod wait_space; -use init_border::InitBorder; -use load_image::LoadImage; -use save_image::SaveImage; -use wait_space::WaitSpace; - -pub trait Sequence { - fn draw(&self, mem: &Param) -> Option>; - fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box>; - fn is_capture(&self) -> bool; -} - -impl std::fmt::Debug for dyn Sequence { - fn fmt(self: &Self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - - -#[derive(Debug)] -pub struct Qualibration { - seq: Vec>, - cam: VideoCapture, - - pub param: Param, -} - -impl Qualibration { - pub fn new() -> Result { - //let v: Vec> = vec![]; - - let mut dir_name = "".to_string(); //"building.jpg".to_string(); // by default - if let Some(dir_name_arg) = args().nth(1) { - dir_name = dir_name_arg; - } - - let mut cam = videoio::VideoCapture::new(0, videoio::CAP_ANY)?; // 0 is the default camera ; - let opened_cam = videoio::VideoCapture::is_opened(&cam)?; - if !opened_cam { - panic!("Unable to open default camera!"); - } - - let mut frame = Mat::default(); - cam.read(&mut frame)?; - - let beg = Point::from((0., 0., 38400)); // r:150, v:0, b:0 - let end = Point::from((4095., 4095., 38400)); // r:150, v:0, b:0 - let seq: Vec> = vec![ - Box::new(LoadImage::new()), - Box::new(WaitSpace::new()), - Box::new(InitBorder::new(beg, end)), - Box::new(SaveImage::new()), - ]; - //let now = std::time::Instant::now(); - Ok(Qualibration { - seq, - cam, - param: Param::new(dir_name.to_owned())?, - }) - } - - pub fn draw_sequence(&mut self) -> Option> { - if self.param.seq_id >= self.seq.len() { - return None; - } - - let pl = self.seq[self.param.seq_id].draw(&self.param); - - if pl.is_none() { - self.param.seq_id += 1; - if self.param.capture_mode { - self.param.imgs.push(vec![]); - } - } - - pl - } - - pub fn run_step(self: &mut Self) -> Result<(), Box> { - let mut frame = Mat::default(); - if self.param.capture_mode { - self.cam.read(&mut frame)?; - highgui::imshow("camera", &frame)?; - - if frame.size()?.width > 0 && self.seq[self.param.seq_id].is_capture() { - self.param.imgs[self.param.seq_id].push(frame.clone()); - } - } - - if frame.size()?.width > 0 || !self.param.capture_mode { - self.seq[self.param.seq_id].compute_sequence(&mut self.param)?; - } - - Ok(()) - } -} diff --git a/src/qualib_refacto/annalyse.rs b/src/qualib_refacto/annalyse.rs deleted file mode 100644 index b69b443..0000000 --- a/src/qualib_refacto/annalyse.rs +++ /dev/null @@ -1,968 +0,0 @@ -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; - -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(()) -} - -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(()) -} - -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 -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) -} - -// 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(); - 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]), - ) -} - -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("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(()) -} - -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(()) -} - -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(mut 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(()) -} diff --git a/src/qualib_refacto/borders.rs b/src/qualib_refacto/borders.rs deleted file mode 100644 index 326176f..0000000 --- a/src/qualib_refacto/borders.rs +++ /dev/null @@ -1,182 +0,0 @@ -use super::param::HoughLine; -use crate::utils::{CartesianEquation, EqAffine, Pt}; -use opencv::core::{add, subtract, Mat, VecN, Vector, CV_8UC3}; -//use opencv::prelude::MatTraitConst; -use opencv::imgproc::{cvt_color, hough_lines_p, COLOR_GRAY2BGR}; -use opencv::prelude::*; //MatTraitConst; -use opencv::types::VectorOfVec4i; -use opencv::Result; - -pub fn mix_borders(background: &Mat, borders: Vec) -> Result { - let (row, col) = (background.rows(), background.cols()); - //let mask = Mat::default(); - let v: VecN = VecN::new(0., 0., 0., 0.); - let mut sum_diff_0 = Mat::new_rows_cols_with_default(row, col, CV_8UC3, v)?; - let mut sum_diff_1 = Mat::new_rows_cols_with_default(row, col, CV_8UC3, v)?; - let mask = Mat::default(); - let mut tmp = Mat::default(); - - // on va faire la somme des difference - // on va les ajouter a l'image du fond - for bord in borders { - //let diff = image_diff(&bord, background)?; - //add(&diff, &mid, &mut diff_bgr_2, &mask, -1)?; - let mut diff = Mat::default(); - subtract(&bord, background, &mut diff, &mask, -1)?; - add(&diff, &sum_diff_0, &mut tmp, &mask, -1)?; - sum_diff_0 = tmp.clone(); - - subtract(background, &bord, &mut diff, &mask, -1)?; - add(&diff, &sum_diff_1, &mut tmp, &mask, -1)?; - sum_diff_1 = tmp.clone(); - } - //let v: VecN = VecN::new(128., 128., 128., 128.); - //let mid = Mat::new_rows_cols_with_default(row, col, CV_8UC3, v)?; - let mut tmp = Mat::default(); - //let mut tmp2 = Mat::default(); - let mut mix = Mat::default(); - add(&sum_diff_0, &background, &mut tmp, &mask, -1)?; - subtract(&tmp, &sum_diff_1, &mut mix, &mask, -1)?; - - Ok(mix) -} - -//impl Add for (f64, f64) { -//} -pub fn bord_mult(pt: Vec<(f64, f64)>, factor: f64) -> Vec<(f64, f64)> { - let pt: Vec = pt.iter().map(|p| Pt::from(p)).collect(); - - let mut pa = vec![]; - let mut pb = vec![]; - for i in 0..pt.len() { - let k = (i + pt.len() - 1) % pt.len(); - let j = (i + 1) % pt.len(); - pa.push((pt[i] - pt[j]) * factor + pt[j]); - pb.push((pt[i] - pt[k]) * factor + pt[k]); - } - - let mut eq = vec![]; - for i in 0..pt.len() { - let j = (i + 1) % pt.len(); - eq.push(CartesianEquation::new_from_pt(&pb[i], &pa[j])); - } - - let mut p_out = vec![]; - for i in 0..pt.len() { - let k = (i + pt.len() - 1) % pt.len(); - p_out.push(eq[i].intersection(&eq[k]).unwrap()); // TODO: faire un truc pour le unwrap... - // normalement c'est un gars sur ta - // compris ;)... mais bon... un alignement - // malencontreux ca arrive vite - } - - p_out.iter().map(|p| (p.x, p.y)).collect() -} - -// en fait ca marche pas dutout...next time -pub fn bord_mult_v2(pt: Vec<(f64, f64)>, factor: f64) -> Vec<(f64, f64)> { - let mut pt: Vec = pt.iter().map(|p| Pt::from(p)).collect(); - - let mut pn = vec![]; - for i in 0..pt.len() { - let j = (i + 2) % pt.len(); - pn.push((pt[i] - pt[j]) * factor + pt[j]); - } - - pn.iter().map(|p| (p.x, p.y)).collect() -} - -pub fn get_intersection(pts: &[((f64, f64), (f64, f64))]) -> Vec<(f64, f64)> { - let mut eq_cart = vec![]; - for i in 0..pts.len() { - eq_cart.push(CartesianEquation::new_from_tuple(pts[i])); - } - - let mut points = vec![]; - for i in 0..eq_cart.len() { - let id_next = (i + 1) % eq_cart.len(); - let pt = eq_cart[i].intersection(&eq_cart[id_next]).unwrap(); // TODO verifier quand meme la sortie au lieu de unwrap salement... xD - points.push((pt.x, pt.y)); - } - - points -} - -pub fn get_extermities(lines: &Vector>, id: usize) -> ((f64, f64), (f64, f64)) { - let mut p0: (f64, f64) = (0., 0.); - let mut p1: (f64, f64) = (0., 0.); - let (mut min, mut max): (f64, f64) = (f64::MAX, f64::MIN); - //let mut eq: (f64, f64, f64) = (0., 0., 0.); - let mut dst_sum = 0.; - let mut v_eq = vec![]; - - // on cherche les extremite - for l in lines { - // rename value and switch x and y if necessery - let (mut a0, mut b0, mut a1, mut b1) = if id % 2 == 0 { - (l[0] as f64, l[1] as f64, l[2] as f64, l[3] as f64) - } else { - (l[1] as f64, l[0] as f64, l[3] as f64, l[2] as f64) // switch x <-> y - }; - - // reorder if not - if a0 > a1 { - (a0, b0, a1, b1) = (a1, b1, a0, b0); - } - - // update min/max - min = min.min(a0).min(a1); - max = max.max(a0).max(a1); - - // cancel computation if devide by zero - if a1 - a0 == 0. { - continue; - } - - let eq = EqAffine::new(a0, b0, a1, b1); - dst_sum += eq.dst; - v_eq.push(eq); - } - - p0.0 = min; - p1.0 = max; - for eq in v_eq { - p0.1 += eq.get_val_dst(min); - p1.1 += eq.get_val_dst(max); - } - p0.1 /= dst_sum; - p1.1 /= dst_sum; - - // revert x-y if already reverted previously - if id % 2 != 0 { - p0 = (p0.1, p0.0); - p1 = (p1.1, p1.0); - } - - (p0, p1) -} - -pub fn probabilistic_hough( - edges: &Mat, - hough_param: &HoughLine, - id: usize, -) -> Result>> { - let mut p_lines = VectorOfVec4i::new(); - let mut probabalistic_hough = Mat::default(); - - cvt_color(edges, &mut probabalistic_hough, COLOR_GRAY2BGR, 0)?; - - // 2. Use Probabilistic Hough Transform - let p = hough_param.get_param(); - hough_lines_p( - edges, - &mut p_lines, - p.rho, - p.theta, - p.treshold, - p.min_length, - p.max_line_gap, - )?; - - Ok(p_lines) -} diff --git a/src/qualib_refacto/compute_image.rs b/src/qualib_refacto/compute_image.rs deleted file mode 100644 index 1072dd8..0000000 --- a/src/qualib_refacto/compute_image.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::param::Treshold; -use opencv::core::{self, bitwise_and, in_range, Mat, Scalar, Size_}; -use opencv::imgproc; -use opencv::Result; - -pub fn image_warp(img: &Mat, homography: &Mat, h_size: Size_) -> Result { - let mut warped_image = Mat::default(); - imgproc::warp_perspective( - &img, - &mut warped_image, - homography, - h_size, - imgproc::INTER_CUBIC, // I dont see difference with INTER_CUBIC - core::BORDER_CONSTANT, - Scalar::default(), - )?; - - Ok(warped_image) -} - -pub fn image_treshold(img: &Mat, tresh: &Treshold) -> Result { - let (t1, s1, l1) = (tresh.min_0 as f64, tresh.min_1 as f64, tresh.min_2 as f64); - let (t2, s2, l2) = (tresh.max_0 as f64, tresh.max_1 as f64, 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(img, &min, &max, &mut color_selected); - let mut bord_treshed = Mat::default(); - bitwise_and(&img, &img, &mut bord_treshed, &color_selected)?; - - Ok(bord_treshed) -} - -pub fn image_warp_treshold( - img: &Mat, - homography: &Mat, - h_size: Size_, - tresh: &Treshold, -) -> Result { - let warped = image_warp(img, homography, h_size)?; - let treshed = image_treshold(&warped, tresh)?; - Ok(treshed) -} diff --git a/src/qualibration.rs b/src/qualibration.rs index e255cbf..a1f8f15 100644 --- a/src/qualibration.rs +++ b/src/qualibration.rs @@ -1,121 +1,44 @@ +//use opencv::Result; +//use opencv::core::{self, Mat}; + +static DEBUG: bool = true; + pub mod annalyse; pub mod borders; pub mod compute_image; -use annalyse::{ - draw_histograme_bgr_tresh, get_horizontal_segment, get_vertical_segment, histogram_3d, - image_diff, image_mean, -}; // mean dans le sans moyenne des image -use borders::{bord_mult, get_extermities, get_intersection, mix_borders, probabilistic_hough}; -use compute_image::{image_treshold, image_warp, image_warp_treshold}; use std::env::args; -use std::time::Instant; -use crate::draw::{draw_line, draw_line_dotted}; -use crate::point::{Color, Point}; +use crate::point::Point; -use enum_iterator::{next, Sequence as Seq}; use opencv::core::Mat; use opencv::Result; -use std::f64::consts::PI; +//use std::f64::consts::PI; -use opencv::core::{bitwise_and, find_file, in_range, Point as OcvPoint, Scalar, Size_}; -use opencv::core::{VecN, Vector}; -use opencv::imgcodecs::imwrite; -use opencv::imgcodecs::{imread, IMREAD_COLOR}; -use opencv::imgproc::{canny, cvt_color, line, COLOR_BGR2GRAY}; use opencv::prelude::*; use opencv::{ highgui, videoio::{self, VideoCapture}, }; -use std::fs::create_dir; -use std::fs::read_dir; -use opencv::{ - calib3d, - core::{self, Size}, - imgproc, -}; +mod param; +use param::Param; -opencv::opencv_branch_4! { - use opencv::imgproc::LINE_AA; - use opencv::imgproc::LINE_8; -} -opencv::not_opencv_branch_4! { - use opencv::core::LINE_AA; -} - -const DEBUG: bool = true; - -#[derive(Debug, PartialEq, Seq, Copy, Clone)] -pub enum Sequence { - //TODO: avoir le meme nombre d'image en mode capture ET en mode replay - FirstState, - WaitSpace, - BackGround, - UpBorder, - LeftBorder, - DownBorder, - RightBorder, - ReadDir, - ComputeArea, - IdCode1, - IdCode2, - Finish, - - PlayLineDotted, - EmptyFrame, - ComputeLineDotted, - - LinearConstSpeed, // [multiple test] - JumpFromTo, - - AdaptLineSeg(u16), // [multiple test] find the correct distense - AdaptLineLum, // [multiple test] try minimu, medium, maximum. - // - SelectSpeedestColor, // on pourait mettre a jour les valeur a chaque passage - - Vertical(u16), - Horizontal(u16), - SelectNbAll(u16), - ComputeSelectNbAll, - TakeMultiple(u16), - TakeMultipleEmpty(u16), -} +mod sequence; +pub use sequence::{InitBorder, LineDotted, LoadImage, SaveImage, Sequence, WaitSpace, InitIdcode}; #[derive(Debug)] pub struct Qualibration { - pub begin: Instant, - pub dst_size: i32, - pub cam: VideoCapture, - pub r: i32, - pub g: i32, - pub b: i32, - pub capture_mode: bool, - pub frame: Mat, - pub frame_prev: Mat, - pub img: Vec, - pub id: Option, - pub nb_all: i32, - pub nb_visible: i32, - pub nb_liss: i32, - pub tresh: Treshold, - pub dir_name: String, - pub key: i32, - pub canny_v1: i32, - pub canny_v2: i32, - pub hough_param: HoughLine, - pub border_pt: Vec<(f64, f64)>, - 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 - pub cnt: usize, + seq: Vec>, + cam: VideoCapture, + + pub param: Param, } impl Qualibration { pub fn new() -> Result { + //let v: Vec> = vec![]; + let mut dir_name = "".to_string(); //"building.jpg".to_string(); // by default if let Some(dir_name_arg) = args().nth(1) { dir_name = dir_name_arg; @@ -130,816 +53,58 @@ impl Qualibration { let mut frame = Mat::default(); cam.read(&mut frame)?; + // 38400 -> r:150, v:0, b:0 + let beg = Point::from((0., 0., 38400)); + let beg2 = Point::from((4095., 0., 38400)); + let end = Point::from((4095., 4095., 38400)); + let seq: Vec> = vec![ + Box::new(LoadImage::new()), + Box::new(WaitSpace::new()), + Box::new(InitBorder::new(beg, end)), + Box::new(LineDotted::new(beg, end, true, false)), + Box::new(InitIdcode::new(beg2, end)), + Box::new(SaveImage::new()), + ]; //let now = std::time::Instant::now(); Ok(Qualibration { - begin: std::time::Instant::now(), - dst_size: 900, + seq, cam, - r: 150, - g: 0, - b: 0, - capture_mode: dir_name.len() == 0, - img: vec![], - frame: Mat::default(), // TODO: init with frame from cam - frame_prev: Mat::default(), - id: Some(Sequence::FirstState), - nb_all: 120, - nb_visible: 40, - nb_liss: 10, - tresh: Treshold::new("histogram: 0", 150, 255)?, - dir_name: dir_name.clone(), - key: 10, - canny_v1: 170, - canny_v2: 255, - hough_param: HoughLine { - rho: 100, - theta: 100, - treshold: 30, - min_length: 0, - max_line_gap: 50000, - }, - border_pt: vec![], - homography: Mat::default(), - h_size: Size::default(), - line_pos: vec![4095; 34], - multiple: 20, - cnt: 0, + param: Param::new(dir_name.to_owned())?, }) } - pub fn run_step(&mut self) -> Result<(), Box> { - if self.capture_mode && self.id != Some(Sequence::PlayLineDotted) { - //println!("capture"); - self.cam.read(&mut self.frame)?; - highgui::imshow("camera", &self.frame)?; - } - - if self.frame.size()?.width > 0 && self.frame_prev.size()?.width > 0 || !self.capture_mode { - if self.id.is_some() { - self.id = if true { - //self.capture_mode || self.id == Some(Sequence::WaitSpace) || is_same_frame(&self.frame, &self.frame_prev)? { - if self.id != Some(Sequence::WaitSpace) - && self.id != Some(Sequence::FirstState) - && self.id != Some(Sequence::PlayLineDotted) - && self.capture_mode - { - self.img.push(self.frame.clone()); - } - self.compute_sequence()?; - self.get_next_id_seq() - } else { - self.id - }; - } - } - //println!("sequence: {:?}", self.id); - self.frame_prev = self.frame.clone(); - self.cnt += 1; - Ok(()) - } - - pub fn draw_sequence(&self) -> Result, Box> { - if !self.capture_mode { - return Ok(vec![]); - } - let seq = self.id; - let mut pl = vec![]; - //let color = Color { r: 0, g: 30, b: 0 }; - let color = Color { - r: self.r as u8, - g: self.g as u8, - b: self.b as u8, - }; - //let color = Color { r: 0, g: 0, b: 50 }; - let (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb) = - get_point_to_draw(self.r as u8, self.g as u8, self.b as u8, color.clone()); - let nb_all = self.nb_all as usize; - let nb_visible = self.nb_visible as usize; - 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::IdCode1 => { - // on va en haut a gauche - // on va a droite en clognotant sur les nombre pair - pl.extend(draw_line_dotted(&pa, &pb, nb_all, nb_visible, true)); - } - Sequence::IdCode2 => { - // on va en haut a gauche - // on va a droite en clognotant sur les nombre impair - pl.extend(draw_line_dotted(&pa, &pb, nb_all, nb_visible, false)); - } - Sequence::PlayLineDotted - | Sequence::TakeMultiple(_) - | Sequence::ComputeLineDotted => { - // la on va faire une ligne qu'on peut observer - let black = Color { r: 0, g: 0, b: 0 }; - for _ in 0..nb_all { - pl.push(Point { - x: 0., - y: 0., - color: black, - }); - } - 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, - }); - } - 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, - }); - } - } - Sequence::WaitSpace => { - pl = draw_line(&p0, &p1, nb_all, nb_visible); - pl.extend(draw_line(&p1, &p2, nb_all, nb_visible)); - pl.extend(draw_line(&p3, &p0, nb_all, nb_visible)); - pl.extend(draw_line(&p2, &p3, nb_all, nb_visible)); - - pl.extend(draw_line_dotted(&p4, &p5, nb_all, nb_visible, true)); - pl.extend(draw_line_dotted(&p6, &p7, nb_all, nb_visible, true)); - pl.extend(draw_line_dotted(&p8, &p9, nb_all, nb_visible, true)); - } - Sequence::SelectNbAll(n) => { - pl = draw_line(&p0, &p1, n as usize, n as usize); - pl.extend(draw_line(&p1, &p2, n as usize, n as usize)); - pl.extend(draw_line(&p3, &p0, n as usize, n as usize)); - pl.extend(draw_line(&p2, &p3, n as usize, n as usize)); - } - Sequence::UpBorder => { - pl = draw_line(&p0, &p1, nb_all, nb_visible); - } - Sequence::RightBorder => { - pl = draw_line(&p1, &p2, nb_all, nb_visible); - } - Sequence::DownBorder => { - pl = draw_line(&p2, &p3, nb_all, nb_visible); - } - Sequence::LeftBorder => { - pl = draw_line(&p3, &p0, nb_all, nb_visible); - } - Sequence::Vertical(n) => { - let p1 = Point { - x: n as f32, - y: 0., - color, - }; - let p2 = Point { - x: n as f32, - y: 4095., - color, - }; - pl = draw_line(&p1, &p2, nb_all, nb_visible); - } - Sequence::Horizontal(n) => { - let p1 = Point { - x: 0., - y: n as f32, - color, - }; - let p2 = Point { - x: 4095., - y: n as f32, - color, - }; - pl = draw_line(&p1, &p2, nb_all, nb_visible); - } - _ => (), - } - } - - Ok(pl) - } - - pub fn get_next_id_seq(&self) -> Option { - let line_max = 4095; - let line_add = 100; - - if self.id.is_none() { + pub fn draw_sequence(&mut self) -> Option> { + if self.param.seq_id >= self.seq.len() { return None; } - //println!("Hey"); - 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)) - } + let pl = self.seq[self.param.seq_id].draw(&self.param); + + if pl.is_none() { + self.param.seq_id += 1; + if self.param.capture_mode { + self.param.imgs.push(vec![]); } - 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)) - } else if (2 * n) > line_max as u16 { - next(&Sequence::SelectNbAll(u16::MAX)) - } else { - next(&Sequence::SelectNbAll(n * 2 - 1)) - } - } - Sequence::WaitSpace => { - //println!("key: {}", self.key); - if self.key == 32 || !self.capture_mode { - next(&Sequence::WaitSpace) - } else { - Some(Sequence::WaitSpace) - } - } - Sequence::PlayLineDotted => { - //println!("key: {}", self.key); - if self.key == 32 || !self.capture_mode { - next(&Sequence::PlayLineDotted) - } else { - Some(Sequence::PlayLineDotted) - } - } - Sequence::Vertical(n) => { - let after = if n > line_max { u16::MAX } else { n + line_add }; - next(&Sequence::Vertical(after)) - } - Sequence::Horizontal(n) => { - let after = if n > line_max { u16::MAX } else { n + line_add }; - next(&Sequence::Horizontal(after)) - } - //Sequence::ComputeArea => Some(Sequence::ComputeArea), // - id => next(&id), } + + pl } - pub fn compute_sequence(&mut self) -> Result<(), Box> { - if self.id.is_none() { - return Ok(()); + pub fn run_step(self: &mut Self) -> Result<(), Box> { + let mut frame = Mat::default(); + if self.param.capture_mode { + self.cam.read(&mut frame)?; + highgui::imshow("camera", &frame)?; + + if frame.size()?.width > 0 && self.seq[self.param.seq_id].is_capture() { + self.param.imgs[self.param.seq_id].push(frame.clone()); + } } - match self.id.unwrap() { - Sequence::IdCode2 => { - let mut id_code_1 = image_diff(&self.img[8], &self.img[1])?; - id_code_1 = image_warp(&id_code_1, &self.homography, self.h_size)?; - id_code_1 = image_treshold(&id_code_1, &self.tresh)?; - - let code_seg_1 = get_horizontal_segment(&id_code_1)?; - let code_seg_1 = code_seg_1[1..(code_seg_1.len() - 1)].to_owned(); - //let l = code_seg_1.len(); - //let code_seg_1 = code_seg_1[(l-16)..(l-1)].to_owned(); - - //let blue = (i as f64 / code_seg_1.len() as f64) * 255.; - let color_1: VecN = VecN::new(255., 0., 0., 255.); - // on dessine - for i in 0..code_seg_1.len() { - let (((x0, y0), (x1, y1)), size) = code_seg_1[i]; - //line(&mut id_code_1, ); - let s = size as i32; - let x = ((x0 + x1) / 2.) as i32; - let y = ((y0 + y1) / 2.) as i32; - let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); - let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); - line(&mut id_code_1, a, b, color_1, 1, LINE_8, 0)?; - //if i < (code_seg_1.len() - 1) { - // let (((x2, y2), _), size) = code_seg_1[i + 1]; - // let x = ((x1 + x2) / 2.) as i32; - // let y = ((y0 + y1) / 2.) as i32; - // let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); - // let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); - // line(&mut id_code_1, a, b, color_1, 1, LINE_8, 0)?; - //} - } - - let mut id_code_2 = image_diff(&self.img[9], &self.img[1])?; - id_code_2 = image_warp(&id_code_2, &self.homography, self.h_size)?; - id_code_2 = image_treshold(&id_code_2, &self.tresh)?; - let code_seg_2 = get_horizontal_segment(&id_code_2)?; - let l = code_seg_2.len(); - let code_seg_2 = code_seg_2[(l - 16)..(l - 1)].to_owned(); - //highgui::imshow("code 2", &id_code_2)?; - let color_2: VecN = VecN::new(0., 255., 0., 255.); - // on dessine - for i in 0..code_seg_2.len() { - let (((x0, y0), (x1, y1)), size) = code_seg_2[i]; - //line(&mut id_code_2, ); - let s = size as i32; - let x = ((x0 + x1) / 2.) as i32; - let y = ((y0 + y1) / 2.) as i32; - let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); - let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); - line(&mut id_code_1, a, b, color_2, 1, LINE_8, 0)?; - //if i < (code_seg_2.len() - 1) { - // let (((x2, y2), _), size) = code_seg_2[i + 1]; - // let x = ((x1 + x2) / 2.) as i32; - // let y = ((y0 + y1) / 2.) as i32; - // let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); - // let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); - // line(&mut id_code_2, a, b, color_2, 1, LINE_8, 0)?; - //} - } - - // on va faire des ligne sur les endroit de scanne - - highgui::imshow("code 1", &id_code_1)?; - - // si on garde les [(len-16)..(len-1)] - - //let mean = image_mean(&[id_code_1, id_code_2])?; - //highgui::imshow("image mean", &mean)?; - - // la on pourrait aussi mettre les segment - - // On va regarder au milieux de code_2 pour voir si on voi un truc sur code_1 - - // fonction warp image - // fonction select thresh - // BOUUUUUH !!! ... t'as eut peur? - // - //let mut id_code_2 = image_diff(&self.img[9], &self.img[1])?; - //id_code_2 = image_warp(&id_code_2, &self.homography, self.h_size)?; - //id_code_2 = image_treshold(&id_code_2, &self.tresh)?; - //highgui::imshow("code 2", &id_code_2)?; - } - Sequence::ComputeLineDotted => { - let id1 = 7 + (self.cnt % 22); - let id2 = 30 + (self.cnt % 22); - //let backgrounds = self.img[7..30].to_owned(); - //let lines_dots = self.img[30..52].to_owned(); - let backgrounds = self.img[id1..(id1 + 1)].to_owned(); - let lines_dots = self.img[id2..(id2 + 1)].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_CUBIC, // 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; - - background = self.img[1].clone(); - steps = self.img[2..6].into(); - - let mut angles: Vec = vec![]; - for (id, step) in steps.iter().enumerate() { - let lines = get_lines( - &background, - step, - id, - self.canny_v1, - self.canny_v2, - &self.hough_param, - )?; - - for l in lines { - let (x0, y0, x1, y1) = (l[0] as f64, l[1] as f64, l[2] as f64, l[3] as f64); - - let ang = (y1 - y0).atan2(x1 - x0); - angles.push(ang); - } - println!("ang: {angles:?}"); - } - - // on compare ce qui doit l'etre - } - Sequence::ComputeArea => { - let background: Mat; - let borders: Vec; - - background = self.img[1].clone(); - borders = self.img[2..6].into(); - - // on recupere chaqu'un des 4 bord - let mut bords_pts = vec![]; - for (i, bord) in borders.iter().enumerate() { - let bord_pt = self.get_one_border(&background, &bord, i)?; - bords_pts.push(bord_pt); - } - - //for (i, m) in self.img.iter().enumerate() { - // highgui::imshow(format!("img[{i}]").as_str(), m)?; - //} - - // on calcul le cadre - let border_pt = get_intersection(&bords_pts); - self.border_pt = bord_mult(border_pt, 1.1); - let color: VecN = VecN::new(255., 128., 0., 255.); - let mut mixed = mix_borders(&background, borders)?; - let b = &self.border_pt; - for i in 0..b.len() { - let j = (i + 1) % self.border_pt.len(); - let pa = VecN::from_array([b[i].0 as i32, b[i].1 as i32]); - let pb = VecN::from_array([b[j].0 as i32, b[j].1 as i32]); - let a = OcvPoint::from_vec2(pa); - let b = OcvPoint::from_vec2(pb); - line(&mut mixed, a, b, color, 1, LINE_AA, 0)?; - } - //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(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, 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[..])?; - let dst_corners_mat = Mat::from_slice(&dst_corners)?; - let h = calib3d::find_homography( - &roi_corners_mat, - &dst_corners_mat, - &mut Mat::default(), - 0, - 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_CUBIC, // I dont see difference with INTER_CUBIC - core::BORDER_CONSTANT, - Scalar::default(), - )?; // do perspective transformation - //highgui::imshow("Warped Image", &warped_image)?; - } - Sequence::ReadDir => { - if !self.capture_mode { - self.load_image()?; - } - } - Sequence::Finish => { - if self.capture_mode { - self.save_image()? - } - } - _ => (), - } - Ok(()) - } - - fn save_image(&self) -> Result<()> { - // on fait le nom de dossier general au cas ou - // on fait un nom de dossier avec le temps - // on sauvgarde toutes les image - 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() - ); - 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:?}/"); - - // - for (i, img) in self.img.iter().enumerate() { - let mut name_img = name.clone(); - name_img.push_str(&format!("img_{i}.png")); - imwrite(&name_img, img, &Vector::from_slice(&[6, 6, 6, 0]))?; + if frame.size()?.width > 0 || !self.param.capture_mode { + self.seq[self.param.seq_id].compute_sequence(&mut self.param)?; } Ok(()) } - - //use std::cmp::Ordering; - fn load_image(&mut self) -> Result<(), Box> { - let mut imgs = vec![]; - let paths = read_dir(&self.dir_name)?; - //let len = paths.size_hint(); - for entry in paths { - 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 img_id: i32 = e.parse()?; - //println!("c: {c:?}"); - //let a: Vec<_> = path.to_str().unwrap().to_string().chars().collect(); - //let b: String = a[27..(a.len() - 4)].iter().collect(); - //let img_id: i32 = b.parse()?; - let path = format!("{path:?}"); - let path = path[1..(path.len() - 1)].to_owned(); - let img: Mat = imread(&find_file(&path, false, false)?, IMREAD_COLOR)?; - // highgui::imshow(&path, &img)?; - imgs.push((img_id, img)); - } - - imgs.sort_by(|v1, v2| { - if v1.0 > v2.0 { - std::cmp::Ordering::Greater - } else if v1.0 == v2.0 { - std::cmp::Ordering::Equal - } else { - std::cmp::Ordering::Less - } - }); - - //println!("Youpi"); - - for (i, m) in imgs.iter().enumerate() { - self.img.push(m.1.clone()); - //highgui::imshow(format!("img: {i}").as_str(), &m.1)?; - } - - //loop {} - - //prointln!(""); - - Ok(()) - } - - pub fn get_one_border( - &self, - background: &Mat, - bord: &Mat, - id: usize, - ) -> Result<((f64, f64), (f64, f64))> { - let diff: Mat = image_diff(bord, background)?; - - //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(&diff, &min, &max, &mut color_selected); - ////highgui::imshow(format!("mask: {id}").as_str(), &color_selected)?; - //let mut bord_treshed = Mat::default(); - //bitwise_and(&diff, &diff, &mut bord_treshed, &color_selected)?; - ////highgui::imshow(format!("diff & mask: {id}").as_str(), &bord_treshed)?; - - // Pass the image to gray - let mut diff_gray = Mat::default(); - cvt_color(&diff, &mut diff_gray, COLOR_BGR2GRAY, 0)?; - //cvt_color(&bord_treshed, &mut diff_gray, COLOR_BGR2GRAY, 0)?; - - // Apply Canny edge detector - let mut edges = Mat::default(); - canny( - &diff_gray, - &mut edges, - self.canny_v1 as f64, - self.canny_v2 as f64, - 3, - false, - )?; - let lines = probabilistic_hough(&edges, &self.hough_param, id)?; - //let ((x1, y1), (x2, y2)) = get_extermities(&lines, id); - Ok(get_extermities(&lines, id)) - } -} - -fn get_point_to_draw( - r: u8, - g: u8, - b: u8, - color: Color, -) -> ( - Point, - Point, - Point, - Point, - Point, - Point, - Point, - Point, - Point, - Point, - Point, - Point, -) { - let p0 = Point { - x: 0., - y: 0., - color, - }; - let p1 = Point { - x: 4095., - y: 0., - color, - }; - let p2 = Point { - x: 4095., - y: 4095., - color, - }; - let p3 = Point { - x: 0., - y: 4095., - color, - }; - let p4 = Point { - x: 0., - y: 1000., - color: Color { r, g: 0, b: 0 }, - }; - let p5 = Point { - x: 4095., - y: 1000., - color: Color { r, g: 0, b: 0 }, - }; - let p6 = Point { - x: 0., - y: 2000., - color: Color { r: 0, g, b: 0 }, - }; - let p7 = Point { - x: 4095., - y: 2000., - color: Color { r: 0, g, b: 0 }, - }; - let p8 = Point { - x: 0., - y: 3000., - color: Color { r: 0, g: 0, b }, - }; - let p9 = Point { - x: 4095., - y: 3000., - color: Color { r: 0, g: 0, b }, - }; - - let pa = Point { - x: 0., - y: 4095., - color, - }; - let pb = Point { - x: 4095., - y: 4095., - color, - }; - (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb) -} - -// ca c'est les donner manipuler par les slider -#[derive(Debug, Clone)] -pub struct HoughLine { - pub rho: i32, - pub theta: i32, - pub treshold: i32, - pub min_length: i32, - pub max_line_gap: i32, -} - -// ca c'est les donner qu'on envoie a la fonction -pub struct HoughLineValue { - pub rho: f64, - pub theta: f64, - pub treshold: i32, - pub min_length: f64, - pub max_line_gap: f64, -} - -impl HoughLine { - pub fn get_param(&self) -> HoughLineValue { - HoughLineValue { - rho: self.rho as f64 / 100., - theta: self.theta as f64 / 100. * PI / 180., - treshold: self.treshold, - min_length: self.min_length as f64 / 100., - max_line_gap: self.max_line_gap as f64 / 100., - } - } -} - -#[derive(Clone, Debug)] -pub struct Treshold { - pub win_name: String, - pub min_0: i32, - pub min_1: i32, - pub min_2: i32, - pub max_0: i32, - pub max_1: i32, - pub max_2: i32, -} - -impl Treshold { - pub fn new(name: &str, min: i32, max: i32) -> Result { - let tresh = Treshold { - win_name: name.to_owned(), - min_0: min, - min_1: min, - min_2: min, - max_0: max, - max_1: max, - max_2: max, - }; - - Ok(tresh) - } -} - -pub fn get_lines( - background: &Mat, - bord: &Mat, - id: usize, - canny_v1: i32, - canny_v2: i32, - hough_param: &HoughLine, -) -> Result>> { - let diff: Mat = image_diff(bord, background)?; - - // Pass the image to gray - let mut diff_gray = Mat::default(); - cvt_color(&diff, &mut diff_gray, COLOR_BGR2GRAY, 0)?; - // Apply Canny edge detector - let mut edges = Mat::default(); - canny( - &diff_gray, - &mut edges, - canny_v1 as f64, - canny_v2 as f64, - 3, - false, - )?; - let lines = probabilistic_hough(&edges, hough_param, id)?; - //let ((x1, y1), (x2, y2)) = get_extermities(&lines, id); - Ok(lines) } diff --git a/src/qualibration/annalyse.rs b/src/qualibration/annalyse.rs index 4e82325..cdbeae7 100644 --- a/src/qualibration/annalyse.rs +++ b/src/qualibration/annalyse.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::f64::consts::PI; -use super::Qualibration; +use super::Param; use super::DEBUG; use crate::utils::Pt; //use opencv::prelude::MatTraitConst; @@ -25,7 +25,7 @@ opencv::not_opencv_branch_4! { use opencv::core::LINE_AA; } -use super::Treshold; +use super::param::Treshold; const MAX_TRACKBAR: i32 = 255; pub fn draw_histograme_dbg( @@ -377,7 +377,7 @@ pub fn get_horizontal_segment(m: &Mat) -> Result Result> { +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 @@ -534,8 +534,11 @@ pub fn get_vertical_segment(m: &Mat) -> Result> { 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), + ( + (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, )); } @@ -799,7 +802,7 @@ pub fn first_invert(histo: &Vec) -> ((usize, f64), (usize, f64)) { ) } -pub fn trackbar_init_param(mem: &mut Qualibration, winname: &str) -> Result<()> { +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.); @@ -815,7 +818,7 @@ pub fn trackbar_init_param(mem: &mut Qualibration, winname: &str) -> Result<()> Ok(()) } -pub fn trackbar_line_segment(mem: &mut Qualibration, winname: &str) -> Result<()> { +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)?; @@ -878,7 +881,7 @@ pub fn trackbar_line_segment(mem: &mut Qualibration, winname: &str) -> Result<() Ok(()) } -pub fn line_pos(mem: &mut Qualibration, winname: &str) -> Result<()> { +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.); @@ -898,7 +901,7 @@ pub fn line_pos(mem: &mut Qualibration, winname: &str) -> Result<()> { Ok(()) } -pub fn adding_trackbar(mut mem: &mut Qualibration, winname: &str) -> Result<()> { +pub fn adding_trackbar(mut mem: &mut Param, winname: &str) -> Result<()> { //println!("winname: {winname}"); //line_pos(&mut mem, "Play Line")?; //trackbar_init_param(mem, "init_param")?; diff --git a/src/qualibration/borders.rs b/src/qualibration/borders.rs index 1302317..326176f 100644 --- a/src/qualibration/borders.rs +++ b/src/qualibration/borders.rs @@ -1,4 +1,4 @@ -use super::HoughLine; +use super::param::HoughLine; use crate::utils::{CartesianEquation, EqAffine, Pt}; use opencv::core::{add, subtract, Mat, VecN, Vector, CV_8UC3}; //use opencv::prelude::MatTraitConst; diff --git a/src/qualibration/compute_image.rs b/src/qualibration/compute_image.rs index 910047b..1072dd8 100644 --- a/src/qualibration/compute_image.rs +++ b/src/qualibration/compute_image.rs @@ -1,4 +1,4 @@ -use super::Treshold; +use super::param::Treshold; use opencv::core::{self, bitwise_and, in_range, Mat, Scalar, Size_}; use opencv::imgproc; use opencv::Result; diff --git a/src/qualib_refacto/param.rs b/src/qualibration/param.rs similarity index 79% rename from src/qualib_refacto/param.rs rename to src/qualibration/param.rs index 622c3fd..52f4487 100644 --- a/src/qualib_refacto/param.rs +++ b/src/qualibration/param.rs @@ -1,8 +1,11 @@ -use opencv::{Result, core::{Mat, Size_, Size, Vector, find_file}}; -use opencv::imgcodecs::{imread, IMREAD_COLOR, imwrite}; -use std::time::Instant; -use std::fs::{create_dir, read_dir}; +use opencv::imgcodecs::{imread, imwrite, IMREAD_COLOR}; +use opencv::{ + core::{find_file, Mat, Size, Size_, Vector}, + Result, +}; use std::f64::consts::PI; +use std::fs::{create_dir, read_dir}; +use std::time::Instant; #[derive(Clone, Debug)] pub struct Param { @@ -14,6 +17,7 @@ pub struct Param { pub b: i32, pub nb_all: i32, pub nb_visible: i32, + pub nb_wait: i32, pub nb_liss: i32, pub tresh: Treshold, pub canny_v1: i32, @@ -33,34 +37,35 @@ pub struct Param { impl Param { pub fn new(dir_name: String) -> Result { Ok(Self { - begin: std::time::Instant::now(), - capture_mode: dir_name.len() == 0, - dir_name, - key: -1, - imgs: vec![vec![]], - seq_id: 0, - dst_size: 900, - r: 150, - g: 0, - b: 0, - nb_all: 120, - nb_visible: 40, - nb_liss: 10, - tresh: Treshold::new("histogram", 160, 255)?, - canny_v1: 170, - canny_v2: 255, - hough_param: HoughLine { - rho: 100, - theta: 100, - treshold: 30, - min_length: 0, - max_line_gap: 50000, - }, - border_pt: vec![], - homography: Mat::default(), - h_size: Size::default(), - line_pos: vec![4095; 34], - multiple: 20, + begin: std::time::Instant::now(), + capture_mode: dir_name.len() == 0, + dir_name, + key: -1, + imgs: vec![vec![]], + seq_id: 0, + dst_size: 900, + r: 150, + g: 0, + b: 0, + nb_all: 120, + nb_visible: 40, + nb_liss: 10, + nb_wait: 30, + tresh: Treshold::new("histogram", 160, 255)?, + canny_v1: 170, + canny_v2: 255, + hough_param: HoughLine { + rho: 100, + theta: 100, + treshold: 30, + min_length: 0, + max_line_gap: 50000, + }, + border_pt: vec![], + homography: Mat::default(), + h_size: Size::default(), + line_pos: vec![4095; 34], + multiple: 20, }) } @@ -122,7 +127,6 @@ impl Param { } } - #[derive(Debug, Clone)] pub struct HoughLine { pub rho: i32, diff --git a/src/qualibration/sequence.rs b/src/qualibration/sequence.rs new file mode 100644 index 0000000..4d543be --- /dev/null +++ b/src/qualibration/sequence.rs @@ -0,0 +1,27 @@ +use super::Param; +use crate::point::Point; + +mod init_border; +mod init_idcode; +mod line_dotted; +mod load_image; +mod save_image; +mod wait_space; +pub use init_border::InitBorder; +pub use init_idcode::InitIdcode; +pub use line_dotted::LineDotted; +pub use load_image::LoadImage; +pub use save_image::SaveImage; +pub use wait_space::WaitSpace; + +pub trait Sequence { + fn draw(&self, mem: &Param) -> Option>; + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box>; + fn is_capture(&self) -> bool; +} + +impl std::fmt::Debug for dyn Sequence { + fn fmt(self: &Self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{self:?}") + } +} diff --git a/src/qualib_refacto/init_border.rs b/src/qualibration/sequence/init_border.rs similarity index 91% rename from src/qualib_refacto/init_border.rs rename to src/qualibration/sequence/init_border.rs index 9b082f3..237de4a 100644 --- a/src/qualib_refacto/init_border.rs +++ b/src/qualibration/sequence/init_border.rs @@ -1,16 +1,19 @@ use crate::draw::draw_line; use crate::point::{Color, Point}; -use crate::qualib_refacto::{param::{Param, HoughLine}, Sequence}; +use crate::qualibration::{ + param::{HoughLine, Param}, + Sequence, +}; -use crate::qualib_refacto::annalyse::image_diff; -use crate::qualib_refacto::borders::{ - bord_mult, get_extermities, get_intersection, mix_borders, probabilistic_hough, +use crate::qualibration::annalyse::image_diff; +use crate::qualibration::borders::{ + bord_mult, get_extermities, get_intersection, probabilistic_hough, }; use opencv::{ calib3d, - core::{self, Mat, Point as OcvPoint, Scalar, Size, VecN, Vector}, - imgproc::{self, canny, cvt_color, line, COLOR_BGR2GRAY}, + core::{Mat, Point as OcvPoint, Size, VecN, Vector}, + imgproc::{canny, cvt_color, COLOR_BGR2GRAY}, Result, }; @@ -23,6 +26,7 @@ opencv::not_opencv_branch_4! { #[derive(Debug, Clone, Copy)] pub struct InitBorder { + finished: bool, borders: [Point; 4], cnt: usize, } @@ -53,6 +57,7 @@ impl InitBorder { }, ], cnt: 0, + finished: false, } } } @@ -92,13 +97,14 @@ impl Sequence for InitBorder { fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { if self.cnt < self.borders.len() { + self.cnt += 1; return Ok(()); } let len = self.borders.len(); let imgs = mem.imgs[mem.seq_id].clone(); + let borders: Vec = imgs[..len].into(); let background = imgs[len].clone(); - let borders: Vec = imgs[..=len].into(); // on recupere chaqu'un des 4 bord let mut bords_pts = vec![]; @@ -167,6 +173,7 @@ impl Sequence for InitBorder { // Scalar::default(), //)?; // do perspective transformation //highgui::imshow("Warped Image", &warped_image)?; + self.finished = true; Ok(()) } diff --git a/src/qualibration/sequence/init_idcode.rs b/src/qualibration/sequence/init_idcode.rs new file mode 100644 index 0000000..81c527c --- /dev/null +++ b/src/qualibration/sequence/init_idcode.rs @@ -0,0 +1,105 @@ +use crate::{ + draw::draw_line_dotted, + point::Point, + qualibration::{ + annalyse::{get_horizontal_segment, image_diff}, + compute_image::{image_treshold, image_warp}, + param::Param, + Sequence, + }, +}; + +use opencv::{ + core::{Point as OcvPoint, VecN}, + highgui, + imgproc::line, + Result, +}; + +opencv::opencv_branch_4! { + use opencv::imgproc::LINE_8; +} +opencv::not_opencv_branch_4! { + use opencv::core::LINE_AA; +} + +#[derive(Debug, Clone, Copy)] +pub struct InitIdcode { + finished: bool, + cnt: usize, + beg: Point, + end: Point, +} + +impl InitIdcode { + pub fn new(beg: Point, end: Point) -> InitIdcode { + InitIdcode { + finished: false, + cnt: 0, + beg, + end, + } + } +} + +impl Sequence for InitIdcode { + fn draw(&self, mem: &Param) -> Option> { + if self.finished { + return None; + } + if self.cnt == 0 { + return Some(vec![]); + } + let mut pl = vec![]; + pl.extend(draw_line_dotted( + &self.beg, + &self.end, + mem.nb_all as usize, + mem.nb_visible as usize, + true, + )); + Some(pl) + } + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { + if self.cnt == 0 { + self.cnt += 1; + return Ok(()) + } + let id = mem.seq_id; + let mut id_code_1 = image_diff(&mem.imgs[id][1], &mem.imgs[id][0])?; + + id_code_1 = image_warp(&id_code_1, &mem.homography, mem.h_size)?; + id_code_1 = image_treshold(&id_code_1, &mem.tresh)?; + + let code_seg_1 = get_horizontal_segment(&id_code_1)?; + let code_seg_1 = code_seg_1[1..(code_seg_1.len() - 1)].to_owned(); + + // on dessine + let color_1: VecN = VecN::new(255., 0., 0., 255.); + for i in 0..code_seg_1.len() { + let (((x0, y0), (x1, y1)), size) = code_seg_1[i]; + //line(&mut id_code_1, ); + let s = size as i32; + let x = ((x0 + x1) / 2.) as i32; + let y = ((y0 + y1) / 2.) as i32; + let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); + let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); + line(&mut id_code_1, a, b, color_1, 1, LINE_8, 0)?; + if i < (code_seg_1.len() - 1) { + let (((x2, _), _), _) = code_seg_1[i + 1]; + let x = ((x1 + x2) / 2.) as i32; + let y = ((y0 + y1) / 2.) as i32; + let a = OcvPoint::from_vec2(VecN::from_array([x, y - s])); + let b = OcvPoint::from_vec2(VecN::from_array([x, y + s])); + line(&mut id_code_1, a, b, color_1, 1, LINE_8, 0)?; + } + } + + highgui::imshow("code 1", &id_code_1)?; + self.finished = true; + Ok(()) + } + fn is_capture(&self) -> bool { + true + } +} diff --git a/src/qualibration/sequence/line_dotted.rs b/src/qualibration/sequence/line_dotted.rs new file mode 100644 index 0000000..b35a403 --- /dev/null +++ b/src/qualibration/sequence/line_dotted.rs @@ -0,0 +1,167 @@ +use crate::point::{Color, Point}; +use crate::qualibration::{ + annalyse::{ + draw_histograme_bgr_tresh, get_horizontal_segment, get_vertical_segment, histogram_3d, + image_diff, + }, + param::Param, + Sequence, +}; + +use opencv::{ + core::{bitwise_and, in_range, Mat, Point as OcvPoint, Scalar, VecN, BORDER_CONSTANT}, + highgui, + imgproc::{self, line}, + Result, +}; + +opencv::opencv_branch_4! { + use opencv::imgproc::LINE_8; +} +opencv::not_opencv_branch_4! { + use opencv::core::LINE_AA; +} + +#[derive(Debug, Clone)] +pub struct LineDotted { + finished: bool, + cnt: usize, + beg: Point, + end: Point, + continuous_y: bool, + continuous_x: bool, +} + +impl LineDotted { + pub fn new(beg: Point, end: Point, continuous_y: bool, continuous_x: bool) -> Self { + Self { + finished: false, + cnt: 0, + beg, + end, + continuous_x, + continuous_y, + } + } +} + +impl Sequence for LineDotted { + fn draw(&self, mem: &Param) -> Option> { + if self.finished { + return None; + } + if self.cnt == 0 { + return Some(vec![]); + } + let nb_all = mem.nb_all; + let nb_wait = mem.nb_wait as usize; + let nb_visible = mem.nb_visible as usize; + let mut pl = vec![]; + let color = Color { + r: mem.r as u8, + g: mem.g as u8, + b: mem.b as u8, + }; + let black = Color { r: 0, g: 0, b: 0 }; + for _ in 0..nb_all { + pl.push(Point { + color: black, + ..self.beg + }); + } + let len = (2 * mem.line_pos.len() + nb_wait) as f32; + for i in 0..nb_wait { + let val_x = i as f32 / len * (self.end.x - self.beg.x) + self.beg.x; + let val_y = i as f32 / len * (self.end.y - self.beg.y) + self.beg.y; + pl.push(Point { + x: if self.continuous_x { val_x } else { self.beg.x }, + y: if self.continuous_y { val_y } else { self.beg.y }, + color: black, + }); + } + for i in 0..(mem.line_pos.len() * 2) { + let val_cont_x = (i + nb_wait) as f32 / len * (self.end.x - self.beg.x) + self.beg.x; + let val_cont_y = (i + nb_wait) as f32 / len * (self.end.y - self.beg.y) + self.beg.y; + let val_x = mem.line_pos[i / 2] as f32 + self.beg.x; + let val_y = mem.line_pos[i / 2] as f32 + self.beg.y; + let is_visible = (i + nb_wait) % 2 == 0 && i < nb_visible; + let c = if is_visible { color } else { black }; + pl.push(Point { + x: if self.continuous_x { val_cont_x } else { val_x }, + y: if self.continuous_y { val_cont_y } else { val_y }, + color: c, + }); + } + + Some(pl) + } + + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { + if self.cnt < 1 { + self.cnt += 1; + } + let ids = mem.seq_id; + let background = mem.imgs[ids][0].to_owned(); + let line_dot = mem.imgs[ids][1].to_owned(); + let diff = image_diff(&background, &line_dot)?; + + let mut warped_image = Mat::default(); + imgproc::warp_perspective( + &diff, + &mut warped_image, + &mem.homography, + mem.h_size, + imgproc::INTER_CUBIC, // I dont see difference with INTER_CUBIC + BORDER_CONSTANT, + Scalar::default(), + )?; + //highgui::imshow("Warped Image", &warped_image)?; + + let histo = histogram_3d(&warped_image, mem.nb_liss)?; + draw_histograme_bgr_tresh("histo bgr", &histo, &mem.tresh)?; + + let (t1, s1, l1) = ( + mem.tresh.min_0 as f64, + mem.tresh.min_1 as f64, + mem.tresh.min_2 as f64, + ); + let (t2, s2, l2) = ( + mem.tresh.max_0 as f64, + mem.tresh.max_1 as f64, + mem.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 = if self.continuous_y { + get_vertical_segment(&bord_treshed)? + } else { + get_horizontal_segment(&bord_treshed)? + }; + for (i, (((x0, y0), (x1, y1)), _size)) 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)?; + self.finished = true; + Ok(()) + } + fn is_capture(&self) -> bool { + true + } +} diff --git a/src/qualib_refacto/load_image.rs b/src/qualibration/sequence/load_image.rs similarity index 85% rename from src/qualib_refacto/load_image.rs rename to src/qualibration/sequence/load_image.rs index c7aa37f..c723241 100644 --- a/src/qualib_refacto/load_image.rs +++ b/src/qualibration/sequence/load_image.rs @@ -1,6 +1,6 @@ -use super::{Param, Sequence}; +use super::{super::Param, Sequence}; use crate::point::Point; -use opencv::{core::Mat, Result}; +use opencv::Result; #[derive(Debug, Clone, Copy)] pub struct LoadImage { @@ -9,7 +9,7 @@ pub struct LoadImage { impl LoadImage { pub fn new() -> LoadImage { - LoadImage { finished: true } + LoadImage { finished: false } } } diff --git a/src/qualib_refacto/save_image.rs b/src/qualibration/sequence/save_image.rs similarity index 85% rename from src/qualib_refacto/save_image.rs rename to src/qualibration/sequence/save_image.rs index 68b92da..1ddfec3 100644 --- a/src/qualib_refacto/save_image.rs +++ b/src/qualibration/sequence/save_image.rs @@ -1,6 +1,6 @@ -use super::{Param, Sequence}; +use super::{super::Param, Sequence}; use crate::point::Point; -use opencv::{core::Mat, Result}; +use opencv::Result; #[derive(Debug, Clone, Copy)] pub struct SaveImage { @@ -9,7 +9,7 @@ pub struct SaveImage { impl SaveImage { pub fn new() -> SaveImage { - SaveImage { finished: true } + SaveImage { finished: false } } } diff --git a/src/qualib_refacto/wait_space.rs b/src/qualibration/sequence/wait_space.rs similarity index 81% rename from src/qualib_refacto/wait_space.rs rename to src/qualibration/sequence/wait_space.rs index e3d959b..ff359dd 100644 --- a/src/qualib_refacto/wait_space.rs +++ b/src/qualibration/sequence/wait_space.rs @@ -1,6 +1,6 @@ -use super::{Param, Sequence}; +use super::{super::Param, Sequence}; use crate::point::Point; -use opencv::{core::Mat, Result}; +use opencv::Result; #[derive(Debug, Clone, Copy)] pub struct WaitSpace {} @@ -13,7 +13,7 @@ impl WaitSpace { impl Sequence for WaitSpace { fn draw(&self, mem: &Param) -> Option> { - if mem.key == 32 { + if mem.key == 32 || !mem.capture_mode { return None; } Some(vec![]) diff --git a/src/utils.rs b/src/utils.rs index b55aa46..1223c49 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,13 +1,7 @@ use crate::point::Point; static NEAR_ZERO: f64 = 0.000001; -use std::ops::Add; -use std::ops::AddAssign; -use std::ops::Div; -use std::ops::DivAssign; -use std::ops::Mul; -use std::ops::MulAssign; -use std::ops::Sub; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub}; //use std::ops::BitXor impl Add for Pt {