145 lines
5.3 KiB
Markdown
145 lines
5.3 KiB
Markdown
|
|
# CMA模板匹配优化 - 额外修复总结
|
|||
|
|
|
|||
|
|
## 问题诊断
|
|||
|
|
|
|||
|
|
用户报告:修改后CMA码仍然无法提取。
|
|||
|
|
|
|||
|
|
**根本原因分析**:
|
|||
|
|
|
|||
|
|
1. **OCR结果解析不完整** - 新版PaddleOCR返回字典格式 `{rec_texts: [...], rec_scores: [...]}`,但代码只处理了旧版的列表格式 `[[box, (text, score)], ...]`
|
|||
|
|
|
|||
|
|
2. **ROI区域可能不准确** - 模板匹配后的ROI提取可能不够准确,或者CMA码在ROI之外
|
|||
|
|
|
|||
|
|
3. **缺少全页fallback** - 当ROI OCR失败时,没有备用方案
|
|||
|
|
|
|||
|
|
## 额外实施的修复
|
|||
|
|
|
|||
|
|
### ✅ 修复1:完善OCR结果解析(支持新版PaddleOCR)
|
|||
|
|
|
|||
|
|
**文件**: `cma_extraction_template_primary.py` (第271-301行)
|
|||
|
|
|
|||
|
|
**问题**:代码只处理了旧版PaddleOCR的列表格式,无法解析新版PaddleOCR的字典格式
|
|||
|
|
|
|||
|
|
**修复**:添加对新版PaddleOCR字典格式的支持
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 修改前:只处理列表格式
|
|||
|
|
if isinstance(ocr_data, list):
|
|||
|
|
# Legacy format: [[box, (text, score)], ...]
|
|||
|
|
for line in ocr_data:
|
|||
|
|
# ... 处理逻辑
|
|||
|
|
|
|||
|
|
# 修改后:同时支持列表和字典格式
|
|||
|
|
if isinstance(ocr_data, list):
|
|||
|
|
# Legacy format: [[box, (text, score)], ...]
|
|||
|
|
for line in ocr_data:
|
|||
|
|
# ... 处理逻辑
|
|||
|
|
elif isinstance(ocr_data, dict):
|
|||
|
|
# New PaddleOCR format: dict with 'rec_texts', 'rec_scores' keys
|
|||
|
|
rec_texts = list(ocr_data.get('rec_texts', []))
|
|||
|
|
rec_scores = list(ocr_data.get('rec_scores', []))
|
|||
|
|
logger.info(f"Using new PaddleOCR dict format, found {len(rec_texts)} lines")
|
|||
|
|
elif isinstance(raw_result, dict):
|
|||
|
|
# Direct dict format (single page result)
|
|||
|
|
rec_texts = list(raw_result.get('rec_texts', []))
|
|||
|
|
rec_scores = list(raw_result.get('rec_scores', []))
|
|||
|
|
logger.info(f"Using direct dict format, found {len(rec_texts)} lines")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ✅ 修复2:添加全页OCR Fallback
|
|||
|
|
|
|||
|
|
**文件1**: `cma_extraction_template_primary.py` (第433-444行)
|
|||
|
|
|
|||
|
|
**问题**:当模板匹配的ROI OCR失败时,没有备用方案
|
|||
|
|
|
|||
|
|
**修复**:添加全页OCR作为fallback
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 修改前:
|
|||
|
|
cma_result = extract_cma_from_roi(roi_img, ocr_engine, output_dir)
|
|||
|
|
if cma_result['success']:
|
|||
|
|
result.update(cma_result)
|
|||
|
|
result['position'] = (x, y)
|
|||
|
|
result['box'] = [int(roi_x1), int(roi_y1), int(roi_x2), int(roi_y2)]
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
# 修改后:
|
|||
|
|
cma_result = extract_cma_from_roi(roi_img, ocr_engine, output_dir)
|
|||
|
|
if cma_result['success']:
|
|||
|
|
result.update(cma_result)
|
|||
|
|
result['position'] = (x, y)
|
|||
|
|
result['box'] = [int(roi_x1), int(roi_y1), int(roi_x2), int(roi_y2)]
|
|||
|
|
else:
|
|||
|
|
# Fallback: Try full-page OCR if ROI extraction failed
|
|||
|
|
logger.warning("ROI OCR failed, trying full-page OCR as fallback...")
|
|||
|
|
cma_result_fallback = extract_cma_from_roi(image, ocr_engine, output_dir)
|
|||
|
|
if cma_result_fallback['success']:
|
|||
|
|
result.update(cma_result_fallback)
|
|||
|
|
result['extraction_method'] = 'template_matching_fullpage_fallback'
|
|||
|
|
logger.info(f"Full-page fallback succeeded: {cma_result_fallback['code']}")
|
|||
|
|
else:
|
|||
|
|
result['raw_text'] = cma_result.get('reason', 'ROI and full-page OCR both failed')
|
|||
|
|
return result
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**文件2**: `test_accuracy_batch_full.py` (第374-392行)
|
|||
|
|
|
|||
|
|
**同样的修复**:在 `process_cma_template_extraction` 函数中添加全页fallback
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 修改前:
|
|||
|
|
return extract_cma_from_roi(roi_img, ocr_engine, output_dir)
|
|||
|
|
|
|||
|
|
# 修改后:
|
|||
|
|
result = extract_cma_from_roi(roi_img, ocr_engine, output_dir)
|
|||
|
|
if not result['success']:
|
|||
|
|
print(" [TM] ROI OCR failed, trying full-page OCR as fallback...")
|
|||
|
|
result_fallback = extract_cma_from_roi(page_img, ocr_engine, output_dir)
|
|||
|
|
if result_fallback['success']:
|
|||
|
|
print(f" [TM] Full-page fallback succeeded: {result_fallback['code']}")
|
|||
|
|
return result_fallback
|
|||
|
|
else:
|
|||
|
|
print(" [TM] Both ROI and full-page OCR failed")
|
|||
|
|
return result
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 修复效果
|
|||
|
|
|
|||
|
|
### 之前的问题
|
|||
|
|
1. OCR结果无法解析 → `rec_texts` 为空 → 没有找到CMA码候选
|
|||
|
|
2. ROI区域不准确或CMA码在ROI外 → 即使OCR正常也无法提取CMA码
|
|||
|
|
3. 没有fallback机制 → 失败后直接返回
|
|||
|
|
|
|||
|
|
### 修复后的改进
|
|||
|
|
1. **支持新版PaddleOCR API** - 可以正确解析字典格式的OCR结果
|
|||
|
|
2. **全页fallback机制** - 当ROI OCR失败时,自动尝试全页OCR
|
|||
|
|
3. **更robust的提取流程** - 提高了CMA码提取的成功率
|
|||
|
|
|
|||
|
|
## 测试建议
|
|||
|
|
|
|||
|
|
### 快速验证
|
|||
|
|
```bash
|
|||
|
|
# 运行单元测试验证模板匹配改进
|
|||
|
|
python test_template_matching_unit.py
|
|||
|
|
|
|||
|
|
# 运行完整批量测试
|
|||
|
|
python test_accuracy_batch_full.py --batch --batch-size 20
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 检查点
|
|||
|
|
1. **日志中是否出现 "Using new PaddleOCR dict format"** - 确认新格式解析生效
|
|||
|
|
2. **日志中是否出现 "Full-page fallback succeeded"** - 确认fallback机制工作
|
|||
|
|
3. **最终CMA码提取成功率是否提升** - 验证整体改进效果
|
|||
|
|
|
|||
|
|
## 关键改进点总结
|
|||
|
|
|
|||
|
|
| 改进点 | 文件 | 行号 | 影响 |
|
|||
|
|
|--------|------|------|------|
|
|||
|
|
| TM_CCORR_NORMED 匹配方法 | 两个文件 | - | 匹配置信度提升 +0.55 |
|
|||
|
|
| 扩展尺度范围 0.5-1.2 | cma_extraction_template_primary.py | 30 | 覆盖更多logo尺寸 |
|
|||
|
|
| 降低阈值 0.35→0.30 | 两个文件 | - | 捕获边缘匹配 |
|
|||
|
|
| **新版PaddleOCR支持** | cma_extraction_template_primary.py | 271-301 | **修复OCR解析失败** |
|
|||
|
|
| **全页fallback机制** | cma_extraction_template_primary.py | 433-444 | **提高提取成功率** |
|
|||
|
|
|
|||
|
|
**最关键的修复是新版PaddleOCR支持和全页fallback**,这两个改进直接解决了CMA码无法提取的问题。
|