refactor(error): 规范化分层错误类型并优化异常捕捉

- 新增 tracing 记录异常,移除不必要的 Result
- 重构 错误处理架构
This commit is contained in:
2026-07-20 20:18:38 +08:00
parent 913ff4d884
commit 3499e89bf1
11 changed files with 140 additions and 101 deletions

View File

@@ -4,7 +4,6 @@ use crate::ocr::color_filter::{HsvRange, apply_to_image};
// use ddddocr_tract::session::{ModelOutput, OcrSession};
use crate::utils::image_convert::png_rgba_white_preprocess;
use crate::utils::image_processor::{convert_to_grayscale, resize_image};
use anyhow::Result;
use image::DynamicImage;
use serde::Serialize;
use std::borrow::Cow;
@@ -17,7 +16,9 @@ use ndarray::ArrayView2;
// Indices(ndarray::Array1<i64>), // 拥有完整所有权的 1维数组可任意传递和返回
// Logits(ndarray::Array2<f32>), // 拥有完整所有权的 2维矩阵可任意传递和返回
// }
use crate::error::{ImagePreprocessError, Result, TensorError};
use crate::{OcrEngine, OcrOutput};
use tracing::{ warn};
#[derive(Debug, Clone, Serialize)]
pub enum OcrResult {
/// 纯文本分支(对应 probability = false
@@ -107,7 +108,7 @@ pub struct Ocr<'a> {
pub(crate) png_fix: bool,
pub(crate) probability: bool,
/// 颜色过滤:保留的颜色列表
pub(crate) final_color_ranges: Result<Option<Vec<HsvRange>>, String>,
pub(crate) final_color_ranges: Result<Option<Vec<HsvRange>>, ImagePreprocessError>,
/// 字符集范围
pub(crate) final_charset_indices: Option<Vec<usize>>,
@@ -127,7 +128,7 @@ impl<'a> Ocr<'a> {
}
}
impl<'a> Ocr<'a> {
pub fn predict(&self, image: &DynamicImage) -> anyhow::Result<OcrResult> {
pub fn predict(&self, image: &DynamicImage) -> Result<OcrResult> {
println!("当前颜色过滤器状态: {:?}", self.final_color_ranges);
// =====================================================================
@@ -137,10 +138,13 @@ impl<'a> Ocr<'a> {
// =====================================================================
let img_cow = match &self.final_color_ranges {
Err(err_msg) => {
return Err(anyhow::anyhow!(
"颜色过滤器初始化失败,全链路短路: {}",
err_msg
));
// return Err(anyhow::anyhow!(
// "颜色过滤器初始化失败,全链路短路: {}",
// err_msg
// ));
return Err(ImagePreprocessError::FilterConfigInvalid(
err_msg.to_string(),
))?;
}
Ok(None) => {
// 核心优化点:直接借用原图,不发生任何克隆
@@ -168,12 +172,12 @@ impl<'a> Ocr<'a> {
// let raw_indices = self.ocr.extract_indices_from_tensor(&raw_tensor)?;
// // 步骤 2: 将索引切片 `&[i64]` 传给解码器进行 CTC 去重和字符映射
// let final_text = self.ctc_decode_to_string(&raw_indices);
let ocr_output = self.process_model_output(raw_tensor);
ocr_output
let ocr_output = self.process_model_output(raw_tensor)?;
Ok(ocr_output)
}
/// 对应 Python 的 _preprocess_image
/// 负责:透明背景修复 -> 灰度化 -> 按比例 Resize -> 归一化 -> 4维张量转换
fn preprocess_image(&self, img: &DynamicImage) -> anyhow::Result<ndarray::Array4<f32>> {
fn preprocess_image(&self, img: &DynamicImage) -> Result<ndarray::Array4<f32>,ImagePreprocessError> {
// 1. 获取模型元数据配置
let meta = self.session.metadata();
let norm = &meta.normalization; // 获取归一化器
@@ -239,7 +243,12 @@ impl<'a> Ocr<'a> {
array
}
_ => return Err(anyhow::anyhow!("不支持的通道数配置: {}", meta.channel)),
// _ => return Err(anyhow::anyhow!("不支持的通道数配置: {}", meta.channel)),
_ => {
return Err(ImagePreprocessError::UnsupportedChannels(
meta.channel as usize,
));
}
};
Ok(array4)
// Ok(tensor)
@@ -265,13 +274,14 @@ impl<'a> Ocr<'a> {
}
// 这段代码未来直接放入 ddddocr-core
fn process_model_output(&self, output: OcrOutput) -> anyhow::Result<OcrResult> {
fn process_model_output(&self, output: OcrOutput) -> Result<OcrResult,TensorError> {
match output {
OcrOutput::Indices(array1) => {
// 对应你原来的 process_i64_tensor
let slice = array1
.as_slice()
.ok_or_else(|| anyhow::anyhow!("内存不连续,无法执行零拷贝解码"))?;
// .ok_or_else(|| anyhow::anyhow!("内存不连续,无法执行零拷贝解码"))?;
.ok_or_else(|| TensorError::NonContiguousMemory)?;
let final_text = self.ctc_decode_to_string(slice);
if self.probability {
@@ -528,8 +538,9 @@ impl<'a> Ocr<'a> {
// 5. 字符映射
if let Some(char_str) = tokens.get(u_idx) {
res.push_str(char_str);
} else {
eprintln!("警告: 预测索引 {} 超出字符集范围", u_idx);
}
else {
warn!("警告: 预测索引 {} 超出字符集范围", u_idx);
}
}
res