feat: dering refacto Qualibration and Sequence

This commit is contained in:
Lapin Raving 2023-09-20 00:48:51 +02:00
parent 910e340ec8
commit 392455e903
11 changed files with 1876 additions and 72 deletions

View File

@ -7,12 +7,12 @@ const X_CENTER: f32 = 2047.0;
const Y_CENTER: f32 = 2047.0; const Y_CENTER: f32 = 2047.0;
pub fn draw_line_dotted( pub fn draw_line_dotted(
p0: &Point,
p1: &Point, p1: &Point,
p2: &Point,
nb_all: usize, nb_all: usize,
nb_visible: usize, nb_visible: usize,
first_on: bool, first_on: bool,
) -> Result<Vec<Point>, Box<dyn std::error::Error>> { ) -> Vec<Point> {
let mut pl = vec![]; let mut pl = vec![];
let black = Color { r: 0, g: 0, b: 0 }; let black = Color { r: 0, g: 0, b: 0 };
@ -21,61 +21,56 @@ pub fn draw_line_dotted(
for _ in 0..nb_all { for _ in 0..nb_all {
pl.push(Point { pl.push(Point {
color: black, color: black,
..*p1 ..*p0
}); });
} }
for i in 0..nb_visible { for i in 0..nb_visible {
pl.push(Point { pl.push(Point {
color: if i % 2 == cmp { p2.color } else { black }, color: if i % 2 == cmp { p1.color } else { black },
..*p2 ..*p1
}); });
} }
if nb_visible > nb_all { if nb_visible > nb_all {
return Ok(pl); return pl;
} }
for _ in 0..(nb_all - nb_visible) { for _ in 0..(nb_all - nb_visible) {
pl.push(Point { pl.push(Point {
color: black, color: black,
..*p2 ..*p1
}); });
} }
Ok(pl) pl
} }
pub fn draw_line( pub fn draw_line(p0: &Point, p1: &Point, nb_all: usize, nb_visible: usize) -> Vec<Point> {
p1: &Point,
p2: &Point,
nb_all: usize,
nb_visible: usize,
) -> Result<Vec<Point>, Box<dyn std::error::Error>> {
let mut pl = vec![]; let mut pl = vec![];
let black = Color { r: 0, g: 0, b: 0 }; let black = Color { r: 0, g: 0, b: 0 };
for _ in 0..nb_all { for _ in 0..nb_all {
pl.push(Point { pl.push(Point {
color: black, color: black,
..*p1 ..*p0
}); });
} }
for _ in 0..nb_visible { for _ in 0..nb_visible {
pl.push(*p2); pl.push(*p1);
} }
if nb_visible > nb_all { if nb_visible > nb_all {
return Ok(pl); return pl;
} }
for _ in 0..(nb_all - nb_visible) { for _ in 0..(nb_all - nb_visible) {
pl.push(Point { pl.push(Point {
color: black, color: black,
..*p2 ..*p1
}); });
} }
Ok(pl) pl
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn draw(_time: f64) -> Result<Vec<Point>, Box<dyn std::error::Error>> { pub fn draw(_time: f64) -> Vec<Point> {
let mut v: Vec<Point> = vec![]; let mut v: Vec<Point> = vec![];
for i in 0..128 { for i in 0..128 {
let a = i as f32 / 128.0 * std::f32::consts::PI * 2.0; let a = i as f32 / 128.0 * std::f32::consts::PI * 2.0;
@ -89,5 +84,5 @@ pub fn draw(_time: f64) -> Result<Vec<Point>, Box<dyn std::error::Error>> {
}, },
}); });
} }
Ok(v) v
} }

View File

