refactor(slide,det): 重构目标检测引擎并统一图像输入类型为 DynamicImage以及滑块匹配与比较引擎为 Rust 实现

- 统一 `predict` 和 `get_bbox` 接口参数为 `&DynamicImage`,消除多步处理时的重复图像解码开销。
- 引入轻量级 `DetectionResult` 结构体和固定大小数组 `[f32; 6]` 替代旧的嵌套 `Vec`,彻底消除后处理中的内存碎片。
- 优化 `preproc` 预处理逻辑,使用连续内存切片批量操作替代原有的逐像素迭代遍历。
- 移除多余的 `multiclass_nms_class_agnostic` 转发层,合并并精简 NMS 聚合函数。
- 优化 `calculate_center` 几何中心点计算函数,提高泛型语义并复用于两种匹配模式
- 在执行核心算法前增加尺寸与通道边界守卫(Guard Clauses),提升库的防防御性编程能力与崩溃安全性
- 移除多余的错误二次包装(map_err),改由 Rust 原生 Result 错误传播机制直接向上层抛出
This commit is contained in:
2026-07-03 17:51:28 +08:00
parent 22cc9709ad
commit 7f1ce04f50
7 changed files with 192 additions and 117 deletions

View File

@@ -27,7 +27,7 @@ impl Slide {
Self
}
/// 对应 Python: slide_match
/// 对应 Python: slide_match 滑块匹配接口
pub fn slide_match(
&self,
target_image: &DynamicImage,
@@ -38,9 +38,8 @@ impl Slide {
let background_array = image_to_ndarray(background_image);
self.perform_slide_match(target_array.view(), background_array.view(), simple_target)
.map_err(|e| anyhow!("滑块匹配失败: {}", e))
}
/// 对应 Python: slide_comparison
/// 对应 Python: slide_comparison 差异比较接口
/// 用于比较带坑位的图片与原始背景图,定位差异点
pub fn slide_comparison(
&self,
@@ -53,7 +52,6 @@ impl Slide {
// 2. 执行比较逻辑 (对应 _perform_slide_comparison)
self.perform_slide_comparison(target_array.view(), background_array.view())
.map_err(|e| anyhow!("滑块比较执行失败: {}", e))
}
/// 对应 Python: _perform_slide_comparison
pub fn perform_slide_comparison(
@@ -61,7 +59,7 @@ impl Slide {
target: ArrayView3<u8>,
background: ArrayView3<u8>,
) -> Result<SlideResult> {
let (h, w, _) = target.dim();
// let (h, w, _) = target.dim();
// 1. 计算图像差异并灰度化 (对应 cv2.absdiff + cv2.cvtColor)
// 使用 OpenCV 标准权重公式0.299R + 0.587G + 0.114B
@@ -77,6 +75,26 @@ impl Slide {
// }
// }
// 1. 计算差异数组 (复用 cv2::absdiff)
let (th, tw, tc) = target.dim();
let (bh, bw, bc) = background.dim();
// 1. 比较模式下的严格尺寸校验
if th != bh || tw != bw || tc != bc {
return Err(anyhow!(
"比较模式要求两张图分辨率与通道数完全一致Target: [{}x{}x{}], Background: [{}x{}x{}]",
tw,
th,
tc,
bw,
bh,
bc
));
}
if th == 0 || tw == 0 {
return Err(anyhow!("输入图像尺寸不能为0"));
}
let diff_array = abs_diff(&target, &background);
// 2. 转换为灰度数组 (复用你的 cv2.cvtColor)
@@ -130,6 +148,30 @@ impl Slide {
background: ArrayView3<u8>,
simple_target: bool, // 增加这个参数
) -> Result<SlideResult> {
let (th, tw, tc) = target.dim();
let (bh, bw, bc) = background.dim();
// 1. 严格的鲁棒性校验(防止底层的 imageproc 算子崩溃)
if th == 0 || tw == 0 || bh == 0 || bw == 0 {
return Err(anyhow!("输入图像的宽度或高度不能为0"));
}
if th > bh || tw > bw {
return Err(anyhow!(
"尺寸不匹配:滑块模板(target)尺寸 [{}x{}] 不能大于背景图(background) [{}x{}]",
tw,
th,
bw,
bh
));
}
if tc != bc {
return Err(anyhow!(
"目标图与背景图的通道数不一致 (target: {}, bg: {})",
tc,
bc
));
}
// 1. 统一灰度化
let target_gray = rgb_to_gray(target);
let background_gray = rgb_to_gray(background);