use crate::{
Exceptions, Point,
common::{BitMatrix, Result},
point,
};
const INIT_SIZE: i32 = 10;
const CORR: i32 = 1;
pub struct WhiteRectangleDetector<'a> {
image: &'a BitMatrix,
height: i32,
width: i32,
leftInit: i32,
rightInit: i32,
downInit: i32,
upInit: i32,
}
impl<'a> WhiteRectangleDetector<'_> {
pub const fn new_from_image(image: &'a BitMatrix) -> Result<WhiteRectangleDetector<'a>> {
WhiteRectangleDetector::new(
image,
INIT_SIZE,
image.getWidth() as i32 / 2,
image.getHeight() as i32 / 2,
)
}
pub const fn new(
image: &'a BitMatrix,
initSize: i32,
x: i32,
y: i32,
) -> Result<WhiteRectangleDetector<'a>> {
let halfsize = initSize / 2;
let leftInit = x - halfsize;
let rightInit = x + halfsize;
let upInit = y - halfsize;
let downInit = y + halfsize;
if upInit < 0
|| leftInit < 0
|| downInit >= image.getHeight() as i32
|| rightInit >= image.getWidth() as i32
{
return Err(Exceptions::NOT_FOUND);
}
Ok(WhiteRectangleDetector {
image,
height: image.getHeight() as i32,
width: image.getWidth() as i32,
leftInit,
rightInit,
downInit,
upInit,
})
}
pub fn detect(&self) -> Result<[Point; 4]> {
let mut left: i32 = self.leftInit;
let mut right: i32 = self.rightInit;
let mut up: i32 = self.upInit;
let mut down: i32 = self.downInit;
let mut size_exceeded = false;
let mut a_black_pointound_on_border = true;
let mut at_least_one_black_pointound_on_right = false;
let mut at_least_one_black_pointound_on_bottom = false;
let mut at_least_one_black_pointound_on_left = false;
let mut at_least_one_black_pointound_on_top = false;
while a_black_pointound_on_border {
a_black_pointound_on_border = false;
let mut right_border_not_white = true;
while (right_border_not_white || !at_least_one_black_pointound_on_right)
&& right < self.width
{
right_border_not_white = self.contains_black_point(up, down, right, false);
if right_border_not_white {
right += 1;
a_black_pointound_on_border = true;
at_least_one_black_pointound_on_right = true;
} else if !at_least_one_black_pointound_on_right {
right += 1;
}
}
if right >= self.width {
size_exceeded = true;
break;
}
let mut bottom_border_not_white = true;
while (bottom_border_not_white || !at_least_one_black_pointound_on_bottom)
&& down < self.height
{
bottom_border_not_white = self.contains_black_point(left, right, down, true);
if bottom_border_not_white {
down += 1;
a_black_pointound_on_border = true;
at_least_one_black_pointound_on_bottom = true;
} else if !at_least_one_black_pointound_on_bottom {
down += 1;
}
}
if down >= self.height {
size_exceeded = true;
break;
}
let mut left_border_not_white = true;
while (left_border_not_white || !at_least_one_black_pointound_on_left) && left >= 0 {
left_border_not_white = self.contains_black_point(up, down, left, false);
if left_border_not_white {
left -= 1;
a_black_pointound_on_border = true;
at_least_one_black_pointound_on_left = true;
} else if !at_least_one_black_pointound_on_left {
left -= 1;
}
}
if left < 0 {
size_exceeded = true;
break;
}
let mut top_border_not_white = true;
while (top_border_not_white || !at_least_one_black_pointound_on_top) && up >= 0 {
top_border_not_white = self.contains_black_point(left, right, up, true);
if top_border_not_white {
up -= 1;
a_black_pointound_on_border = true;
at_least_one_black_pointound_on_top = true;
} else if !at_least_one_black_pointound_on_top {
up -= 1;
}
}
if up < 0 {
size_exceeded = true;
break;
}
}
if !size_exceeded {
let max_size = right - left;
let mut z: Option<Point> = None;
let mut i = 1;
while z.is_none() && i < max_size {
z = self.get_black_point_on_segment(
left as f32,
(down - i) as f32,
(left + i) as f32,
down as f32,
);
i += 1;
}
if z.is_none() {
return Err(Exceptions::NOT_FOUND);
}
let mut t: Option<Point> = None;
let mut i = 1;
while t.is_none() && i < max_size {
t = self.get_black_point_on_segment(
left as f32,
(up + i) as f32,
(left + i) as f32,
up as f32,
);
i += 1;
}
if t.is_none() {
return Err(Exceptions::NOT_FOUND);
}
let mut x: Option<Point> = None;
let mut i = 1;
while x.is_none() && i < max_size {
x = self.get_black_point_on_segment(
right as f32,
(up + i) as f32,
(right - i) as f32,
up as f32,
);
i += 1;
}
if x.is_none() {
return Err(Exceptions::NOT_FOUND);
}
let mut y: Option<Point> = None;
let mut i = 1;
while y.is_none() && i < max_size {
y = self.get_black_point_on_segment(
right as f32,
(down - i) as f32,
(right - i) as f32,
down as f32,
);
i += 1;
}
if y.is_none() {
return Err(Exceptions::NOT_FOUND);
}
Ok(self.center_edges(y.unwrap(), z.unwrap(), x.unwrap(), t.unwrap()))
} else {
Err(Exceptions::NOT_FOUND)
}
}
fn get_black_point_on_segment(&self, a_x: f32, a_y: f32, b_x: f32, b_y: f32) -> Option<Point> {
let a = point(a_x, a_y);
let b = point(b_x, b_y);
let dist = a.distance(b).round() as i32;
let x_step: f32 = (b_x - a_x) / dist as f32;
let y_step: f32 = (b_y - a_y) / dist as f32;
for i in 0..dist {
let x = (a_x + i as f32 * x_step).round() as i32;
let y = (a_y + i as f32 * y_step).round() as i32;
if self.image.get(x as u32, y as u32) {
return Some(point(x as f32, y as f32));
}
}
None
}
fn center_edges(&self, y: Point, z: Point, x: Point, t: Point) -> [Point; 4] {
let yi = y.x;
let yj = y.y;
let zi = z.x;
let zj = z.y;
let xi = x.x;
let xj = x.y;
let ti = t.x;
let tj = t.y;
if yi < self.width as f32 / 2.0 {
[
point(ti - CORR as f32, tj + CORR as f32),
point(zi + CORR as f32, zj + CORR as f32),
point(xi - CORR as f32, xj - CORR as f32),
point(yi + CORR as f32, yj - CORR as f32),
]
} else {
[
point(ti + CORR as f32, tj + CORR as f32),
point(zi + CORR as f32, zj - CORR as f32),
point(xi - CORR as f32, xj + CORR as f32),
point(yi - CORR as f32, yj - CORR as f32),
]
}
}
fn contains_black_point(&self, a: i32, b: i32, fixed: i32, horizontal: bool) -> bool {
if horizontal {
for x in a..=b {
if self.image.get(x as u32, fixed as u32) {
return true;
}
}
} else {
for y in a..=b {
if self.image.get(fixed as u32, y as u32) {
return true;
}
}
}
false
}
}