@ -1,3 +1,7 @@
//mod qualib_refacto;
//use qualib_refacto;
//use qualib_refacto::{self, Qualibration as Qualib};
/// ///
/// Configure udev: /// Configure udev:
/// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md /// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md
@ -8,10 +12,10 @@ mod framerate;
mod logs; mod logs;
mod point; mod point;
mod qualibration; mod qualib_refacto;
mod utils; mod utils;
use qualibration::{annalyse::adding_trackbar, Qualibration, Sequence}; use qualib_refacto::{annalyse::adding_trackbar, Qualibration, Sequence};
use conf::Conf; use conf::Conf;
use log::{/*debug, warn, */ error, info}; use log::{/*debug, warn, */ error, info};
@ -78,7 +82,7 @@ fn run_all() -> Result<(), Box<dyn std::error::Error>> {
info!("{:?}", config); info!("{:?}", config);
let mut qualibration = Qualibration::new()?; 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(format!("/kpps/{}", conf2.laser_id), 65535)?;
let _a: () = con.set( let _a: () = con.set(
@ -90,41 +94,43 @@ fn run_all() -> Result<(), Box<dyn std::error::Error>> {
//let _t = framerate_handler.handle_time()?; //let _t = framerate_handler.handle_time()?;
///////////////// /////////////////
let key = highgui::wait_key(1)?; let key = highgui::wait_key(1)?;
//if key != -1 { qualibration.param.key = key;
qualibration.key = key;
//}
if key == 27 { if key == 27 {
// esc in my case
break; break;
} }
//qualibration.id = next_id; let v = qualibration.draw_sequence();
let v: Vec<(f32, f32, u32)> = qualibration if v.is_some() {
.draw_sequence()? if qualibration.param.capture_mode {
.iter() let pl: Vec<(f32, f32, u32)> = v
.map(|pt| (pt.x, pt.y, u32::from(pt.color))) .unwrap()
.collect(); .iter()
// println!("{:?}", v); .map(|pt| (pt.x, pt.y, u32::from(pt.color)))
let _ = con.set( .collect();
format!("/pl/{}/{}", config.client_id, config.laser_id),
format!("{:?}", v),
)?;
qualibration.run_step()?; let _ = con.set(
format!("/pl/{}/{}", config.client_id, config.laser_id),
format!("{:?}", pl),
)?;
}
let q_id = qualibration.id.clone(); qualibration.run_step()?;
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.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( let _ = con.set(

313
src/qualib_refacto.rs Normal file
View File

@ -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<Self> {
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<Vec<Point>>;
fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box<dyn std::error::Error>>;
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<Vec<Mat>>,
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_<i32>,
line_pos: Vec<i32>,
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<dyn std::error::Error>> {
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<Box<dyn Sequence>>,
cam: VideoCapture,
cnt: usize,
pub param: Param,
}
impl Qualibration {
pub fn new() -> Result<Self> {
//let v: Vec<Box<dyn Sequence>> = 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<Box<dyn Sequence>> = 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<Vec<Point>> {
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<dyn std::error::Error>> {
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(())
}
}

View File

@ -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<f64>,
(from, to): (usize, usize),
) -> Result<()> {
let v: VecN<f64, 4> = VecN::new(0., 0., 0., 255.);
let c1: VecN<f64, 4> = VecN::new(128., 128., 128., 255.);
let c2: VecN<f64, 4> = VecN::new(255., 255., 255., 255.);
//let color: VecN<f64, 4> = 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<f64>) -> Result<()> {
let v: VecN<f64, 4> = VecN::new(0., 0., 0., 255.);
let color: VecN<f64, 4> = 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<Vec<f64>>) -> Result<()> {
let v: VecN<f64, 4> = VecN::new(0., 0., 0., 255.);
let b: VecN<f64, 4> = VecN::new(255., 0., 0., 255.);
let g: VecN<f64, 4> = VecN::new(0., 255., 0., 255.);
let r: VecN<f64, 4> = 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<Vec<f64>>,
tresh: &Treshold,
) -> Result<()> {
let v: VecN<f64, 4> = VecN::new(0., 0., 0., 255.);
let b: VecN<f64, 4> = VecN::new(255., 0., 0., 255.);
let g: VecN<f64, 4> = VecN::new(0., 255., 0., 255.);
let r: VecN<f64, 4> = 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<f64> = [tresh.max_0 as f64, tresh.max_1 as f64, tresh.max_2 as f64].into();
let min: Vec<f64> = [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<bool> {
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<Vec<(((f32, f32), (f32, f32)), f32)>> {
// 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_<u8> = 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<Vec<Pt>> = 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<Vec<((f32, f32), (f32, f32))>> {
// 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_<u8> = 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<Vec<Pt>> = 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<usize> {
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<Vec<Vec<(i32, i32)>>> {
// 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_<u8> = 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<Mat> {
/*
* Il faudrait pouvoir changer les matrice de type pour avoir des valeur plus grande
* */
let mut frames_big: Vec<Mat> = 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_<i16> = 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<Mat> {
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<f64, 4> = 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<Vec<Vec<f64>>> {
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_<u8> = 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<Vec<f64>> {
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<f64> = 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<f64>) -> ((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<f64, 4> = 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<f64, 4> = 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<f64, 4> = 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(())
}

View File

@ -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<Mat>) -> Result<Mat> {
let (row, col) = (background.rows(), background.cols());
//let mask = Mat::default();
let v: VecN<f64, 4> = 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<f64, 4> = 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> = 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> = 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<VecN<i32, 4>>, 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<Vector<VecN<i32, 4>>> {
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)
}

View File

@ -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_<i32>) -> Result<Mat> {
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<Mat> {
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_<i32>,
tresh: &Treshold,
) -> Result<Mat> {
let warped = image_warp(img, homography, h_size)?;
let treshed = image_treshold(&warped, tresh)?;
Ok(treshed)
}

View File

@ -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<Vec<Point>> {
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<dyn std::error::Error>> {
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<Mat> = 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<f64, 4> = 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<OcvPoint> = 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<OcvPoint> = 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<Vector<VecN<i32, 4>>> {
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)
}

View File

@ -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<Vec<Point>> {
if self.finished {
return None;
}
Some(vec![])
}
fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box<dyn std::error::Error>> {
if !mem.capture_mode {
mem.load_image()?;
}
self.finished = true;
Ok(())
}
fn is_capture(&self) -> bool {
false
}
}

View File

@ -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<Vec<Point>> {
if self.finished {
return None;
}
Some(vec![])
}
fn compute_sequence(&mut self, mem: &mut Param) -> Result<(), Box<dyn std::error::Error>> {
mem.save_image()?;
self.finished = true;
Ok(())
}
fn is_capture(&self) -> bool {
false
}
}

View File

@ -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<Vec<Point>> {
if mem.key == 32 {
return None;
}
Some(vec![])
}
fn compute_sequence(&mut self, _mem: &mut Param) -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn is_capture(&self) -> bool {
false
}
}

View File

@ -223,12 +223,12 @@ impl Qualibration {
Sequence::IdCode1 => { Sequence::IdCode1 => {
// on va en haut a gauche // on va en haut a gauche
// on va a droite en clognotant sur les nombre pair // 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 => { Sequence::IdCode2 => {
// on va en haut a gauche // on va en haut a gauche
// on va a droite en clognotant sur les nombre impair // 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::PlayLineDotted
| Sequence::TakeMultiple(_) | Sequence::TakeMultiple(_)
@ -266,32 +266,32 @@ impl Qualibration {
} }
} }
Sequence::WaitSpace => { Sequence::WaitSpace => {
pl = draw_line(&p0, &p1, 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(&p1, &p2, nb_all, nb_visible));
pl.extend(draw_line(&p3, &p0, 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(&p2, &p3, nb_all, nb_visible));
pl.extend(draw_line_dotted(&p4, &p5, 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(&p6, &p7, nb_all, nb_visible, true));
pl.extend(draw_line_dotted(&p8, &p9, nb_all, nb_visible, true)?); pl.extend(draw_line_dotted(&p8, &p9, nb_all, nb_visible, true));
} }
Sequence::SelectNbAll(n) => { Sequence::SelectNbAll(n) => {
pl = draw_line(&p0, &p1, 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(&p1, &p2, n as usize, n as usize));
pl.extend(draw_line(&p3, &p0, 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.extend(draw_line(&p2, &p3, n as usize, n as usize));
} }
Sequence::UpBorder => { Sequence::UpBorder => {
pl = draw_line(&p0, &p1, nb_all, nb_visible)?; pl = draw_line(&p0, &p1, nb_all, nb_visible);
} }
Sequence::RightBorder => { Sequence::RightBorder => {
pl = draw_line(&p1, &p2, nb_all, nb_visible)?; pl = draw_line(&p1, &p2, nb_all, nb_visible);
} }
Sequence::DownBorder => { Sequence::DownBorder => {
pl = draw_line(&p2, &p3, nb_all, nb_visible)?; pl = draw_line(&p2, &p3, nb_all, nb_visible);
} }
Sequence::LeftBorder => { Sequence::LeftBorder => {
pl = draw_line(&p3, &p0, nb_all, nb_visible)?; pl = draw_line(&p3, &p0, nb_all, nb_visible);
} }
Sequence::Vertical(n) => { Sequence::Vertical(n) => {
let p1 = Point { let p1 = Point {
@ -304,7 +304,7 @@ impl Qualibration {
y: 4095., y: 4095.,
color, color,
}; };
pl = draw_line(&p1, &p2, nb_all, nb_visible)?; pl = draw_line(&p1, &p2, nb_all, nb_visible);
} }
Sequence::Horizontal(n) => { Sequence::Horizontal(n) => {
let p1 = Point { let p1 = Point {
@ -317,7 +317,7 @@ impl Qualibration {
y: n as f32, y: n as f32,
color, 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)?; //highgui::imshow("Warped Image", &warped_image)?;
let histo = histogram_3d(&warped_image, self.nb_liss)?; 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) = ( let (t1, s1, l1) = (
self.tresh.min_0 as f64, self.tresh.min_0 as f64,