7 次代码提交 1e4a3fa9b9 ... 24f1406736

作者 SHA1 备注 提交日期
  zhch158_admin 24f1406736 feat(normalize_financial_numbers): 优化金额规范化逻辑,增加对纯整数和正确小数格式的直接返回处理 2 周之前
  zhch158_admin 8d5d0a9f3e feat(process_single_input): 优化调试模式处理,确保命令行参数优先级最高 2 周之前
  zhch158_admin c669c3c99a feat(pipeline_manager): 添加调试模式参数以增强元素处理的灵活性 2 周之前
  zhch158_admin f3ae94099b feat(element_processors): 添加调试模式选项以增强表格识别的灵活性 2 周之前
  zhch158_admin 9a25df70fd refactor(config): 禁用所有配置文件中的调试模式以提高性能 2 周之前
  zhch158_admin 5fc9a819ab refactor(processor_configs): 更新日志级别配置,禁用调试模式以提高性能 2 周之前
  zhch158_admin 5e7ced0f84 feat(debug_mode): 添加调试模式文档,描述当前问题及建议设计 2 周之前

+ 109 - 0
docs/ocr_tools/universal_doc_parser/debug参数控制.md

@@ -0,0 +1,109 @@
+## 当前问题诊断
+
+**共 5 处断裂点:**
+
+| # | 问题 | 位置 |
+|---|---|---|
+| 1 | `--debug` CLI 参数**从未传进** `pipeline.debug_mode` | main_v2.py:yaml 有 output 块时,`debug` 参数被完全丢弃 |
+| 2 | `_process_all_elements` 调用时**没有穿透** `debug_mode` | pipeline_manager_v2.py:element_processors 看不到 pipeline 层的 debug 状态 |
+| 3 | `element_processors` 构建子模块 `debug_options` 时**漏掉了 `enabled`** | 只注入了 `output_dir`/`prefix`,子模块即使拿到路径也不会输出 |
+| 4 | 新旧两套系统(`debug_mode` vs `debug_options.enabled`)**无统一激活入口** | base.py 做了兼容读取但没有统一写入 |
+| 5 | streaming 模式**完全不传播** `debug_mode` | `pipeline_manager_v2_streaming.py` |
+
+---
+
+## 建议设计:单一开关 → 逐层传播
+
+**核心原则**:命令行 `--debug` 是唯一的顶层开关,向下覆盖 yaml 配置。
+
+### 第一层:main_v2.py — 写入 pipeline.debug_mode
+
+```python
+# process_single_input 中,创建 pipeline 后立即覆盖 debug_mode
+pipeline = _create_pipeline(...)
+
+# 命令行 --debug 优先级高于 yaml
+if debug:
+    pipeline.debug_mode = True
+    # 同步覆盖 output 配置
+    pipeline.config.setdefault('output', {})['debug_mode'] = True
+```
+
+### 第二层:pipeline_manager_v2.py — 向子模块传播
+
+在 `_process_all_elements` 调用处加入 debug 参数传递(pipeline → element_processors):
+
+```python
+processed_elements, discarded_elements = self._process_all_elements(
+    ...
+    debug_mode=self.debug_mode,   # ← 补充这一项
+    output_dir=output_dir,
+)
+```
+
+### 第三层:element_processors.py — 构建 debug_options 时加上 enabled
+
+```python
+# 当前(缺失 enabled)
+debug_opts_override = {'output_dir': output_dir, 'prefix': basename}
+
+# 修正后
+debug_opts_override = {
+    'enabled': debug_mode,        # ← 缺失的这一项
+    'output_dir': output_dir,
+    'prefix': basename,
+}
+```
+
+### yaml 配置建议:`debug_options` 应只配置**保存内容**,不配 `enabled`
+
+各子模块的 `debug_options` 只负责描述"调试时保存什么",`enabled` 动态注入:
+
+```yaml
+# ✅ 推荐:只描述保存项,enabled 由运行时控制
+table_recognition_wired:
+  debug_options:
+    output_dir: null        # null = 随输出目录
+    save_table_lines: true
+    save_grid_structure: true
+    save_text_overlay: true
+
+# ❌ 不推荐:hardcode enabled: true,会绕过命令行开关
+table_recognition_wired:
+  debug_options:
+    enabled: true           # 这会永远开启,无法被命令行关闭
+```
+
+---
+
+## 推荐的完整控制层级
+
+```
+命令行 --debug
+    ↓ 写入
+pipeline.debug_mode(覆盖 yaml output.debug_mode)
+    ↓ 注入(已有)
+layout_detector.debug_mode
+    ↓ 注入(待补)
+element_processors(debug_mode=...)
+    ↓ 构建(待补 enabled 字段)
+wired_table.recognize(debug_options={'enabled': debug_mode, ...})
+table_classifier.classify(debug_options={'enabled': debug_mode, ...})
+```
+
+`--log_level` 与 debug 是**独立维度**,不需要联动,按现有设计保持即可。
+
+---
+
+## 需要修改的文件清单
+
+| 文件 | 改动 |
+|---|---|
+| main_v2.py | `--debug` 写入 `pipeline.debug_mode` |
+| pipeline_manager_v2.py | `_process_all_elements` 传入 `debug_mode` |
+| core/element_processors.py | 接收并向子模块传递 `debug_mode`,构建 `debug_options` 时加 `enabled` |
+| pipeline_manager_v2_streaming.py | 同步补充 debug_mode 传播(与 v2 保持一致) |
+| yaml 配置文件 | 各模块 `debug_options.enabled` 改为 `false`(运行时由命令行控制) |
+
+---
+## 结语

