From 392455e903bc24e89d296fe3154818cf7f40e934 Mon Sep 17 00:00:00 2001 From: Lapin Raving Date: Wed, 20 Sep 2023 00:48:51 +0200 Subject: [PATCH] feat: dering refacto Qualibration and Sequence --- src/draw.rs | 37 +- src/main.rs | 68 +- src/qualib_refacto.rs | 313 +++++++++ src/qualib_refacto/annalyse.rs | 968 ++++++++++++++++++++++++++++ src/qualib_refacto/borders.rs | 182 ++++++ src/qualib_refacto/compute_image.rs | 43 ++ src/qualib_refacto/init_border.rs | 204 ++++++ src/qualib_refacto/load_image.rs | 33 + src/qualib_refacto/save_image.rs | 31 + src/qualib_refacto/wait_space.rs | 29 + src/qualibration.rs | 40 +- 11 files changed, 1876 insertions(+), 72 deletions(-) create mode 100644 src/qualib_refacto.rs create mode 100644 src/qualib_refacto/annalyse.rs create mode 100644 src/qualib_refacto/borders.rs create mode 100644 src/qualib_refacto/compute_image.rs create mode 100644 src/qualib_refacto/init_border.rs create mode 100644 src/qualib_refacto/load_image.rs create mode 100644 src/qualib_refacto/save_image.rs create mode 100644 src/qualib_refacto/wait_space.rs diff --git a/src/draw.rs b/src/draw.rs index 84165bf..f904f30 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -7,12 +7,12 @@ const X_CENTER: f32 = 2047.0; const Y_CENTER: f32 = 2047.0; pub fn draw_line_dotted( + p0: &Point, p1: &Point, - p2: &Point, nb_all: usize, nb_visible: usize, first_on: bool, -) -> Result, Box> { +) -> Vec { let mut pl = vec![]; let black = Color { r: 0, g: 0, b: 0 }; @@ -21,61 +21,56 @@ pub fn draw_line_dotted( for _ in 0..nb_all { pl.push(Point { color: black, - ..*p1 + ..*p0 }); } for i in 0..nb_visible { pl.push(Point { - color: if i % 2 == cmp { p2.color } else { black }, - ..*p2 + color: if i % 2 == cmp { p1.color } else { black }, + ..*p1 }); } if nb_visible > nb_all { - return Ok(pl); + return pl; } for _ in 0..(nb_all - nb_visible) { pl.push(Point { color: black, - ..*p2 + ..*p1 }); } - Ok(pl) + pl } -pub fn draw_line( - p1: &Point, - p2: &Point, - nb_all: usize, - nb_visible: usize, -) -> Result, Box> { +pub fn draw_line(p0: &Point, p1: &Point, nb_all: usize, nb_visible: usize) -> Vec { let mut pl = vec![]; let black = Color { r: 0, g: 0, b: 0 }; for _ in 0..nb_all { pl.push(Point { color: black, - ..*p1 + ..*p0 }); } for _ in 0..nb_visible { - pl.push(*p2); + pl.push(*p1); } if nb_visible > nb_all { - return Ok(pl); + return pl; } for _ in 0..(nb_all - nb_visible) { pl.push(Point { color: black, - ..*p2 + ..*p1 }); } - Ok(pl) + pl } #[allow(dead_code)] -pub fn draw(_time: f64) -> Result, Box> { +pub fn draw(_time: f64) -> Vec { let mut v: Vec = vec![]; for i in 0..128 { let a = i as f32 / 128.0 * std::f32::consts::PI * 2.0; @@ -89,5 +84,5 @@ pub fn draw(_time: f64) -> Result, Box> { }, }); } - Ok(v) + v } diff --git a/src/main.rs b/src/main.rs index 011300e..98c290e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +//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 @@ -8,10 +12,10 @@ mod framerate; mod logs; mod point; -mod qualibration; +mod qualib_refacto; mod utils; -use qualibration::{annalyse::adding_trackbar, Qualibration, Sequence}; +use qualib_refacto::{annalyse::adding_trackbar, Qualibration, Sequence}; use conf::Conf; use log::{/*debug, warn, */ error, info}; @@ -78,7 +82,7 @@ fn run_all() -> Result<(), Box> { info!("{:?}", config); let mut qualibration = Qualibration::new()?; - adding_trackbar(&mut qualibration, "histogram: 0")?; + adding_trackbar(&mut qualibration.param, "histogram: 0")?; let _a: () = con.set(format!("/kpps/{}", conf2.laser_id), 65535)?; let _a: () = con.set( @@ -90,41 +94,43 @@ fn run_all() -> Result<(), Box> { //let _t = framerate_handler.handle_time()?; ///////////////// let key = highgui::wait_key(1)?; - //if key != -1 { - qualibration.key = key; - //} + qualibration.param.key = key; if key == 27 { - // esc in my case break; } - //qualibration.id = next_id; - let v: Vec<(f32, f32, u32)> = qualibration - .draw_sequence()? - .iter() - .map(|pt| (pt.x, pt.y, u32::from(pt.color))) - .collect(); - // println!("{:?}", v); - let _ = con.set( - format!("/pl/{}/{}", config.client_id, config.laser_id), - format!("{:?}", v), - )?; + let v = qualibration.draw_sequence(); + if v.is_some() { + if qualibration.param.capture_mode { + let pl: Vec<(f32, f32, u32)> = v + .unwrap() + .iter() + .map(|pt| (pt.x, pt.y, u32::from(pt.color))) + .collect(); - qualibration.run_step()?; + let _ = con.set( + format!("/pl/{}/{}", config.client_id, config.laser_id), + format!("{:?}", pl), + )?; + } - 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); + qualibration.run_step()?; } + //qualibration.id = next_id; + + //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); + //} } let _ = con.set( diff --git a/src/qualib_refacto.rs b/src/qualib_refacto.rs new file mode 100644 index 0000000..550bc13 --- /dev/null +++ b/src/qualib_refacto.rs @@ -0,0 +1,313 @@ +//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 enum_iterator::{next, Sequence as Seq}; +use opencv::core::Mat; +use opencv::Result; +use std::f64::consts::PI; + +use opencv::core::{bitwise_and, find_file, in_range, Point as OcvPoint, Scalar, Size, 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; + +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; + +//impl Clone for dyn Sequence { +// fn clone() { +// } +//} + +#[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) + } +} + +//impl + +//#[derive(Copy)] +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(Clone, Debug)] +pub struct Param { + seq_id: usize, + imgs: Vec>, + dst_size: i32, + r: i32, + g: i32, + b: i32, + nb_all: i32, + nb_visible: i32, + nb_liss: i32, + tresh: Treshold, + canny_v1: i32, + canny_v2: i32, + hough_param: HoughLine, + border_pt: Vec<(f64, f64)>, + homography: Mat, + h_size: Size_, + line_pos: Vec, + multiple: u16, // le nombre de fois qu'une photo est prise pour certaine sequence + pub key: i32, + dir_name: String, + begin: Instant, + pub capture_mode: bool, +} + +impl Param { + fn save_image(&self) -> Result<()> { + let now = self.begin; + let img_root = format!("image"); + create_dir(&img_root).unwrap_or(()); + let new_dir = format!( + "{img_root}/{:0>6?}_{:0>9?}", + now.elapsed().as_millis(), + now.elapsed().as_nanos() + ); + create_dir(&new_dir).unwrap_or(()); + for (i, img_seq) in self.imgs.iter().enumerate() { + let seq_dir_name = format!("{new_dir}/{i}"); + create_dir(&seq_dir_name).unwrap_or(()); + for img in img_seq { + let mut name_img = format!("{seq_dir_name}/"); + name_img.push_str(&format!("img_{i}.png")); + imwrite(&name_img, img, &Vector::from_slice(&[6, 6, 6, 0]))?; + } + } + + Ok(()) + } + + fn load_image(&mut self) -> Result<(), Box> { + let mut imgs = vec![]; + let paths = read_dir(&self.dir_name)?; + for entry in paths { + let mut seq_img = vec![]; + let dir = entry?; + let path = dir.path(); // sequence directory + let names: Vec<&str> = path.to_str().unwrap().split("/").collect(); + let seq_id: usize = names[names.len() - 1].parse()?; + for entry in read_dir(&path)? { + let sub_path = entry?.path(); + let names: Vec<&str> = path.to_str().unwrap().split("/").collect(); + let img_name = names[names.len() - 1]; + let img_id: usize = img_name[4..img_name.len() - 4].parse()?; + let img: Mat = imread( + &find_file(&sub_path.to_str().unwrap(), false, false)?, + IMREAD_COLOR, + )?; + seq_img.push((img_id, img)); + } + imgs.push((seq_id, seq_img)); + } + + self.imgs = vec![vec![]; imgs.len()]; + for (seq_id, seq_img) in imgs { + self.imgs[seq_id] = vec![Mat::default(); seq_img.len()]; + for (img_id, img) in seq_img { + self.imgs[seq_id][img_id] = img; + } + } + + Ok(()) + } +} + +#[derive(Debug)] +pub struct Qualibration { + seq: Vec>, + cam: VideoCapture, + cnt: usize, + + 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, + cnt: 0, + param: Param { + begin: std::time::Instant::now(), + dir_name: dir_name.clone(), + capture_mode: dir_name.len() == 0, + 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, + }, + }) + } + + 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)?; + } + + self.cnt += 1; + Ok(()) + } +} diff --git a/src/qualib_refacto/annalyse.rs b/src/qualib_refacto/annalyse.rs new file mode 100644 index 0000000..b16a54e --- /dev/null +++ b/src/qualib_refacto/annalyse.rs @@ -0,0 +1,968 @@ +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::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 new file mode 100644 index 0000000..1302317 --- /dev/null +++ b/src/qualib_refacto/borders.rs @@ -0,0 +1,182 @@ +use super::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 new file mode 100644 index 0000000..910047b --- /dev/null +++ b/src/qualib_refacto/compute_image.rs @@ -0,0 +1,43 @@ +use super::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/qualib_refacto/init_border.rs b/src/qualib_refacto/init_border.rs new file mode 100644 index 0000000..fd6a422 --- /dev/null +++ b/src/qualib_refacto/init_border.rs @@ -0,0 +1,204 @@ +use crate::draw::draw_line; +use crate::point::{Color, Point}; +use crate::qualib_refacto::{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 opencv::{ + calib3d, + core::{self, Mat, Point as OcvPoint, Scalar, Size, VecN, Vector}, + imgproc::{self, canny, cvt_color, line, COLOR_BGR2GRAY}, + Result, +}; + +opencv::opencv_branch_4! { + use opencv::imgproc::LINE_AA; +} +opencv::not_opencv_branch_4! { + use opencv::core::LINE_AA; +} + +#[derive(Debug, Clone, Copy)] +pub struct InitBorder { + borders: [Point; 4], + cnt: usize, +} + +impl InitBorder { + pub fn new(beg: Point, end: Point) -> Self { + InitBorder { + borders: [ + Point { + x: beg.x, + y: beg.y, + color: end.color, + }, + Point { + x: end.x, + y: beg.y, + color: end.color, + }, + Point { + x: end.x, + y: end.y, + color: end.color, + }, + Point { + x: beg.x, + y: end.y, + color: end.color, + }, + ], + cnt: 0, + } + } +} + +impl Sequence for InitBorder { + //type Obj = Self; + + fn draw(&self, mem: &Param) -> Option> { + if self.cnt > self.borders.len() { + return None; + } + if self.cnt == self.borders.len() { + return Some(vec![]); + } + + let color = Color { + r: mem.r as u8, + g: mem.g as u8, + b: mem.b as u8, + }; + let id1 = (self.cnt + 1) % self.borders.len(); + let p0 = Point { + color, + ..self.borders[self.cnt] + }; + let p1 = Point { + color, + ..self.borders[id1] + }; + Some(draw_line( + &p0, + &p1, + mem.nb_all as usize, + mem.nb_visible as usize, + )) + } + + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { + if self.cnt < self.borders.len() { + return Ok(()); + } + + let len = self.borders.len(); + let imgs = mem.imgs[mem.seq_id].clone(); + let background = imgs[len].clone(); + let borders: Vec = imgs[..=len].into(); + + // on recupere chaqu'un des 4 bord + let mut bords_pts = vec![]; + for (id, bord) in borders.iter().enumerate() { + let lines = get_lines( + &background, + &bord, + id, + mem.canny_v1, + mem.canny_v2, + &mem.hough_param, + )?; + let bord_pt = get_extermities(&lines, id); + bords_pts.push(bord_pt); + } + + // on calcul le cadre + let border_pt = get_intersection(&bords_pts); + mem.border_pt = bord_mult(border_pt, 1.1); + + //// on dessine le cadre + //let color: VecN = VecN::new(255., 128., 0., 255.); + //let mut mixed = mix_borders(&background, borders)?; + //let b = &mem.border_pt; + //for i in 0..b.len() { + // let j = (i + 1) % mem.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)?; + + // on calcule l'homography + let size = mem.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 = mem + .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 + mem.homography = h.clone(); + mem.h_size = warped_image_size.clone(); + //let mut warped_image = Mat::default(); + //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)?; + Ok(()) + } + + fn is_capture(&self) -> bool { + true + } +} + +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/qualib_refacto/load_image.rs b/src/qualib_refacto/load_image.rs new file mode 100644 index 0000000..c7aa37f --- /dev/null +++ b/src/qualib_refacto/load_image.rs @@ -0,0 +1,33 @@ +use super::{Param, Sequence}; +use crate::point::Point; +use opencv::{core::Mat, Result}; + +#[derive(Debug, Clone, Copy)] +pub struct LoadImage { + finished: bool, +} + +impl LoadImage { + pub fn new() -> LoadImage { + LoadImage { finished: true } + } +} + +impl Sequence for LoadImage { + fn draw(&self, _mem: &Param) -> Option> { + if self.finished { + return None; + } + Some(vec![]) + } + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { + if !mem.capture_mode { + mem.load_image()?; + } + self.finished = true; + Ok(()) + } + fn is_capture(&self) -> bool { + false + } +} diff --git a/src/qualib_refacto/save_image.rs b/src/qualib_refacto/save_image.rs new file mode 100644 index 0000000..68b92da --- /dev/null +++ b/src/qualib_refacto/save_image.rs @@ -0,0 +1,31 @@ +use super::{Param, Sequence}; +use crate::point::Point; +use opencv::{core::Mat, Result}; + +#[derive(Debug, Clone, Copy)] +pub struct SaveImage { + finished: bool, +} + +impl SaveImage { + pub fn new() -> SaveImage { + SaveImage { finished: true } + } +} + +impl Sequence for SaveImage { + fn draw(&self, _mem: &Param) -> Option> { + if self.finished { + return None; + } + Some(vec![]) + } + fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box> { + mem.save_image()?; + self.finished = true; + Ok(()) + } + fn is_capture(&self) -> bool { + false + } +} diff --git a/src/qualib_refacto/wait_space.rs b/src/qualib_refacto/wait_space.rs new file mode 100644 index 0000000..655b6ae --- /dev/null +++ b/src/qualib_refacto/wait_space.rs @@ -0,0 +1,29 @@ +use super::{Param, Sequence}; +use crate::point::Point; +use opencv::{core::Mat, Result}; + +#[derive(Debug, Clone, Copy)] +pub struct WaitSpace {} + +impl WaitSpace { + pub fn new() -> Self { + Self {} + } +} + +impl Sequence for WaitSpace { + //type Obj = Self; + + fn draw(&self, mem: &Param) -> Option> { + if mem.key == 32 { + return None; + } + Some(vec![]) + } + fn compute_sequence(&mut self, _mem: &mut Param) -> Result<(), Box> { + Ok(()) + } + fn is_capture(&self) -> bool { + false + } +} diff --git a/src/qualibration.rs b/src/qualibration.rs index 562a1a4..e255cbf 100644 --- a/src/qualibration.rs +++ b/src/qualibration.rs @@ -223,12 +223,12 @@ impl Qualibration { 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)?); + 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)?); + pl.extend(draw_line_dotted(&pa, &pb, nb_all, nb_visible, false)); } Sequence::PlayLineDotted | Sequence::TakeMultiple(_) @@ -266,32 +266,32 @@ impl Qualibration { } } 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 = 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)?); + 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)?); + 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)?; + pl = draw_line(&p0, &p1, nb_all, nb_visible); } Sequence::RightBorder => { - pl = draw_line(&p1, &p2, nb_all, nb_visible)?; + pl = draw_line(&p1, &p2, nb_all, nb_visible); } Sequence::DownBorder => { - pl = draw_line(&p2, &p3, nb_all, nb_visible)?; + pl = draw_line(&p2, &p3, nb_all, nb_visible); } Sequence::LeftBorder => { - pl = draw_line(&p3, &p0, nb_all, nb_visible)?; + pl = draw_line(&p3, &p0, nb_all, nb_visible); } Sequence::Vertical(n) => { let p1 = Point { @@ -304,7 +304,7 @@ impl Qualibration { y: 4095., color, }; - pl = draw_line(&p1, &p2, nb_all, nb_visible)?; + pl = draw_line(&p1, &p2, nb_all, nb_visible); } Sequence::Horizontal(n) => { let p1 = Point { @@ -317,7 +317,7 @@ impl Qualibration { y: n as f32, color, }; - pl = draw_line(&p1, &p2, nb_all, nb_visible)?; + pl = draw_line(&p1, &p2, nb_all, nb_visible); } _ => (), } @@ -504,7 +504,7 @@ impl Qualibration { //highgui::imshow("Warped Image", &warped_image)?; let histo = histogram_3d(&warped_image, self.nb_liss)?; - draw_histograme_bgr_tresh("histo bgr", &histo, &self.tresh)?; + draw_histograme_bgr_tresh("histo bgr", &histo, &self.tresh); let (t1, s1, l1) = ( self.tresh.min_0 as f64,