1628 lines
50 KiB
Rust
1628 lines
50 KiB
Rust
use std::cmp::Ordering;
|
|
use std::collections::HashSet;
|
|
use std::f64::consts::PI;
|
|
|
|
use super::Param;
|
|
use super::DEBUG;
|
|
use crate::utils::Pt;
|
|
//use opencv::prelude::MatTraitConst;
|
|
use opencv::prelude::*; //MatTraitConst;
|
|
|
|
use opencv::core::{add, subtract, Mat, Point as OcvPoint, Point3_, VecN, CV_8UC3};
|
|
use opencv::highgui::{self, create_trackbar, named_window, WINDOW_AUTOSIZE};
|
|
use opencv::imgproc::{cvt_color, line, COLOR_BGR2GRAY};
|
|
use opencv::Result;
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum Cnt {
|
|
Beg(usize),
|
|
End(usize),
|
|
}
|
|
|
|
opencv::opencv_branch_4! {
|
|
use opencv::imgproc::LINE_AA;
|
|
}
|
|
opencv::not_opencv_branch_4! {
|
|
use opencv::core::LINE_AA;
|
|
}
|
|
|
|
use super::param::Treshold;
|
|
const MAX_TRACKBAR: i32 = 255;
|
|
|
|
#[allow(dead_code)]
|
|
pub fn draw_histograme_dbg(
|
|
window_name: &str,
|
|
histo: &Vec<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(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn draw_histograme_log(window_name: &str, histo: &Vec<f64>, is_log: bool) -> Result<()> {
|
|
let v: VecN<f64, 4> = VecN::new(0., 0., 0., 255.);
|
|
let color: VecN<f64, 4> = VecN::new(210., 210., 255., 255.);
|
|
let mut img = Mat::new_rows_cols_with_default(
|
|
histo.len() as i32 * 2,
|
|
histo.len() as i32 * 2,
|
|
CV_8UC3,
|
|
v,
|
|
)?;
|
|
|
|
let mut max = f64::MIN;
|
|
for i in 0..histo.len() {
|
|
if histo[i] > max {
|
|
max = histo[i];
|
|
}
|
|
}
|
|
|
|
for i in 0..(histo.len() - 1) {
|
|
let x1 = ((i + 0) * 2) as i32;
|
|
let x2 = ((i + 1) * 2) as i32;
|
|
let (y1, y2);
|
|
if is_log {
|
|
y1 = ((1.-((histo[i + 0] as f64 + 1.).log10() / (max as f64).log10())) * 512.) as i32;
|
|
y2 = ((1.-((histo[i + 1] as f64 + 1.).log10() / (max as f64).log10())) * 512.) as i32;
|
|
} else {
|
|
y1 = ((1. - ((histo[i + 0] as f64) / (max as f64))) * 512.) as i32;
|
|
y2 = ((1. - ((histo[i + 1] as f64) / (max as f64))) * 512.) as i32;
|
|
}
|
|
let pt1 = OcvPoint::new(x1, y1);
|
|
let pt2 = OcvPoint::new(x2, y2);
|
|
line(&mut img, pt1, pt2, color, 1, LINE_AA, 0)?;
|
|
}
|
|
|
|
highgui::imshow(window_name, &img)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn draw_histograme(window_name: &str, histo: &Vec<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(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
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
|
|
#[allow(dead_code)]
|
|
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)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Line {
|
|
h_lst: HashSet<(i32, i32)>,
|
|
v_lst: Vec<Pt>,
|
|
line: Vec<Pt>,
|
|
nb_liss: i32,
|
|
}
|
|
|
|
impl Line {
|
|
pub fn new(
|
|
pt_all: &mut HashSet<(i32, i32)>,
|
|
y_ordering: bool,
|
|
beg_lowest: bool,
|
|
nb_liss: i32,
|
|
) -> Self {
|
|
let mut illand = Line {
|
|
h_lst: HashSet::new(),
|
|
v_lst: vec![],
|
|
line: vec![],
|
|
nb_liss,
|
|
};
|
|
if pt_all.len() <= 0 {
|
|
return illand;
|
|
}
|
|
//dbg!(&pt_all);
|
|
illand.populate_hlst(pt_all);
|
|
|
|
illand.define_line();
|
|
//println!("");
|
|
//illand.define_beg_end(false, beg_lowest);
|
|
//dbg!(&illand);
|
|
illand
|
|
}
|
|
|
|
//fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) {
|
|
// // on a tout les point ordonner
|
|
// // on peut les re-ordonner selon un axes interne
|
|
//}
|
|
fn define_line(&mut self) {
|
|
// la ca va etre marant.
|
|
//
|
|
let mut last_id = 0;
|
|
let to_look = [
|
|
(-1, -1),
|
|
(0, -1),
|
|
(1, -1),
|
|
(1, 0),
|
|
(1, 1),
|
|
(0, 1),
|
|
(-1, 1),
|
|
(-1, 0),
|
|
];
|
|
|
|
if self.h_lst.len() == 0 {
|
|
return;
|
|
}
|
|
|
|
// search botom-left point.
|
|
let mut first = (i32::MAX / 2, 0);
|
|
self.h_lst.iter().for_each(|(x, y)| {
|
|
if *y > first.1 || (*y == first.1 && *x < first.0) {
|
|
first = (*x, *y);
|
|
}
|
|
});
|
|
|
|
println!("\nfirst: {first:?}\n");
|
|
|
|
let mut ll = self.h_lst.clone();
|
|
let mut l = vec![first];
|
|
let (mut px, mut py) = first;
|
|
ll.remove(&first);
|
|
|
|
// adding all borders point to a line
|
|
'line: loop {
|
|
for k in 0..to_look.len() {
|
|
//let id = (k + last_id) % to_look.len();
|
|
let (i, j) = to_look[(k + last_id) % to_look.len()];
|
|
if ll.get(&(i + px, j + py)).is_some() {
|
|
l.push((i + px, j + py));
|
|
ll.remove(&(i + px, j + py));
|
|
(px, py) = (i + px, j + py);
|
|
last_id = (k + last_id + to_look.len() - 2) % to_look.len();
|
|
continue 'line;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
let (mut min_y, mut max_y) = (f64::MAX, f64::MIN);
|
|
let mut l_tmp: Vec<Pt> = l
|
|
.iter()
|
|
.map(|(x, y)| {
|
|
min_y = (*y as f64).min(min_y);
|
|
max_y = (*y as f64).max(max_y);
|
|
Pt {
|
|
x: *x as f64,
|
|
y: *y as f64,
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
// lissage des point
|
|
let raw_pt = l_tmp.clone();
|
|
//println!("[1]raw_pt:{:?}", &raw_pt);
|
|
let nb_liss = self.nb_liss * 2;
|
|
let (c1, c2) = (1., 48.);
|
|
for i in 0..nb_liss {
|
|
let mut nl = vec![];
|
|
for i in 0..l_tmp.len() {
|
|
let (id_prev, id_next) =
|
|
((i + l_tmp.len() - 1) % l_tmp.len(), (i + 1) % l_tmp.len());
|
|
let (prev, pt, next) = (l_tmp[id_prev], l_tmp[i], l_tmp[id_next]);
|
|
|
|
nl.push((prev * c1 + pt * c2 + next * c1) / (2. * c1 + c2));
|
|
}
|
|
l_tmp = nl;
|
|
}
|
|
|
|
let mut histo_ang = vec![0.; 256];
|
|
let mut length_max = 0.;
|
|
for i in 0..l_tmp.len() {
|
|
let next = l_tmp[(i + 1) % l_tmp.len()];
|
|
let diff = next - l_tmp[i];
|
|
let ang = diff.y.atan2(diff.x);
|
|
let id = ((((ang + PI) / (2. * PI)) * 255.) as usize) % 256;
|
|
let length = (diff.x * diff.x + diff.y * diff.y).sqrt();
|
|
length_max = length.max(length_max);
|
|
histo_ang[id] += 1.;
|
|
}
|
|
let _ = draw_histograme_log("histo ang", &histo_ang, false);
|
|
|
|
let mut histo_ang_diff = vec![0.; 256];
|
|
let mirange = 3;
|
|
let mut id_out = vec![0];
|
|
for i in 0..l_tmp.len() {
|
|
let next = l_tmp[(i + 1) % l_tmp.len()];
|
|
let next2 = l_tmp[(i + 2) % l_tmp.len()];
|
|
let diff1 = next - l_tmp[i];
|
|
let diff2 = next2 - next;
|
|
let ang1 = diff1.y.atan2(diff1.x);
|
|
let ang2 = diff2.y.atan2(diff2.x);
|
|
|
|
let id = (((((ang2 - ang1) + PI) / (2. * PI)) * 255.) as usize) % 256;
|
|
if !(id >= 128 - mirange && id <= 128 + mirange) {
|
|
id_out.push(i);
|
|
//println!("id:{i}");
|
|
}
|
|
histo_ang_diff[id] += 1.;
|
|
}
|
|
let _ = draw_histograme_log("histo ang diff", &histo_ang_diff, true);
|
|
|
|
//let mut histo_lenght = vec![0.; 256];
|
|
//for i in 0..l_tmp.len() {
|
|
// let next = l_tmp[(i + 1) % l_tmp.len()];
|
|
// let diff = next - l_tmp[i];
|
|
// //let ang = diff.y.atan2(diff.x);
|
|
// //let ang_id = ((ang / (2. * PI) * 256.) as usize) % 256;
|
|
// let length = (diff.x * diff.x + diff.y * diff.y).sqrt();
|
|
// let id = (length / length_max * 255.) as usize % 256;
|
|
// histo_lenght[id] += 1.;
|
|
//}
|
|
// draw histo ang
|
|
// draw histo dst
|
|
//let _ = draw_histograme_log("histo length", &histo_lenght, false);
|
|
|
|
// opposite side
|
|
// Le but c'est de trouver le point le plus proche mais pas un point voisin
|
|
// On va le dessiner pour une partie des point seulement
|
|
|
|
|
|
let mut diff = vec![];
|
|
for i in 0..id_out.len()-1 {
|
|
let d = id_out[i+1] - id_out[i];
|
|
diff.push((d, id_out[i], id_out[i+1]));
|
|
}
|
|
diff.sort_by(|a, b| {
|
|
match (a.0, b.0) {
|
|
(a, b) if a < b => Ordering::Greater,
|
|
(a, b) if a == b => Ordering::Equal,
|
|
_ => Ordering::Less,
|
|
}
|
|
});
|
|
|
|
if diff.len() < 2 {
|
|
return ;
|
|
}
|
|
let mut dio = vec![diff[0], diff[1]];
|
|
dio.sort_by(|a, b| {
|
|
match (a.1, b.1) {
|
|
(a, b) if a > b => Ordering::Greater,
|
|
(a, b) if a == b => Ordering::Equal,
|
|
_ => Ordering::Less,
|
|
}
|
|
});
|
|
|
|
|
|
//dbg!("diff:", &diff[..3]);
|
|
//dbg!(&id_out);
|
|
dbg!(&dio);
|
|
|
|
//*
|
|
|
|
////println!("[2]raw_pt:{:?}", &raw_pt);
|
|
//let left: Vec<Pt> = raw_pt[dio[0].1..=dio[0].2].into();
|
|
//let mut right = vec![];
|
|
//for i in (dio[1].1..=dio[1].2).rev() {
|
|
////for i in (id_out[1]..=id_out[2]).rev() {
|
|
// right.push(raw_pt[i]);
|
|
//}
|
|
//println!("[2]raw_pt:{:?}", &raw_pt);
|
|
let left: Vec<Pt> = l_tmp[dio[0].1..=dio[0].2].into();
|
|
let mut right = vec![];
|
|
for i in (dio[1].1..=dio[1].2).rev() {
|
|
//for i in (id_out[1]..=id_out[2]).rev() {
|
|
right.push(l_tmp[i]);
|
|
}
|
|
|
|
let mut couple = vec!();
|
|
let nb_jump = 10;
|
|
let mut id_last = 0;
|
|
let range_id = 20;
|
|
for i in 0..left.len() {
|
|
//if i % nb_jump == 0 {
|
|
let mut dist = vec![];
|
|
let from = (id_last as i32 - range_id as i32).max(0) as usize;
|
|
let to = (id_last + range_id).min(right.len()-1);
|
|
for id in from..=to {
|
|
let df = right[id] - left[i];
|
|
let dst = (df.cross(&df)).sqrt();
|
|
dist.push((id, dst));
|
|
}
|
|
dist.sort_by(|a, b| {
|
|
if a.1 > b.1 {
|
|
Ordering::Greater
|
|
} else if a.1 == b.1 {
|
|
Ordering::Equal
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
});
|
|
//println!("closest: {:?}", &dist[0]);
|
|
id_last = dist[0].0; //dbg!(dist);
|
|
couple.push((left[i], right[id_last]));
|
|
//}
|
|
}
|
|
|
|
let l3: Vec<Pt> = couple.iter().map(|(l, r)| (*l+*r)/2.).collect();
|
|
//*/
|
|
|
|
//
|
|
|
|
//let space = 100;
|
|
//let dst = 30;
|
|
//for i in 0..l_tmp.len() {
|
|
// if i % space == 0 {
|
|
// //
|
|
// }
|
|
//}
|
|
|
|
self.line = l3;
|
|
println!("rest:{}\t", ll.len());
|
|
}
|
|
|
|
/// the function also set: self.v_lst
|
|
fn populate_hlst(&mut self, pt_all: &mut HashSet<(i32, i32)>) {
|
|
let first = pt_all.iter().next().unwrap();
|
|
let mut added = HashSet::from([(first.0, first.1)]);
|
|
let to_look = [
|
|
(-1, 0),
|
|
(0, -1),
|
|
(1, 0),
|
|
(0, 1),
|
|
(-1, -1),
|
|
(-1, 1),
|
|
(1, 1),
|
|
(1, -1),
|
|
];
|
|
self.h_lst.extend(&added);
|
|
while added.len() > 0 {
|
|
let mut tmp = HashSet::new();
|
|
for (x, y) in added {
|
|
for (i, j) in to_look {
|
|
if let Some(pt_new) = pt_all.get(&(x + i, y + j)) {
|
|
tmp.insert(*pt_new);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.h_lst.extend(tmp.clone());
|
|
tmp.iter().for_each(|pt| {
|
|
pt_all.remove(pt);
|
|
});
|
|
added = tmp;
|
|
}
|
|
|
|
let mut tmp = HashSet::new();
|
|
self.h_lst.iter().for_each(|(x, y)| {
|
|
for (i, j) in to_look[..4].iter() {
|
|
if self.h_lst.get(&(x + i, y + j)).is_none() {
|
|
tmp.insert((*x, *y));
|
|
}
|
|
}
|
|
});
|
|
self.h_lst = tmp;
|
|
|
|
println!("\t\t\t\tfinal selecteted:{}", self.h_lst.len());
|
|
|
|
//dbg!(a);
|
|
//println!(I);
|
|
}
|
|
}
|
|
|
|
pub fn get_lines(
|
|
m: &Mat,
|
|
y_ordering: bool,
|
|
beg_lowest: bool,
|
|
nb_liss: i32,
|
|
) -> Result<Vec<Vec<Pt>>> {
|
|
// donc on va refaire un algo de detection de segement
|
|
// * on va chercher des illot:
|
|
// * chaque illo on lui definie des limit (min, max) sur x et y
|
|
// * on calcule le centre
|
|
let mut pt_inside = HashSet::new();
|
|
let (cols, rows) = (m.cols(), m.rows());
|
|
for j in 0..rows {
|
|
for i in 0..cols {
|
|
let v: &Point3_<u8> = m.at_2d(j, i)?;
|
|
if v.x != 0 && v.y != 0 && v.z != 0 {
|
|
pt_inside.insert((i, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("\tFlifou: {}\t\tpt_inserted:{}", line!(), pt_inside.len());
|
|
|
|
let mut lines = vec![];
|
|
while pt_inside.len() > 0 {
|
|
lines.push(Line::new(&mut pt_inside, y_ordering, beg_lowest, nb_liss));
|
|
}
|
|
|
|
println!("\tFlifou: {}\t\tnb_line:{}", line!(), lines.len());
|
|
|
|
// pour chaque ligne
|
|
Ok(lines.iter().map(|line| line.line.clone()).collect())
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Seg {
|
|
h_lst: HashSet<(i32, i32)>,
|
|
v_lst: Vec<Pt>,
|
|
min: Pt,
|
|
max: Pt,
|
|
center: Pt,
|
|
beg: Pt,
|
|
end: Pt,
|
|
}
|
|
|
|
impl Seg {
|
|
pub fn new(pt_all: &mut HashSet<(i32, i32)>, y_ordering: bool, beg_lowest: bool) -> Self {
|
|
let mut illand = Seg {
|
|
min: Pt::from((f64::MAX, f64::MAX)),
|
|
max: Pt::from((f64::MIN, f64::MIN)),
|
|
center: Pt::from((0., 0.)),
|
|
beg: Pt::from((0., 0.)),
|
|
end: Pt::from((0., 0.)),
|
|
h_lst: HashSet::new(),
|
|
v_lst: vec![],
|
|
};
|
|
if pt_all.len() <= 0 {
|
|
return illand;
|
|
}
|
|
//dbg!(&pt_all);
|
|
illand.populate_hlst(pt_all);
|
|
//println!("");
|
|
illand.define_beg_end(false, beg_lowest);
|
|
//dbg!(&illand);
|
|
illand
|
|
}
|
|
|
|
//fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) {
|
|
// // on a tout les point ordonner
|
|
// // on peut les re-ordonner selon un axes interne
|
|
//}
|
|
|
|
/// the function also set: self.v_lst
|
|
fn populate_hlst(&mut self, pt_all: &mut HashSet<(i32, i32)>) {
|
|
let first = pt_all.iter().next().unwrap();
|
|
let mut added = HashSet::from([(first.0, first.1)]);
|
|
let to_look = [
|
|
(-1, 0),
|
|
(0, -1),
|
|
(1, 0),
|
|
(0, 1),
|
|
(-1, -1),
|
|
(-1, 1),
|
|
(1, 1),
|
|
(1, -1),
|
|
];
|
|
self.h_lst.extend(&added);
|
|
while added.len() > 0 {
|
|
let mut tmp = HashSet::new();
|
|
for (x, y) in added {
|
|
for (i, j) in to_look {
|
|
if let Some(pt_new) = pt_all.get(&(x + i, y + j)) {
|
|
tmp.insert(*pt_new);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.h_lst.extend(tmp.clone());
|
|
tmp.iter().for_each(|pt| {
|
|
pt_all.remove(pt);
|
|
});
|
|
added = tmp;
|
|
}
|
|
|
|
// calcule min, max, center
|
|
self.h_lst.iter().for_each(|(x, y)| {
|
|
self.min.x = (*x as f64).min(self.min.x);
|
|
self.max.x = (*x as f64).max(self.max.x);
|
|
self.min.y = (*y as f64).min(self.min.y);
|
|
self.max.y = (*y as f64).max(self.max.y);
|
|
|
|
self.center.x += *x as f64;
|
|
self.center.y += *y as f64;
|
|
});
|
|
|
|
self.center /= self.h_lst.len() as f64;
|
|
|
|
self.v_lst = self
|
|
.h_lst
|
|
.iter()
|
|
.map(|(x, y)| Pt {
|
|
x: (*x as f64),
|
|
y: (*y as f64),
|
|
})
|
|
.collect();
|
|
}
|
|
|
|
/// y_ordering: means we ordering vertically. Else we ordering horizontay
|
|
/// beg_lowest: means first value are the lowset. Else first value are the biggest
|
|
fn define_beg_end(&mut self, y_ordering: bool, beg_lowest: bool) {
|
|
let pts: Vec<Pt> = self
|
|
.h_lst
|
|
.iter()
|
|
.map(|(x, y)| {
|
|
Pt {
|
|
x: (*x as f64),
|
|
y: (*y as f64),
|
|
} - self.center
|
|
})
|
|
.collect();
|
|
|
|
let y_cmp = |pt_a: &Pt, pt_b: &Pt| {
|
|
if (pt_a.y > pt_b.y) == beg_lowest {
|
|
Ordering::Greater
|
|
} else if pt_a.y == pt_b.y {
|
|
if pt_a.x.abs() > pt_b.x.abs() {
|
|
Ordering::Greater
|
|
} else if pt_a.x.abs() == pt_b.x.abs() {
|
|
Ordering::Equal
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
};
|
|
let x_cmp = |pt_a: &Pt, pt_b: &Pt| {
|
|
if (pt_a.x > pt_b.x) == beg_lowest {
|
|
Ordering::Greater
|
|
} else if pt_a.x == pt_b.x {
|
|
if pt_a.y.abs() > pt_b.y.abs() {
|
|
Ordering::Greater
|
|
} else if pt_a.y.abs() == pt_b.y.abs() {
|
|
Ordering::Equal
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
};
|
|
|
|
//println!("\n\nserching axes:");
|
|
// finding by dichotomie
|
|
// finding best central axes.
|
|
let mut begin = 0.;
|
|
let mut len = 8.;
|
|
let nb_deg = 8;
|
|
let nb_iter = 20;
|
|
let mut id_min = 0;
|
|
let mut v_max_x = vec![];
|
|
for _iter in 0..nb_iter {
|
|
//println!("iter:{_iter}");
|
|
v_max_x = vec![];
|
|
for i in 0..(nb_deg + 2) {
|
|
let rad = (i as f64 - 1.) / len * PI + begin;
|
|
let axes_x = Pt::from((rad.cos(), rad.sin()));
|
|
let axes_y = Pt::from((-rad.sin(), rad.cos()));
|
|
let new_base: Vec<Pt> = pts
|
|
.iter()
|
|
.map(|pt| Pt::from((pt.cross(&axes_x), pt.cross(&axes_y))))
|
|
.collect();
|
|
|
|
let mut max_x = 0.;
|
|
new_base.iter().for_each(|pt| {
|
|
if pt.x.abs() > max_x {
|
|
max_x = pt.x.abs();
|
|
}
|
|
});
|
|
//println!("\t[i:{i}]rad:{rad:32.26}\tval: {max_x}");
|
|
v_max_x.push((rad, max_x));
|
|
}
|
|
|
|
id_min = 0;
|
|
for i in 1..=nb_deg {
|
|
let (p, n, a) = (v_max_x[i - 1].1, v_max_x[i].1, v_max_x[i + 1].1);
|
|
let cnd = (y_ordering && n <= p && n <= a) || (!y_ordering && n >= p && n >= a);
|
|
id_min = if cnd { i - 1 } else { id_min };
|
|
}
|
|
|
|
begin = v_max_x[id_min].0;
|
|
len *= 4.;
|
|
}
|
|
|
|
let rad = v_max_x[id_min + 1].0;
|
|
let axes_x = Pt::from((rad.cos(), rad.sin()));
|
|
let axes_y = Pt::from((-rad.sin(), rad.cos()));
|
|
let mut npts: Vec<Pt> = pts
|
|
.iter()
|
|
.map(|pt| Pt::from((pt.cross(&axes_x), pt.cross(&axes_y))))
|
|
.collect();
|
|
|
|
if y_ordering {
|
|
npts.sort_by(y_cmp);
|
|
} else {
|
|
npts.sort_by(x_cmp);
|
|
}
|
|
|
|
let mut beg = Pt::from((0., 0.));
|
|
for pt in npts[..(npts.len() / 2)].iter() {
|
|
beg += *pt;
|
|
}
|
|
beg /= (npts.len() / 2) as f64;
|
|
|
|
let mut end = Pt::from((0., 0.));
|
|
for pt in npts[(npts.len() / 2)..].iter() {
|
|
end += *pt;
|
|
}
|
|
end /= (npts.len() - (npts.len() / 2)) as f64;
|
|
|
|
let beg_tmp = axes_x * beg.x + axes_y * beg.y + self.center;
|
|
let end_tmp = axes_x * end.x + axes_y * end.y + self.center;
|
|
|
|
self.beg = (beg_tmp - end_tmp) * 1.5 + end_tmp;
|
|
self.end = (end_tmp - beg_tmp) * 1.5 + beg_tmp;
|
|
}
|
|
}
|
|
|
|
pub fn get_segment(
|
|
m: &Mat,
|
|
y_ordering: bool,
|
|
beg_lowest: bool,
|
|
) -> Result<Vec<((f64, f64), (f64, f64))>> {
|
|
// donc on va refaire un algo de detection de segement
|
|
// * on va chercher des illot:
|
|
// * chaque illo on lui definie des limit (min, max) sur x et y
|
|
// * on calcule le centre
|
|
let mut pt_inside = HashSet::new();
|
|
let (cols, rows) = (m.cols(), m.rows());
|
|
for j in 0..rows {
|
|
for i in 0..cols {
|
|
let v: &Point3_<u8> = m.at_2d(j, i)?;
|
|
if v.x != 0 && v.y != 0 && v.z != 0 {
|
|
pt_inside.insert((i, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut illands = vec![];
|
|
while pt_inside.len() > 0 {
|
|
illands.push(Seg::new(&mut pt_inside, y_ordering, beg_lowest));
|
|
}
|
|
//todo!();
|
|
if y_ordering {
|
|
illands.sort_by(|a, b| {
|
|
if beg_lowest && a.center.y > b.center.y || !beg_lowest && a.center.y < b.center.y {
|
|
Ordering::Greater
|
|
} else if a.center.y == b.center.y {
|
|
Ordering::Equal
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
});
|
|
} else {
|
|
illands.sort_by(|a, b| {
|
|
if beg_lowest && a.center.x > b.center.x || !beg_lowest && a.center.x < b.center.x {
|
|
Ordering::Greater
|
|
} else if a.center.x == b.center.x {
|
|
Ordering::Equal
|
|
} else {
|
|
Ordering::Less
|
|
}
|
|
});
|
|
}
|
|
let segments = illands
|
|
.iter()
|
|
.map(|illand| ((illand.beg.x, illand.beg.y), (illand.end.x, illand.end.y)))
|
|
.collect();
|
|
Ok(segments)
|
|
}
|
|
// On cherche des segment regourper par ilot de point. chaque illot a une plage de valeur en y qui
|
|
// lui est propre, aucun autre ilot aura des point dans une plage de valeurs d'un autre illot.
|
|
pub fn get_vertical_segment(m: &Mat) -> Result<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_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 = 8;
|
|
let (mut _err_min, mut rad_min, mut x_min) = (f64::MAX, 0., f64::MAX);
|
|
let mut iland_min = vec![];
|
|
for deg in 0..max_deg {
|
|
let rad = (deg as f64) / (max_deg as f64) * PI * 2.;
|
|
let y_axis = Pt {
|
|
x: rad.sin(),
|
|
y: rad.cos(),
|
|
};
|
|
let x_axis = Pt {
|
|
x: -y_axis.y,
|
|
y: y_axis.x,
|
|
};
|
|
let mut tmp_iland = vec![];
|
|
let mut x_abs_max = f64::MIN;
|
|
for pt in iland {
|
|
let mut p = *pt - center;
|
|
p = Pt {
|
|
x: p.cross(&x_axis),
|
|
y: p.cross(&y_axis),
|
|
};
|
|
tmp_iland.push(p);
|
|
if x_abs_max < p.x.abs() {
|
|
x_abs_max = p.x.abs();
|
|
}
|
|
}
|
|
if x_abs_max < x_min {
|
|
x_min = x_abs_max;
|
|
rad_min = rad;
|
|
iland_min = tmp_iland;
|
|
}
|
|
}
|
|
iland_min.sort_by(|pta, ptb| {
|
|
if pta.y < ptb.y {
|
|
std::cmp::Ordering::Greater
|
|
} else if pta.y == ptb.y {
|
|
if pta.x.abs() < ptb.x.abs() {
|
|
std::cmp::Ordering::Greater
|
|
} else if pta.x.abs() == ptb.x.abs() {
|
|
std::cmp::Ordering::Equal
|
|
} else {
|
|
std::cmp::Ordering::Less
|
|
}
|
|
} else {
|
|
std::cmp::Ordering::Less
|
|
}
|
|
});
|
|
let id1 = iland_min.len() / 2;
|
|
let id2 = iland_min.len() - id1;
|
|
let mean_up = Pt::mean(&iland_min[..id1]);
|
|
let mean_down = Pt::mean(&iland_min[id2..]);
|
|
//let mean_up = iland_min[0];
|
|
//let mean_down = iland_min.last().unwrap();
|
|
|
|
let y_axis = Pt {
|
|
x: rad_min.sin(),
|
|
y: rad_min.cos(),
|
|
};
|
|
let x_axis = Pt {
|
|
x: -y_axis.y,
|
|
y: y_axis.x,
|
|
};
|
|
let pt_up = center + (y_axis * mean_up.y) + (x_axis * mean_up.x);
|
|
let pt_down = center + (y_axis * mean_down.y) + (x_axis * mean_down.x);
|
|
//segments.push(((pt_down.x as f32, pt_down.y as f32), (pt_up.x as f32, pt_up.y as f32)));
|
|
let pt_up_2 = pt_down + (pt_up - pt_down) * 1.5;
|
|
let pt_down_2 = pt_up + (pt_down - pt_up) * 1.5;
|
|
segments.push((
|
|
(
|
|
(pt_down_2.x as f32, pt_down_2.y as f32),
|
|
(pt_up_2.x as f32, pt_up_2.y as f32),
|
|
),
|
|
x_min as f32,
|
|
));
|
|
}
|
|
|
|
Ok(segments)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn average_pt_i32(vals: &[(i32, i32)]) -> (f32, f32) {
|
|
let (mut mean_x, mut mean_y) = (0., 0.);
|
|
let len = vals.len() as f32;
|
|
|
|
for (x, y) in vals {
|
|
mean_x += *x as f32;
|
|
mean_y += *y as f32;
|
|
}
|
|
(mean_x / len, mean_y / len)
|
|
}
|
|
|
|
fn get_id_groups(limits: &Vec<(usize, usize)>, id: usize) -> Option<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...
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
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)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
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]),
|
|
)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn trackbar_init_param(mem: &mut Param, winname: &str) -> Result<()> {
|
|
named_window(winname, WINDOW_AUTOSIZE)?;
|
|
highgui::move_window(winname, 20, 20)?;
|
|
let v: VecN<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("nb_wait", winname, Some(&mut mem.nb_wait), 40, None)?;
|
|
create_trackbar("r", winname, Some(&mut mem.r), MAX_TRACKBAR, None)?;
|
|
create_trackbar("g", winname, Some(&mut mem.g), MAX_TRACKBAR, None)?;
|
|
create_trackbar("b", winname, Some(&mut mem.b), MAX_TRACKBAR, None)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn trackbar_line_segment(mem: &mut Param, winname: &str) -> Result<()> {
|
|
//highgui
|
|
let winname = format!("{}: {}", winname, 0); //"bord selected: 0";
|
|
named_window(winname.as_str(), WINDOW_AUTOSIZE)?;
|
|
highgui::move_window(winname.as_str(), 20, 520)?;
|
|
//highgui::move_window(winname, 20, 20)?;
|
|
let v: VecN<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(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn line_pos(mem: &mut Param, winname: &str) -> Result<()> {
|
|
named_window(winname, WINDOW_AUTOSIZE)?;
|
|
highgui::move_window(winname, 20, 20)?;
|
|
let v: VecN<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(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(())
|
|
}
|