+ 2 - 2
ocr_tools/ocr_batch/processor_configs.yaml

@@ -34,8 +34,8 @@ processors:
       # - "--config=/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/config/bank_statement_yusys_v2.yaml"
       - "--pages=1-35"
       - "--streaming"
-      - "--debug"
-      - "--log_level=DEBUG"
+      # - "--debug"
+      - "--log_level=INFO"
     output_subdir: "bank_statement_yusys_v3"
     log_subdir: "logs/bank_statement_yusys_v3"
     # output_subdir: "bank_statement_yusys_v2"

+ 1 - 2
ocr_tools/universal_doc_parser/config/bank_statement_glm_vl.yaml

@@ -78,7 +78,6 @@ vl_recognition:
   
   # 场景特定配置
   table_recognition:
-    return_cells_coordinate: false  # GLM-OCR 不直接返回单元格坐标
     bank_statement_mode: true
 
 # ============================================================
@@ -110,4 +109,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 1 - 2
ocr_tools/universal_doc_parser/config/bank_statement_mineru_vl.yaml

@@ -56,7 +56,6 @@ vl_recognition:
   
   # 场景特定配置
   table_recognition:
-    return_cells_coordinate: true
     bank_statement_mode: true
     
 ocr_recognition:
@@ -85,4 +84,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 1 - 2
ocr_tools/universal_doc_parser/config/bank_statement_paddle_vl.yaml

@@ -62,7 +62,6 @@ vl_recognition:
   
   # 场景特定配置
   table_recognition:
-    return_cells_coordinate: true
     bank_statement_mode: true
     
 ocr_recognition:
@@ -91,4 +90,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 1 - 2
ocr_tools/universal_doc_parser/config/bank_statement_smart_router.yaml

@@ -133,7 +133,6 @@ vl_recognition:
   
   # 表格识别特定配置
   table_recognition:
-    return_cells_coordinate: true  # 返回单元格坐标
 
 output:
   create_subdir: false
@@ -149,4 +148,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 2 - 3
ocr_tools/universal_doc_parser/config/bank_statement_yusys_v2.yaml

@@ -67,7 +67,6 @@ vl_recognition:
   
   # 表格识别特定配置
   table_recognition:
-    return_cells_coordinate: true  # 返回单元格坐标
     bank_statement_mode: true      # 银行流水优化模式
 
 # ============================================================
@@ -93,8 +92,8 @@ output:
   save_html: true           # 保存表格 HTML 文件
   
   # Debug 输出(通过命令行 --debug 开启)
-  save_layout_image: false  # 保存 layout 可视化图片
-  save_ocr_image: false     # 保存 OCR 可视化图片
+  save_layout_image: true  # 保存 layout 可视化图片
+  save_ocr_image: true     # 保存 OCR 可视化图片
   draw_type_label: true     # 在可视化图片上标注类型
   draw_bbox_number: true    # 在可视化图片上标注序号
   

+ 4 - 5
ocr_tools/universal_doc_parser/config/bank_statement_yusys_v3.yaml

@@ -31,7 +31,7 @@ layout_detection:
   # Debug 可视化配置(与 MinerUWiredTableRecognizer.DebugOptions 对齐)
   # 默认关闭。开启后将保存:layout检测结果
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     prefix: ""                  # 保存文件名前缀(如设置为页码)
 
