fix: 科目名称补位 + 摘要过滤科目名称

- 科目代码没名称时,从下方200px内补取含中文的文本作为名称
- 摘要候选排除以-/-开头的会计科目文本
- 摘要候选排除与代码名称重复的候选
- 名称补位排除含其他科目代码的行(防误匹配)
This commit is contained in:
xiaoxue_admin 2026-06-22 12:11:58 +08:00
parent da0b5ff0b8
commit 5f3df306c2
1 changed files with 59 additions and 0 deletions

View File

@ -178,6 +178,41 @@ class K3VoucherParser:
if not codes:
return self._parse_entries_fallback()
# === 4.3 科目名称补位:如果代码没有名称,从附近行找 ===
for code in codes:
if code['name']: # 已有名称则跳过
continue
# 在当前代码下方附近找包含中文的文本(排除含其他科目代码的行)
name_candidates = []
for text, conf, cx, cy, box in self.lines:
if conf < 0.5:
continue
if not re.search(r'[\u4e00-\u9fff]', text): # 必须含中文字
continue
if any(k in text for k in skip_keywords + ['合计', '亿', '', '', '', '']):
continue
if re.match(r'^[\d.\-,]+$', text.strip()):
continue
if len(text.strip()) < 3:
continue
if cx < 400 or cx > 1300: # 名称列通常在x=400-1300
continue
if re.search(r'(公司|有限|集团|企业)', text): # 排除公司名
continue
if text.strip() in ('未到账', '已到账', '未达', '已达'):
continue
# 排除包含其他科目代码的行(如 "6603.02-财务费用"
if re.search(r'\d{3,}\.\d', text):
continue
dist_y = cy - code['y_pos']
if 0 < dist_y < 200: # 代码下方200px以内
name_candidates.append((text, dist_y, cx, cy))
if name_candidates:
# 取y距离最近的
name_candidates.sort(key=lambda x: (x[1], -x[2]))
code['name'] = name_candidates[0][0]
code['y_pos'] = max(code['y_pos'], name_candidates[0][3]) # 更新y为名称位置
# === 4.5 摘要提取:从剩余 OCR 文字中找摘要 ===
code_y_min = min(c['y_pos'] for c in codes)
@ -218,6 +253,13 @@ class K3VoucherParser:
continue
if text.strip() in ('未到账', '已到账', '未达', '已达'):
continue
# 过滤垃圾摘要:纯字母数字/太短/无中文
if not re.search(r'[\u4e00-\u9fff]', text): # 没有中文字,排除
continue
if len(text.strip()) < 4: # 太短
continue
if conf < 0.6: # 提高置信度要求
continue
# 过滤非摘要的日期/期号文本
if re.match(r'\d{3}年第\d+期', text.strip()):
continue
@ -233,6 +275,23 @@ class K3VoucherParser:
# 过滤包含"公司"、"物业"、"有限"等公司相关词
if re.search(r'(公司|有限|物业|集团|企业)', text):
continue
# 过滤科目名称文本:以 -/ 开头的会计科目
if re.match(r'^[-][^\d]', text.strip()) and any(
kw in text for kw in ['银行', '货币', '账款', '费用', '收入', '成本',
'应收', '应付', '预付', '预收', '其他', '库存',
'管理', '财务', '手续费', '押金', '资本']
):
continue
# 过滤纯科目名称(在代码附近找到的科目名不应作为摘要)
if len(text.strip()) < 20: # 短文本更容易是科目名
is_near_name = False
for c in codes:
if c['name'] and c['name'] in text:
if abs(c['y_pos'] - cy) < 100:
is_near_name = True
break
if is_near_name:
continue
if cy < desc_upper_bound:
continue
# 去重:跳过 y 距离 < 30 的重复文本