refactor(ocr): 优化并精简 color_filter 架构设计
- 重构 `OcrBuilder`,将图像矩阵过滤与像素比对等执行层逻辑彻底剥离解耦。 - 优化 `OcrBuilder` 的 `color_filter` 链式调用,将其改造为无心智负担的单次覆盖(Overwrite)逻辑。 - 扩展 `ColorFilter` 特征,新增 `collect_to_vec` 方法,实现底层规则的高内聚收集、精准内存开辟与原地去重排序。
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use std::borrow::Cow;
|
||||
use crate::charset::{TokenFilter, ValidationCtx};
|
||||
use crate::model_metadata::ModelMetadata;
|
||||
use crate::models::base::ModelArgs;
|
||||
use crate::models::loader::{ModelLoader, ModelSession, ModelType};
|
||||
use crate::utils::color_filter::{filter_image, ColorFilter, HsvRange};
|
||||
use crate::utils::color_filter::{ColorFilter, HsvRange, filter_image};
|
||||
use crate::utils::image_io::png_rgba_white_preprocess;
|
||||
use crate::utils::image_processor::{convert_to_grayscale, resize_image};
|
||||
use anyhow::Context;
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{Result, anyhow};
|
||||
use image::{DynamicImage, ImageBuffer, Rgb};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use tract_onnx::prelude::tract_ndarray::s;
|
||||
use tract_onnx::prelude::{
|
||||
@@ -158,48 +158,13 @@ impl<'a> OcrBuilder<'a> {
|
||||
// 反复调用color_filter怎么处理?
|
||||
pub fn color_filter(mut self, filter: &dyn ColorFilter) -> Self {
|
||||
// self.color_filter = Some(value);
|
||||
// 利用组合子预估能力,获取精准分配槽位数
|
||||
let total_capacity = filter.estimated_count();
|
||||
if total_capacity == 0 {
|
||||
return self;
|
||||
|
||||
// 一句话把活全包了!错误信息无缝传递,完美熔断
|
||||
match filter.collect_to_vec() {
|
||||
Ok(new_ranges) => self.color_filter = Ok(new_ranges),
|
||||
Err(err_msg) => self.color_filter = Err(err_msg), // 校验失败,Builder 正式中毒
|
||||
}
|
||||
|
||||
match self.color_filter {
|
||||
Ok(mut ranges) => {
|
||||
// 2. 触发特征多态自检:快捷预设秒过,Custom 变体严格政审
|
||||
if let Err(err_msg) = filter.validate_self() {
|
||||
// 校验失败,Builder 正式中毒,熔断器闭合
|
||||
self.color_filter = Err(err_msg);
|
||||
return self;
|
||||
}
|
||||
|
||||
match &mut ranges {
|
||||
None => {
|
||||
// 情况 A:这是用户第一次配置,直接一击必中分配精准内存
|
||||
let mut v = Vec::with_capacity(total_capacity);
|
||||
|
||||
// 尝试倒入,如果出错,Builder 正式中毒
|
||||
filter.append_ranges(&mut v);
|
||||
ranges = Some(v);
|
||||
}
|
||||
Some(existing_vec) => {
|
||||
// 情况 B:用户之前配置过,现在追加配置。
|
||||
// 绝不让它动态乱涨!我们利用 reserve 方法,【仅此一次】精准追加所需的扩容空间!
|
||||
existing_vec.reserve(total_capacity);
|
||||
|
||||
// 尝试追加倒入
|
||||
filter.append_ranges(existing_vec);
|
||||
}
|
||||
}
|
||||
if let Some(v) = &mut ranges {
|
||||
v.sort_unstable();
|
||||
v.dedup();
|
||||
}
|
||||
// 5. 更新状态
|
||||
self.color_filter = Ok(ranges);
|
||||
}
|
||||
Err(_) => return self,
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
@@ -211,42 +176,7 @@ impl<'a> OcrBuilder<'a> {
|
||||
let charset = &self.ocr.model_metadata.charset;
|
||||
let tokens = &charset.tokens;
|
||||
// let mut temp_indices = Vec::new();
|
||||
let mut has_any_match = false;
|
||||
|
||||
let estimated_capacity = restrict.estimated_capacity();
|
||||
// 精准开辟内存,完美避开 8210 个槽位的巨大空置浪费
|
||||
let mut temp_indices = Vec::with_capacity(estimated_capacity);
|
||||
|
||||
for (idx, token) in tokens.iter().enumerate() {
|
||||
let token_str = token.as_ref();
|
||||
// CTC Blank 空字符串无条件放行,其余交给超高性能的 matches
|
||||
if token_str.is_empty() || idx == 0 {
|
||||
temp_indices.push(idx);
|
||||
};
|
||||
// 组装无拷贝上下文
|
||||
let ctx = ValidationCtx {
|
||||
text: token_str,
|
||||
token_id: idx,
|
||||
};
|
||||
|
||||
if restrict.matches(&ctx) {
|
||||
temp_indices.push(idx);
|
||||
has_any_match = true;
|
||||
}
|
||||
}
|
||||
|
||||
// self.charset_restrict = Some(restrict);
|
||||
// 终极防御:如果除了 Blank 外什么都没匹配上,退化恢复为 None(全量识别)
|
||||
if !has_any_match {
|
||||
println!("警告:当前限制策略与模型字符集完全没有交集!已自动恢复全量识别。");
|
||||
self.charset_restrict = None;
|
||||
} else {
|
||||
// 这一步非常重要:二分查找(binary_search)强依赖数组【有序】。
|
||||
// TopN 天然有序,但如果是用户自定义的 CustomList 或者复杂的 Or 组合,
|
||||
// 遍历出来的索引天然有序,但为了绝对的安全,我们在这里顺手排个序
|
||||
temp_indices.sort_unstable();
|
||||
self.charset_restrict = Some(temp_indices);
|
||||
}
|
||||
self.charset_restrict = restrict.apply_to_charset(tokens);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -260,7 +190,10 @@ impl<'a> OcrBuilder<'a> {
|
||||
// =====================================================================
|
||||
let img_cow = match &self.color_filter {
|
||||
Err(err_msg) => {
|
||||
return Err(anyhow::anyhow!("颜色过滤器初始化失败,全链路短路: {}", err_msg));
|
||||
return Err(anyhow::anyhow!(
|
||||
"颜色过滤器初始化失败,全链路短路: {}",
|
||||
err_msg
|
||||
));
|
||||
}
|
||||
Ok(None) => {
|
||||
// 核心优化点:直接借用原图,不发生任何克隆
|
||||
@@ -313,10 +246,6 @@ impl<'a> OcrBuilder<'a> {
|
||||
|
||||
Ok(tensor)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
impl<'a> OcrBuilder<'a> {
|
||||
pub fn get_valid_indices(&self) -> HashSet<usize> {
|
||||
|
||||
Reference in New Issue
Block a user