@@ -55,7 +55,7 @@ table_classification:
   # Debug 可视化配置(与 MinerUWiredTableRecognizer.DebugOptions 对齐)
   # 默认关闭。开启后将保存:表格线
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
     image_format: "png"          # 可视化图片格式:png/jpg
@@ -98,7 +98,7 @@ table_recognition_wired:
   # Debug 可视化配置(与 MinerUWiredTableRecognizer.DebugOptions 对齐)
   # 默认关闭。开启后将保存:表格线、连通域、逻辑网格结构、文本覆盖可视化。
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
     save_connected_components: true  # 保存连通域提取的单元格图
@@ -133,7 +133,6 @@ vl_recognition:
   
   # 表格识别特定配置
   table_recognition:
-    return_cells_coordinate: true  # 返回单元格坐标
 
 output:
   create_subdir: false
@@ -149,4 +148,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 4 - 5
ocr_tools/universal_doc_parser/config/bank_statement_yusys_v4.yaml

@@ -61,7 +61,7 @@ layout_detection:
 
   # Debug 可视化配置
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     prefix: ""                  # 保存文件名前缀(如设置为页码)
 
@@ -88,7 +88,7 @@ table_classification:
 
   # Debug 可视化配置
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
     image_format: "png"          # 可视化图片格式:png/jpg
@@ -132,7 +132,7 @@ table_recognition_wired:
 
   # Debug 可视化配置
   debug_options:
-    enabled: true               # 是否开启调试可视化输出
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
     output_dir: null             # 调试输出目录;null不输出
     save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
     save_connected_components: true  # 保存连通域提取的单元格图
@@ -178,7 +178,6 @@ vl_recognition:
   
   # 场景特定配置
   table_recognition:
-    return_cells_coordinate: false  # GLM-OCR 不直接返回单元格坐标
 
 # ============================================================
 # 输出配置
@@ -197,4 +196,4 @@ output:
   draw_bbox_number: true
   save_enhanced_json: true
   normalize_numbers: true
-  debug_mode: true
+  debug_mode: false

+ 3 - 5
ocr_tools/universal_doc_parser/core/element_processors.py

@@ -363,6 +363,7 @@ class ElementProcessors:
         output_dir: Optional[str] = None,
         basename: Optional[str] = None,
         normalize_numbers: bool = True,
+        debug_mode: bool = False,
     ) -> Dict[str, Any]:
         """
         使用 UNet 有线表格识别处理表格元素
@@ -397,7 +398,7 @@ class ElementProcessors:
                 raise RuntimeError("Wired table recognizer not available")
             
             # 构造调试选项覆盖
-            debug_opts_override = {}
+            debug_opts_override = {'enabled': debug_mode}
             if output_dir:
                 debug_opts_override['output_dir'] = output_dir
             if basename:
@@ -510,10 +511,7 @@ class ElementProcessors:
             raise RuntimeError("VL recognizer not available")
             
         try:
-            vl_result = vl_recognizer.recognize_table(
-                cropped_table,
-                return_cells_coordinate=True
-            )
+            vl_result = vl_recognizer.recognize_table(cropped_table)
             table_html = vl_result.get('html', '')
             logger.info(f"📊 VLM recognized table structure")
         except Exception as e:

+ 4 - 1
ocr_tools/universal_doc_parser/core/pipeline_manager_v2.py

@@ -559,6 +559,7 @@ class EnhancedDocPipeline:
             output_dir=output_dir,
             basename=page_name,
             normalize_numbers=normalize_numbers,
+            debug_mode=self.debug_mode,
         )
         
         # 7. 按阅读顺序排序
@@ -823,6 +824,7 @@ class EnhancedDocPipeline:
         output_dir: Optional[str] = None,
         basename: Optional[str] = None,
         normalize_numbers: bool = True,
+        debug_mode: bool = False,
     ) -> tuple:
         """
         处理所有分类后的元素
@@ -915,7 +917,7 @@ class EnhancedDocPipeline:
                     table_img = CoordinateUtils.crop_region(detection_image, bbox)
                     
                     # 构造调试选项
-                    cls_debug_opts = {}
+                    cls_debug_opts = {'enabled': debug_mode}
                     if output_dir:
                         cls_debug_opts['output_dir'] = output_dir
                     if basename:
@@ -945,6 +947,7 @@ class EnhancedDocPipeline:
                         detection_image, item, scale, pre_matched_spans=spans, pdf_type=pdf_type,
                         output_dir=output_dir, basename=f"{basename}_{idx}",
                         normalize_numbers=normalize_numbers,
