#![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;
}
const FLOOD_FILL_MAX_DEPTH: usize = 4096;
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<'_>,
x: i32,
y: usize,
from: Pixel,
to: Pixel,
func: Option<&F>,
user_data: &mut UserData<'_>,
depth: usize,
) where
F: Fn(&mut UserData<'_>, usize, i32, i32),
{
if depth >= FLOOD_FILL_MAX_DEPTH {
return;
}
let mut left = x as usize;
let mut right = x as usize;
let width = image.width;
assert!(image.pixels.len() >= (y + 1) * 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;
for i in left..=right {
let val = unsafe { *image.pixels.get_unchecked(offset + i) };
if val == from {
flood_fill_seed(image, i as i32, y - 1, from, to, func, user_data, depth + 1);
}
}
}
if y < image.height - 1 {
let offset = (y + 1) * width;
for i in left..=right {
let val = unsafe { *image.pixels.get_unchecked(offset + i) };
if val == from {
flood_fill_seed(image, i as i32, y + 1, from, to, func, user_data, depth + 1);
}
}
}
}
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: f64 = 0 as f64;
let mut threshold: u8 = 0 as u8;
for (i, val) in histogram.iter().enumerate() {
q1 = (q1 as u32).wrapping_add(*val) as i32 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 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(ref mut 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]),
0,
);
region
}
fn find_one_corner(user_data: &mut UserData<'_>, y: usize, left: i32, right: i32) {
if let UserData::Polygon(ref mut 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(ref mut 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,
0,
);
let mut 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(&mut psd),
0,
);
}
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 mut capstone = Capstone::default();
capstone.qr_grid = -1;
capstone.ring = ring as i32;
capstone.stone = stone;
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 ratio < 10 || ratio > 70 {
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(ref mut 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 mut 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 as usize].corners[0],
&capstones[a as usize].corners[1],
&capstones[c as usize].corners[0],
&capstones[c as usize].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,
0,
);
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),
0,
);
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 as usize == 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 as usize;
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 as usize;
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)
}
}