#![allow(clippy::many_single_char_names)]
use std::convert::TryFrom;
use crate::error::ExtractError;
use crate::quirc::*;
use crate::version_db::*;
#[derive(Copy, Clone)]
struct Neighbour {
pub index: i32,
pub distance: f64,
}
#[derive(Copy, Clone)]
struct NeighbourList {
pub n: [Neighbour; 32],
pub count: usize,
}
struct PolygonScoreData<'a> {
pub ref_0: Point,
pub scores: [i32; 4],
pub corners: &'a mut [Point; 4],
}
fn line_intersect(p0: &Point, p1: &Point, q0: &Point, q1: &Point, r: &mut Point) -> i32 {
let a = -(p1.y - p0.y);
let b = p1.x - p0.x;
let c = -(q1.y - q0.y);
let d = q1.x - q0.x;
let e = a * p1.x + b * p1.y;
let f = c * q1.x + d * q1.y;
let det = a * d - b * c;
if det == 0 {
return 0;
}
r.x = (d * e - b * f) / det;
r.y = (-c * e + a * f) / det;
1
}
fn perspective_setup(c: &mut [f64; 8], rect: &[Point; 4], w: f64, h: f64) {
let x0 = rect[0].x as f64;
let y0 = rect[0].y as f64;
let x1 = rect[1].x as f64;
let y1 = rect[1].y as f64;
let x2 = rect[2].x as f64;
let y2 = rect[2].y as f64;
let x3 = rect[3].x as f64;
let y3 = rect[3].y as f64;
let wden = w * (x2 * y3 - x3 * y2 + (x3 - x2) * y1 + x1 * (y2 - y3));
let hden = h * (x2 * y3 + x1 * (y2 - y3) - x3 * y2 + (x3 - x2) * y1);
c[0] = (x1 * (x2 * y3 - x3 * y2) + x0 * (-x2 * y3 + x3 * y2 + (x2 - x3) * y1) + x1 * (x3 - x2) * y0) / wden;
c[1] = -(x0 * (x2 * y3 + x1 * (y2 - y3) - x2 * y1) - x1 * x3 * y2 + x2 * x3 * y1 + (x1 * x3 - x2 * x3) * y0) / hden;
c[2] = x0;
c[3] = (y0 * (x1 * (y3 - y2) - x2 * y3 + x3 * y2) + y1 * (x2 * y3 - x3 * y2) + x0 * y1 * (y2 - y3)) / wden;
c[4] = (x0 * (y1 * y3 - y2 * y3) + x1 * y2 * y3 - x2 * y1 * y3 + y0 * (x3 * y2 - x1 * y2 + (x2 - x3) * y1)) / hden;
c[5] = y0;
c[6] = (x1 * (y3 - y2) + x0 * (y2 - y3) + (x2 - x3) * y1 + (x3 - x2) * y0) / wden;
c[7] = (-x2 * y3 + x1 * y3 + x3 * y2 + x0 * (y1 - y2) - x3 * y1 + (x2 - x1) * y0) / hden;
}
fn perspective_map(c: &[f64; 8], u: f64, v: f64, ret: &mut Point) {
let den = c[6] * u + c[7] * v + 1.0f64;
let x = (c[0] * u + c[1] * v + c[2]) / den;
let y = (c[3] * u + c[4] * v + c[5]) / den;
ret.x = x.round() as i32;
ret.y = y.round() as i32;
}
fn perspective_unmap(c: &[f64; 8], in_0: &Point, u: &mut f64, v: &mut f64) {
let x = in_0.x as f64;
let y = in_0.y as f64;
let den = -c[0] * c[7] * y + c[1] * c[6] * y + (c[3] * c[7] - c[4] * c[6]) * x + c[0] * c[4] - c[1] * c[3];
*u = -(c[1] * (y - c[5]) - c[2] * c[7] * y + (c[5] * c[7] - c[4]) * x + c[2] * c[4]) / den;
*v = (c[0] * (y - c[5]) - c[2] * c[6] * y + (c[5] * c[6] - c[3]) * x + c[2] * c[3]) / den;
}
enum UserData<'a> {
Region(&'a mut Region),
Polygon(&'a mut PolygonScoreData<'a>),
None,
}
impl<'a> UserData<'a> {
fn into_polygon(self) -> &'a mut PolygonScoreData<'a> {
match self {
UserData::Polygon(poly) => poly,
_ => panic!("invalid user data"),
}
}
}
#[derive(Debug)]
struct ImageMut<'a> {
pixels: &'a mut [Pixel],
width: usize,
height: usize,
}
impl<'a> From<&'a mut Quirc> for ImageMut<'a> {
fn from(q: &'a mut Quirc) -> Self {
Self { pixels: &mut q.pixels, width: q.w, height: q.h }
}
}
#[allow(clippy::too_many_arguments)]
fn flood_fill_seed<F>(image: &mut ImageMut<'_>, starting_x: i32, starting_y: usize, from: Pixel, to: Pixel, func: Option<&F>, user_data: &mut UserData<'_>)
where
F: Fn(&mut UserData<'_>, usize, i32, i32),
{
let mut flood_from = vec![(starting_x as usize, starting_y)];
while let Some((x, y)) = flood_from.pop() {
let mut left = x;
let mut right = x;
let width = image.width;
let row = &mut image.pixels[y * width..(y + 1) * width];
while left > 0 && row[left - 1] == from {
left -= 1;
}
while right < width - 1 && row[right + 1] == from {
right += 1;
}
for val in &mut row[left..=right] {
*val = to;
}
if let Some(func) = func {
func(user_data, y, left as i32, right as i32);
}
if y > 0 {
let offset = (y - 1) * width;
let mut prev_matched = false;
for i in left..=right {
let val = unsafe { *image.pixels.get_unchecked(offset + i) };
if val == from {
if !prev_matched {
flood_from.push((i, y - 1));
prev_matched = true;
}
} else {
prev_matched = false;
}
}
}
if y < image.height - 1 {
let offset = (y + 1) * width;
let mut prev_matched = false;
for i in left..=right {
let val = unsafe { *image.pixels.get_unchecked(offset + i) };
if val == from {
if !prev_matched {
flood_from.push((i, y + 1));
prev_matched = true;
}
} else {
prev_matched = false;
}
}
}
}
}
fn otsu(q: &Quirc, image: &[u8]) -> u8 {
let num_pixels = q.w * q.h;
let mut histogram: [u32; 256] = [0; 256];
for value in image {
let value = *value as usize;
histogram[value] = histogram[value].wrapping_add(1);
}
let mut sum: u32 = 0;
for (i, val) in histogram.iter().enumerate() {
sum = sum.wrapping_add((i as u32).wrapping_mul(*val));
}
let mut sum_b: i32 = 0;
let mut q1: i32 = 0;
let mut max = 0_f64;
let mut threshold = 0_u8;
for (i, val) in histogram.iter().enumerate() {
q1 = (q1 as u32).wrapping_add(*val) as i32;
if q1 == 0 {
continue;
}
let q2 = num_pixels as i32 - q1;
if q2 == 0 {
break;
}
sum_b = (sum_b as u32).wrapping_add((i as u32).wrapping_mul(*val)) as i32;
let m1 = sum_b as f64 / q1 as f64;
let m2 = (sum as f64 - sum_b as f64) / q2 as f64;
let m1m2 = m1 - m2;
let variance = m1m2 * m1m2 * q1 as f64 * q2 as f64;
if variance >= max {
threshold = i as u8;
max = variance
}
}
threshold
}
fn area_count(user_data: &mut UserData<'_>, _y: usize, left: i32, right: i32) {
if let UserData::Region(region) = user_data {
region.count += right - left + 1;
} else {
panic!("invalid user data");
}
}
fn region_code(image: &mut ImageMut<'_>, regions: &mut Vec<Region>, x: i32, y: usize) -> i32 {
if x < 0 || x >= image.width as i32 || y >= image.height {
return -1;
}
let pixel = image.pixels[(y as i32 * image.width as i32 + x) as usize];
if pixel >= 2 {
return pixel as i32;
}
if pixel == 0 {
return -1;
}
let region = regions.len() as i32;
if region >= 65_534 {
return -1;
}
regions.push(Region { seed: Point { x, y: y as i32 }, count: 0, capstone: -1 });
flood_fill_seed(image, x, y, pixel, region as Pixel, Some(&area_count), &mut UserData::Region(&mut regions[region as usize]));
region
}
fn find_one_corner(user_data: &mut UserData<'_>, y: usize, left: i32, right: i32) {
if let UserData::Polygon(psd) = user_data {
let xs: [i32; 2] = [left, right];
let dy = y as i32 - psd.ref_0.y;
for x in &xs {
let dx = *x - psd.ref_0.x;
let d = dx * dx + dy * dy;
if d > psd.scores[0] {
psd.scores[0] = d;
psd.corners[0].x = *x;
psd.corners[0].y = y as i32;
}
}
} else {
panic!("invalid user data");
}
}
fn find_other_corners(user_data: &mut UserData<'_>, y: usize, left: i32, right: i32) {
if let UserData::Polygon(psd) = user_data {
let xs: [i32; 2] = [left, right];
for x in &xs {
let up = *x * psd.ref_0.x + y as i32 * psd.ref_0.y;
let right_0 = *x * -psd.ref_0.y + y as i32 * psd.ref_0.x;
let scores: [i32; 4] = [up, right_0, -up, -right_0];
for (j, score) in scores.iter().enumerate() {
if *score > psd.scores[j] {
psd.scores[j] = *score;
psd.corners[j].x = *x;
psd.corners[j].y = y as i32;
}
}
}
} else {
panic!("invalid user data");
}
}
fn find_region_corners(image: &mut ImageMut<'_>, region: &Region, rcode: Pixel, point: &Point, corners: &mut [Point; 4]) {
let mut psd = PolygonScoreData { ref_0: *point, scores: [-1, 0, 0, 0], corners };
let mut psd_ref = UserData::Polygon(&mut psd);
flood_fill_seed(image, region.seed.x, region.seed.y as usize, rcode, 1, Some(&find_one_corner), &mut psd_ref);
let psd = psd_ref.into_polygon();
psd.ref_0.x = psd.corners[0].x - psd.ref_0.x;
psd.ref_0.y = psd.corners[0].y - psd.ref_0.y;
for corner in &mut psd.corners[..] {
*corner = region.seed;
}
let i = region.seed.x * psd.ref_0.x + region.seed.y * psd.ref_0.y;
psd.scores[0] = i;
psd.scores[2] = -i;
let i = region.seed.x * -psd.ref_0.y + region.seed.y * psd.ref_0.x;
psd.scores[1] = i;
psd.scores[3] = -i;
flood_fill_seed(image, region.seed.x, region.seed.y as usize, 1, rcode, Some(&find_other_corners), &mut UserData::Polygon(psd));
}
fn record_capstone(image: &mut ImageMut<'_>, regions: &mut [Region], capstones: &mut Vec<Capstone>, ring: Pixel, stone: i32) {
if capstones.len() >= 32 {
return;
}
let cs_index = capstones.len() as i32;
let capstone = Capstone { qr_grid: -1, ring: ring as i32, stone, ..Default::default() };
capstones.push(capstone);
let capstone = &mut capstones[cs_index as usize];
regions[stone as usize].capstone = cs_index;
regions[ring as usize].capstone = cs_index;
let region = ®ions[ring as usize];
let seed = ®ions[stone as usize].seed;
find_region_corners(image, region, ring, seed, &mut capstone.corners);
perspective_setup(&mut capstone.c, &capstone.corners, 7.0, 7.0);
perspective_map(&capstone.c, 3.5, 3.5, &mut capstone.center);
}
fn test_capstone(image: &mut ImageMut<'_>, regions: &mut Vec<Region>, capstones: &mut Vec<Capstone>, x: i32, y: usize, pb: &[i32]) {
let ring_right = region_code(image, regions, x - pb[4], y);
let stone = region_code(image, regions, x - pb[4] - pb[3] - pb[2], y);
let ring_left = region_code(image, regions, x - pb[4] - pb[3] - pb[2] - pb[1] - pb[0], y);
if ring_left < 0 || ring_right < 0 || stone < 0 {
return;
}
if ring_left != ring_right {
return;
}
if ring_left == stone {
return;
}
let stone_reg = ®ions[stone as usize];
let ring_reg = ®ions[ring_left as usize];
if stone_reg.capstone >= 0 || ring_reg.capstone >= 0 {
return;
}
let ratio = stone_reg.count * 100 / ring_reg.count;
if !(10..=70).contains(&ratio) {
return;
}
record_capstone(image, regions, capstones, ring_left as Pixel, stone);
}
fn finder_scan(image: &mut ImageMut<'_>, regions: &mut Vec<Region>, capstones: &mut Vec<Capstone>, y: usize) {
static CHECK: [i32; 5] = [1, 1, 3, 1, 1];
let offset = y * image.width;
let mut last_color = 0;
let mut run_length = 0;
let mut run_count = 0;
let mut pb = [0; 5];
assert!(image.pixels.len() >= offset + image.width);
for x in 0..image.width {
let pixel = unsafe { image.pixels.get_unchecked(offset + x) };
let color = if *pixel as i32 != 0 { 1 } else { 0 };
if x != 0 && color != last_color {
pb.copy_within(1.., 0);
pb[4] = run_length;
run_length = 0;
run_count += 1;
if color == 0 && run_count >= 5 {
let mut ok = 1;
let avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4;
let err = avg * 3 / 4;
for (pb, check) in pb.iter().zip(CHECK.iter()) {
if *pb < *check * avg - err || *pb > *check * avg + err {
ok = 0;
}
}
if ok != 0 {
test_capstone(image, regions, capstones, x as i32, y, &pb);
}
}
}
run_length += 1;
last_color = color;
}
}
fn find_alignment_pattern(image: &mut ImageMut<'_>, capstones: &[Capstone], regions: &mut Vec<Region>, qr: &mut Grid) {
let c0 = &capstones[qr.caps[0]];
let c2 = &capstones[qr.caps[2]];
let mut a = Point::default();
let mut c = Point::default();
let mut step_size = 1;
let mut dir = 0;
let mut u = 0.;
let mut v = 0.;
let mut b = qr.align;
perspective_unmap(&c0.c, &b, &mut u, &mut v);
perspective_map(&c0.c, u, v + 1.0f64, &mut a);
perspective_unmap(&c2.c, &b, &mut u, &mut v);
perspective_map(&c2.c, u + 1.0f64, v, &mut c);
let size_estimate = ((a.x - b.x) * -(c.y - b.y) + (a.y - b.y) * (c.x - b.x)).abs();
static DX_MAP: [i32; 4] = [1, 0, -1, 0];
static DY_MAP: [i32; 4] = [0, -1, 0, 1];
while step_size * step_size < size_estimate * 100 {
for _ in 0..step_size {
let code = region_code(image, regions, b.x, b.y as usize);
if code >= 0 {
let reg = ®ions[code as usize];
if reg.count >= size_estimate / 2 && reg.count <= size_estimate * 2 {
qr.align_region = Some(code as Pixel);
return;
}
}
b.x += DX_MAP[dir as usize];
b.y += DY_MAP[dir as usize];
}
dir = (dir + 1) % 4;
if dir & 1 == 0 {
step_size += 1
}
}
}
fn find_leftmost_to_line(user_data: &mut UserData<'_>, y: usize, left: i32, right: i32) {
if let UserData::Polygon(psd) = user_data {
let xs: [i32; 2] = [left, right];
for x in &xs {
let d = -psd.ref_0.y * *x + psd.ref_0.x * y as i32;
if d < psd.scores[0] {
psd.scores[0] = d;
psd.corners[0].x = *x;
psd.corners[0].y = y as i32;
}
}
} else {
panic!("invalid user data");
}
}
fn timing_scan(image: &Image<'_>, p0: &Point, p1: &Point) -> i32 {
let mut n = p1.x - p0.x;
let mut d = p1.y - p0.y;
let mut x = p0.x;
let mut y = p0.y;
if p0.x < 0 || p0.y < 0 || p0.x >= image.width as i32 || p0.y >= image.height as i32 {
return -1;
}
if p1.x < 0 || p1.y < 0 || p1.x >= image.width as i32 || p1.y >= image.height as i32 {
return -1;
}
let is_x_dom = if n.abs() > d.abs() {
std::mem::swap(&mut n, &mut d);
true
} else {
false
};
let nondom_step = if n < 0 {
n = -n;
-1
} else {
1
};
let dom_step = if d < 0 {
d = -d;
-1
} else {
1
};
let mut a = 0;
let mut run_length = 0;
let mut count = 0;
for _ in 0..=d {
if y < 0 || y >= image.height as i32 || x < 0 || x >= image.width as i32 {
break;
}
let pixel = image.pixels[(y * image.width as i32 + x) as usize] as i32;
if pixel != 0 {
if run_length >= 2 {
count += 1;
}
run_length = 0;
} else {
run_length += 1;
}
a += n;
if is_x_dom {
x += dom_step;
} else {
y += dom_step;
}
if a >= d {
if is_x_dom {
y += nondom_step;
} else {
x += dom_step;
}
a -= d;
}
}
count
}
fn measure_timing_pattern(qr: &mut Grid, capstones: &[Capstone], image: &Image<'_>) -> i32 {
static US: [f64; 3] = [6.5, 6.5, 0.5];
static VS: [f64; 3] = [0.5, 6.5, 6.5];
for (i, (us, vs)) in US.iter().zip(VS.iter()).enumerate() {
let cap = &capstones[qr.caps[i]];
perspective_map(&cap.c, *us, *vs, &mut qr.tpep[i]);
}
qr.hscan = timing_scan(image, &qr.tpep[1], &qr.tpep[2]);
qr.vscan = timing_scan(image, &qr.tpep[1], &qr.tpep[0]);
let mut scan = qr.hscan;
if qr.vscan > scan {
scan = qr.vscan
}
if scan < 0 {
return -1;
}
let size = scan * 2 + 13;
let ver = (size - 15) / 4;
qr.grid_size = ver * 4 + 17;
0
}
fn read_cell(q: &Quirc, index: usize, x: i32, y: i32) -> i32 {
let qr = &q.grids[index];
let mut p = Point::default();
perspective_map(&qr.c, x as f64 + 0.5f64, y as f64 + 0.5f64, &mut p);
if p.y < 0 || p.y >= q.h as i32 || p.x < 0 || p.x >= q.w as i32 {
return 0;
}
if q.pixels[(p.y * q.w as i32 + p.x) as usize] != 0 { 1 } else { -1 }
}
#[derive(Debug)]
struct Image<'a> {
pixels: &'a [Pixel],
width: usize,
height: usize,
}
impl<'a> From<&'a Quirc> for Image<'a> {
fn from(q: &'a Quirc) -> Self {
Self { pixels: &q.pixels, width: q.w, height: q.h }
}
}
impl<'a> From<&'a ImageMut<'a>> for Image<'a> {
fn from(img: &'a ImageMut<'a>) -> Self {
Self { pixels: img.pixels, width: img.width, height: img.height }
}
}
fn fitness_cell(qr: &Grid, image: &Image<'_>, x: i32, y: i32) -> i32 {
static OFFSETS: [f64; 3] = [0.3, 0.5, 0.7];
let mut score = 0;
let mut p = Point::default();
for v in &OFFSETS {
for u in &OFFSETS {
p.clear();
perspective_map(&qr.c, x as f64 + *u, y as f64 + *v, &mut p);
if !(p.y < 0 || p.y >= image.height as i32 || p.x < 0 || p.x >= image.width as i32) {
if image.pixels[(p.y * image.width as i32 + p.x) as usize] != 0 {
score += 1;
} else {
score -= 1;
}
}
}
}
score
}
fn fitness_ring(qr: &Grid, image: &Image<'_>, cx: i32, cy: i32, radius: i32) -> i32 {
let mut score: i32 = 0;
for i in 0..radius * 2 {
score += fitness_cell(qr, image, cx - radius + i, cy - radius);
score += fitness_cell(qr, image, cx - radius, cy + radius - i);
score += fitness_cell(qr, image, cx + radius, cy - radius + i);
score += fitness_cell(qr, image, cx + radius - i, cy + radius);
}
score
}
fn fitness_apat(qr: &Grid, image: &Image<'_>, cx: i32, cy: i32) -> i32 {
fitness_cell(qr, image, cx, cy) - fitness_ring(qr, image, cx, cy, 1) + fitness_ring(qr, image, cx, cy, 2)
}
fn fitness_capstone(qr: &Grid, image: &Image<'_>, mut x: i32, mut y: i32) -> i32 {
x += 3;
y += 3;
fitness_cell(qr, image, x, y) + fitness_ring(qr, image, x, y, 1) - fitness_ring(qr, image, x, y, 2) + fitness_ring(qr, image, x, y, 3)
}
const MAX_ALIGNMENT: usize = 7;
fn fitness_all(qr: &Grid, image: &Image<'_>) -> i32 {
let version = usize::try_from((qr.grid_size - 17) / 4).expect("invalid version");
let info = &VERSION_DB[version];
let mut score: i32 = 0;
for i in 0..qr.grid_size - 14 {
let expect = if i & 1 != 0 { 1 } else { -1 };
score += fitness_cell(qr, image, i + 7, 6) * expect;
score += fitness_cell(qr, image, 6, i + 7) * expect;
}
score += fitness_capstone(qr, image, 0, 0);
score += fitness_capstone(qr, image, qr.grid_size - 7, 0);
score += fitness_capstone(qr, image, 0, qr.grid_size - 7);
if version > VERSION_MAX {
return score;
}
let mut ap_count = 0;
while ap_count < MAX_ALIGNMENT && info.apat[ap_count] != 0 {
ap_count += 1;
}
if ap_count == 0 {
return score;
}
for x in &info.apat[1..ap_count - 1] {
score += fitness_apat(qr, image, 6, *x);
score += fitness_apat(qr, image, *x, 6);
}
for x in &info.apat[1..ap_count] {
for y in &info.apat[1..ap_count] {
score += fitness_apat(qr, image, *x, *y);
}
}
score
}
fn jiggle_perspective(qr: &mut Grid, image: &Image<'_>) {
let mut best = fitness_all(qr, image);
let mut adjustments: [f64; 8] = [0.; 8];
for (a_val, c_val) in adjustments.iter_mut().zip(qr.c.iter()) {
*a_val = c_val * 0.02;
}
for _pass in 0..5 {
for i in 0..16 {
let j = i >> 1;
let old = qr.c[j];
let step = adjustments[j];
qr.c[j] = if i & 1 != 0 { old + step } else { old - step };
let test = fitness_all(qr, image);
if test > best { best = test } else { qr.c[j] = old }
}
for val in &mut adjustments {
*val *= 0.5;
}
}
}
fn setup_qr_perspective(qr: &mut Grid, capstones: &[Capstone], image: &Image<'_>) {
let rect = [capstones[qr.caps[1]].corners[0], capstones[qr.caps[2]].corners[0], qr.align, capstones[qr.caps[0]].corners[0]];
perspective_setup(&mut qr.c, &rect, (qr.grid_size - 7) as f64, (qr.grid_size - 7) as f64);
jiggle_perspective(qr, image);
}
fn rotate_capstone(cap: &mut Capstone, h0: &Point, hd: &Point) {
let mut copy: [Point; 4] = [Point::default(); 4];
let mut best = 0;
let mut best_score = 2147483647;
for (j, p) in cap.corners.iter().enumerate() {
let score = (p.x - h0.x) * -hd.y + (p.y - h0.y) * hd.x;
if j == 0 || score < best_score {
best = j;
best_score = score
}
}
for (i, copy) in copy.iter_mut().enumerate() {
*copy = cap.corners[(i + best) % 4];
}
cap.corners = copy;
perspective_setup(&mut cap.c, &cap.corners, 7.0, 7.0);
}
fn record_qr_grid(
image: &mut ImageMut<'_>, regions: &mut Vec<Region>, capstones: &mut [Capstone], grids: &mut Vec<Grid>, mut a: usize, b: usize, mut c: usize,
) {
if grids.len() >= 8 {
return;
}
let h0 = capstones[a].center;
let mut hd = Point { x: capstones[c].center.x - capstones[a].center.x, y: capstones[c].center.y - capstones[a].center.y };
if (capstones[b].center.x - h0.x) * -hd.y + (capstones[b].center.y - h0.y) * hd.x > 0 {
std::mem::swap(&mut a, &mut c);
hd.x = -hd.x;
hd.y = -hd.y
}
let qr_index = grids.len();
let mut qr = Grid::default();
qr.caps[0] = a;
qr.caps[1] = b;
qr.caps[2] = c;
qr.align_region = None;
grids.push(qr);
let qr = &mut grids[qr_index];
for cap_index in &qr.caps {
let cap = &mut capstones[*cap_index];
rotate_capstone(cap, &h0, &hd);
cap.qr_grid = qr_index as i32;
}
if measure_timing_pattern(qr, capstones, &Image::from(&*image)) >= 0 {
if line_intersect(&capstones[a].corners[0], &capstones[a].corners[1], &capstones[c].corners[0], &capstones[c].corners[3], &mut qr.align) != 0 {
if qr.grid_size > 21 {
find_alignment_pattern(image, capstones, regions, qr);
if let Some(align_region) = qr.align_region {
let reg = ®ions[align_region as usize];
qr.align = reg.seed;
let mut corners = [qr.align, Point::default(), Point::default(), Point::default()];
let mut psd = PolygonScoreData { ref_0: hd, scores: [0; 4], corners: &mut corners };
psd.scores[0] = -hd.y * qr.align.x + hd.x * qr.align.y;
flood_fill_seed::<fn(&mut UserData<'_>, usize, i32, i32) -> ()>(
image,
reg.seed.x,
reg.seed.y as usize,
align_region,
1,
None,
&mut UserData::None,
);
flood_fill_seed(image, reg.seed.x, reg.seed.y as usize, 1, align_region, Some(&find_leftmost_to_line), &mut UserData::Polygon(&mut psd));
qr.align = corners[0];
}
}
setup_qr_perspective(qr, capstones, &Image::from(&*image));
return;
}
}
for cap_index in &qr.caps {
capstones[*cap_index].qr_grid = -1;
}
grids.pop();
}
fn test_neighbours(
image: &mut ImageMut<'_>, regions: &mut Vec<Region>, capstones: &mut [Capstone], grids: &mut Vec<Grid>, i: usize, hlist: &NeighbourList,
vlist: &NeighbourList,
) {
let mut best_score = 0.0;
let mut best_h = -1;
let mut best_v = -1;
for hn in &hlist.n[..hlist.count] {
for vn in &vlist.n[0..vlist.count] {
let score = (1.0 - hn.distance / vn.distance).abs();
if score > 2.5 {
continue;
}
if best_h < 0 || score < best_score {
best_h = hn.index;
best_v = vn.index;
best_score = score
}
}
}
if best_h < 0 || best_v < 0 {
return;
}
record_qr_grid(image, regions, capstones, grids, best_h as usize, i, best_v as usize);
}
fn test_grouping(image: &mut ImageMut<'_>, regions: &mut Vec<Region>, capstones: &mut [Capstone], grids: &mut Vec<Grid>, i: usize) {
let mut hlist = NeighbourList { n: [Neighbour { index: 0, distance: 0. }; 32], count: 0 };
let mut vlist = NeighbourList { n: [Neighbour { index: 0, distance: 0. }; 32], count: 0 };
if capstones[i].qr_grid >= 0 {
return;
}
hlist.count = 0;
vlist.count = 0;
let c1c = capstones[i].c;
for (j, c2) in capstones.iter_mut().enumerate() {
let mut u = 0.;
let mut v = 0.;
if i == j || c2.qr_grid >= 0 {
continue;
}
perspective_unmap(&c1c, &c2.center, &mut u, &mut v);
u = (u - 3.5).abs();
v = (v - 3.5).abs();
if u < 0.2 * v {
let count = hlist.count;
hlist.count += 1;
let n = &mut hlist.n[count];
n.index = j as i32;
n.distance = v;
}
if v < 0.2 * u {
let count = vlist.count;
vlist.count += 1;
let n = &mut vlist.n[count];
n.index = j as i32;
n.distance = u;
}
}
if !(hlist.count != 0 && vlist.count != 0) {
return;
}
test_neighbours(image, regions, capstones, grids, i, &hlist, &vlist);
}
fn pixels_setup(q: &mut Quirc, source: &[u8], threshold: u8) {
let dest = &mut q.pixels;
for (value, dest) in source.iter().zip(dest.iter_mut()) {
*dest = if (*value as i32) < threshold as i32 { 1 } else { 0 } as Pixel;
}
}
impl Quirc {
pub fn identify<'a>(&'a mut self, width: usize, height: usize, image: &[u8]) -> CodeIter<'a> {
self.resize(width, height);
assert_eq!(self.w * self.h, image.len(), "image must be exactly of the size width * height");
self.reset();
self.regions.push(Default::default());
self.regions.push(Default::default());
let threshold = otsu(self, image);
pixels_setup(self, image, threshold);
let mut image = ImageMut { pixels: &mut self.pixels, width: self.w, height: self.h };
let regions = &mut self.regions;
let capstones = &mut self.capstones;
for i in 0..self.h {
finder_scan(&mut image, regions, capstones, i);
}
let grids = &mut self.grids;
for i in 0..capstones.len() {
test_grouping(&mut image, regions, capstones, grids, i);
}
CodeIter { quirc: self, current: 0 }
}
fn extract(&self, index: usize) -> Result<Code, ExtractError> {
let qr = self.grids[index];
if index > self.count() {
return Err(ExtractError::OutOfBounds);
}
let mut code = Code::default();
perspective_map(&qr.c, 0.0, 0.0, &mut code.corners[0]);
perspective_map(&qr.c, qr.grid_size as f64, 0.0, &mut code.corners[1]);
perspective_map(&qr.c, qr.grid_size as f64, qr.grid_size as f64, &mut code.corners[2]);
perspective_map(&qr.c, 0.0, qr.grid_size as f64, &mut code.corners[3]);
code.size = qr.grid_size;
let mut i = 0;
for y in 0..qr.grid_size {
for x in 0..qr.grid_size {
if read_cell(self, index, x, y) > 0 {
code.cell_bitmap[(i >> 3) as usize] = (code.cell_bitmap[(i >> 3) as usize] as i32 | 1 << (i & 7)) as u8
}
i += 1;
}
}
Ok(code)
}
}
pub struct CodeIter<'a> {
quirc: &'a Quirc,
current: usize,
}
impl Iterator for CodeIter<'_> {
type Item = Result<Code, ExtractError>;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.quirc.count() {
return None;
}
let res = self.quirc.extract(self.current);
self.current += 1;
Some(res)
}
}