+                        debug_mode=debug_mode,
                     )
                     # 如果有线识别失败(返回空 HTML),fallback 到 VLM
                     if not element['content'].get('html') and not element['content'].get('cells'):

+ 14 - 7
ocr_tools/universal_doc_parser/main_v2.py

@@ -171,6 +171,13 @@ def process_single_input(
         # 创建流水线
         pipeline = _create_pipeline(streaming, str(config_path), str(output_dir))
         output_config = pipeline.config.get('output', {}) or _get_default_output_config(debug)
+
+        # 命令行 --debug 优先级最高:覆盖 yaml 中的所有 debug 设置
+        if debug:
+            pipeline.debug_mode = True
+            output_config['debug_mode'] = True
+            output_config.setdefault('save_layout_image', True)
+            output_config.setdefault('save_ocr_image', True)
         
         use_context = not streaming and hasattr(pipeline, '__enter__')
         if use_context:
@@ -420,8 +427,8 @@ if __name__ == "__main__":
             # "input": "/Users/zhch158/workspace/data/流水分析/湛_平安银行图.pdf",
             # "output_dir": "./output/湛_平安银行图/bank_statement_yusys_v3",
 
-            # "input": "/Users/zhch158/workspace/data/流水分析/张_微信图.pdf",
-            # "output_dir": "./output/张_微信图/bank_statement_yusys_v3",
+            "input": "/Users/zhch158/workspace/data/流水分析/张_微信图.pdf",
+            "output_dir": "./output/张_微信图/bank_statement_yusys_v4",
 
             # "input": "/Users/zhch158/workspace/data/流水分析/许_民生银行图.pdf",
             # "output_dir": "./output/许_民生银行图/bank_statement_yusys_v3",
@@ -461,8 +468,8 @@ if __name__ == "__main__":
 
             # "input": "/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/tests/提取自赤峰黄金2023年报.pdf",
             # "output_dir": "./output/提取自赤峰黄金2023年报/bank_statement_yusys_v3",
-            "input": "/Users/zhch158/workspace/data/流水分析/提取自赤峰黄金2023年报.pdf",
-            "output_dir": "./output/提取自赤峰黄金2023年报/bank_statement_yusys_v4",
+            # "input": "/Users/zhch158/workspace/data/流水分析/提取自赤峰黄金2023年报.pdf",
+            # "output_dir": "./output/提取自赤峰黄金2023年报/bank_statement_yusys_v4",
             # "output_dir": "/Users/zhch158/workspace/data/流水分析/提取自赤峰黄金2023年报/bank_statement_yusys_v4",
 
             # "input": "/Users/zhch158/workspace/data/流水分析/施博深.pdf",
@@ -487,11 +494,11 @@ if __name__ == "__main__":
             # "config": "./config/bank_statement_paddle_vl.yaml",
             
             # 场景
-            # "scene": "bank_statement",
-            "scene": "financial_report",
+            "scene": "bank_statement",
+            # "scene": "financial_report",
             
             # 页面范围(可选)
-            "pages": "11",  # 只处理前1页
+            "pages": "1",  # 只处理前1页
             # "pages": "1-3,5,7-10",  # 处理指定页面
             # "pages": "83-109",  # 处理指定页面
 

+ 17 - 0
ocr_utils/normalize_financial_numbers.py

@@ -27,6 +27,15 @@ def _normalize_amount_token(token: str) -> str:
     if core[0] in "+-":
         sign, core = core[0], core[1:]
 
+    # 条件1:去符号后为纯整数(无分隔符),无需处理
+    if core.isdigit():
+        return token
+
+    # 条件2:最后一个小数点之前无逗号/小数点(整数部分是纯数字)→ 已是正确小数格式,直接返回
+    dot_pos = core.rfind('.')
+    if dot_pos != -1 and core[:dot_pos].isdigit() and core[dot_pos + 1:].isdigit():
+        return token
+
     # 步骤 1:确定小数分隔符('.' 优先于 ',')
     dec_digits: str | None = None
     int_part = core
@@ -45,6 +54,14 @@ def _normalize_amount_token(token: str) -> str:
     if not int_digits or not int_digits.isdigit():
         return token  # 无法解析,保留原样
 
+    # 步骤 2.5:整数部分本身没有分隔符(如 1101,55 中的 1101)
+    # → 原数字未使用千分位,只修正小数点符号,不添加千分位
+    if int_part == int_digits:
+        result = sign + int_digits
+        if dec_digits is not None:
+            result += "." + dec_digits
+        return result
+
     # 步骤 3:重新做千分位分组
     n = len(int_digits)
     rem = n % 3 or 3