fix: 解码器核心bug修复 — 容差计算+积分图像+Sauvola下溢

- detect.rs: finder比例容差改用 base=total/7 替代 avg=total/5
  修复 1:1:3:1:1 模式匹配对任意module_size均正确工作
- detect.rs: finder尺寸估算改为5段总长度(完整28px)替代外圈暗环(4px)
  修复版本号误判(原本将version 1误判为version 10)
- detect.rs: scan_row/scan_col 返回类型改为 (cx,cy,total_size) 三元组
- image.rs: 积分图像公式修正为 I(y+1,x+1)=I(y,x+1)+I(y+1,x)-I(y,x)+pixel
  替换错误的 cumulative row_sum 公式
- image.rs: 积分和运算 u64→i64 避免 debug 模式溢出panic
- perspective.rs: count_finder_hits 应用相同容差修复
- mask.rs: test_score_rule2 改用 score_rule2_raw
- bch.rs: 测试适配 decode_format_info→3元组, decode_version_info→2元组
This commit is contained in:
2026-06-21 22:19:00 +08:00
parent 5651dbf732
commit a03ab95ce5
6 changed files with 47 additions and 47 deletions
+20 -20
View File
@@ -21,7 +21,7 @@ pub(crate) struct DetectResult {
}
/// 水平扫描查找 1:1:3:1:1 比例
fn scan_row(gray: &[Vec<bool>], row: usize) -> Vec<(usize, usize)> {
fn scan_row(gray: &[Vec<bool>], row: usize) -> Vec<(usize, usize, usize)> {
// (列号,运行长度)
let mut runs: Vec<(usize, usize)> = Vec::new();
let width = if gray.is_empty() { 0 } else { gray[0].len() };
@@ -37,8 +37,8 @@ fn scan_row(gray: &[Vec<bool>], row: usize) -> Vec<(usize, usize)> {
runs.push((col - run_len, run_len));
}
// 找 5 连段符合 1:1:3:1:1 比例
let mut centers: Vec<(usize, usize)> = Vec::new();
// 找 5 连段符合 1:1:3:1:1 比例 — 返回 (cx, cy, total_size_px)
let mut centers: Vec<(usize, usize, usize)> = Vec::new();
for i in 0..runs.len().saturating_sub(4) {
let r0 = runs[i].1 as f32;
let r1 = runs[i + 1].1 as f32;
@@ -46,18 +46,17 @@ fn scan_row(gray: &[Vec<bool>], row: usize) -> Vec<(usize, usize)> {
let r3 = runs[i + 3].1 as f32;
let r4 = runs[i + 4].1 as f32;
let avg = (r0 + r1 + r2 + r3 + r4) / 5.0;
if avg < 2.0 {
let total = r0 + r1 + r2 + r3 + r4;
let base = total / 7.0;
if base < 2.0 {
continue;
}
// 检查比例容差 ±40%
let tolerance = 0.4;
let check = |v: f32, expected: f32| (v - expected * avg).abs() < avg * tolerance;
let check = |v: f32, expected: f32| (v - expected * base).abs() < base * tolerance;
if check(r0, 1.0) && check(r1, 1.0) && check(r2, 3.0) && check(r3, 1.0) && check(r4, 1.0) {
let cx = runs[i + 2].0 + runs[i + 2].1 / 2;
centers.push((cx, row));
centers.push((cx, row, total as usize));
}
}
@@ -65,7 +64,7 @@ fn scan_row(gray: &[Vec<bool>], row: usize) -> Vec<(usize, usize)> {
}
/// 垂直扫描查找 1:1:3:1:1 比例
fn scan_col(gray: &[Vec<bool>], col: usize) -> Vec<(usize, usize)> {
fn scan_col(gray: &[Vec<bool>], col: usize) -> Vec<(usize, usize, usize)> {
let height = gray.len();
let mut runs: Vec<(usize, usize)> = Vec::new();
@@ -80,7 +79,7 @@ fn scan_col(gray: &[Vec<bool>], col: usize) -> Vec<(usize, usize)> {
runs.push((row - run_len, run_len));
}
let mut centers: Vec<(usize, usize)> = Vec::new();
let mut centers: Vec<(usize, usize, usize)> = Vec::new();
for i in 0..runs.len().saturating_sub(4) {
let r0 = runs[i].1 as f32;
let r1 = runs[i + 1].1 as f32;
@@ -88,17 +87,19 @@ fn scan_col(gray: &[Vec<bool>], col: usize) -> Vec<(usize, usize)> {
let r3 = runs[i + 3].1 as f32;
let r4 = runs[i + 4].1 as f32;
let avg = (r0 + r1 + r2 + r3 + r4) / 5.0;
if avg < 2.0 {
let total = r0 + r1 + r2 + r3 + r4;
let base = total / 7.0;
if base < 2.0 {
continue;
}
let tolerance = 0.4;
let check = |v: f32, expected: f32| (v - expected * avg).abs() < avg * tolerance;
let check = |v: f32, expected: f32| (v - expected * base).abs() < base * tolerance;
if check(r0, 1.0) && check(r1, 1.0) && check(r2, 3.0) && check(r3, 1.0) && check(r4, 1.0) {
let cy = runs[i + 2].0 + runs[i + 2].1 / 2;
centers.push((col, cy));
let total_px = total as usize;
centers.push((col, cy, total_px));
}
}
@@ -154,18 +155,17 @@ fn find_finders(gray: &[Vec<bool>]) -> Option<[FinderMatch; 3]> {
return None;
}
// 水平扫描
// 水平扫描(含 finder 总尺寸)
let mut h_centers: Vec<(usize, usize, usize)> = Vec::new(); // (cx, cy, size)
for row in (0..height).step_by(2) {
for (cx, cy) in scan_row(gray, row) {
for (cx, cy, total_size) in scan_row(gray, row) {
// 交叉验证:垂直扫描
let v_matches = scan_col(gray, cx);
if v_matches
.iter()
.any(|&(_, vy)| (vy as i32 - cy as i32).abs() < 5)
.any(|&(_, vy, _)| (vy as i32 - cy as i32).abs() < 5)
{
let size = estimate_finder_size(gray, cx, cy);
h_centers.push((cx, cy, size));
h_centers.push((cx, cy, total_size));
}
}
}