File size: 4,967 Bytes
aa975a2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | # parser.py
# -*- coding: utf-8 -*-
"""
Step5 解析器(稳健版)
- 兼容中文全角/半角、圆括号/方括号、中文/英文逗号
- 抽取:增强后的指令 + 逐帧 bbox(与采样 t 序列对齐)
- 校验:0<=x1<x2<=1, 0<=y1<y2<=1;不合法则记为 None
"""
import re
from typing import Dict, List, Tuple
CN2NUM = {
"一": 1, "二": 2, "三": 3, "四": 4, "五": 5,
"六": 6, "七": 7, "八": 8, "九": 9, "十": 10
}
# 全角到半角的基础归一化(冒号/逗号/括号等)
FULL2HALF_MAP = str.maketrans({
"(": "(", ")": ")", "【": "[", "】": "]", "[": "[", "]": "]",
",": ",", ":": ":", ";": ";", "。": ".", "、": ",", "|": "|",
})
def _normalize_text(s: str) -> str:
s = (s or "").strip().replace("\u00A0", " ").replace("\t", " ")
# 去多空格
s = re.sub(r"[ ]{2,}", " ", s)
return s.translate(FULL2HALF_MAP)
def _extract_enhanced_instruction(step5_line: str) -> str:
"""
支持两种写法:
1) 修改后的编辑指令:{ ... },任务一boxing序列:
2) 修改后的编辑指令:...,任务一boxing序列:
同时兼容中文/英文逗号,冒号,花括号可选
"""
# 先尝试「有花括号」版本
m = re.search(r"修改后的编辑指令[::]\s*\{(?P<inst>.+?)\}\s*[,,]", step5_line)
if m:
return m.group("inst").strip()
# 再尝试「无花括号」版本:取到 “任务一boxing序列:”/“任务1boxing序列:” 之前
m = re.search(
r"修改后的编辑指令[::]\s*(?P<inst>.+?)\s*[,,]\s*任务\s*[一二三四五六七八九十\d]+\s*boxing序列[::]",
step5_line
)
return m.group("inst").strip() if m else ""
def _task_key_to_idx(tok: str) -> int:
# "任务一"/"任务1"
m = re.search(r"任务\s*([一二三四五六七八九十\d]+)", tok)
if not m:
return 1
key = m.group(1)
return CN2NUM.get(key, int(key) if key.isdigit() else 1)
def parse_step5(step5_line: str, t_list: List[int]) -> Tuple[str, Dict[str, List[Dict]]]:
"""
返回 (enhanced_instruction, tasks_json)
tasks_json 结构:
{
"任务一": [{"t": t0, "bbox":[x1,y1,x2,y2] or None}, ...],
...
}
"""
s = _normalize_text(step5_line)
enhanced = _extract_enhanced_instruction(s)
# 按任务切分:“…任务一boxing序列:[...],任务二boxing序列:[...]…”
# ⚠️ 兼容半角/全角冒号(归一化后为半角 ":",但正则也允许全角)
parts = re.split(r"(任务[一二三四五六七八九十\d]+\s*boxing序列[::])", s)
tasks_json: Dict[str, List[Dict]] = {}
def parse_seq(seq_txt: str) -> List[Dict]:
"""
解析形如:
[(t=0,[(0.64,0.68,0.71,0.78)]);(t=1,[(0.64,0.68,0.71,0.78)])]
允许 () 或 [] 包围 bbox,允许中文逗号/空格。
"""
seq_txt = (seq_txt or "").strip()
# 拆帧项,分隔符 ; 或 ;(也容忍多余空格)
items = re.split(r"[;;]+", seq_txt.strip("[] "))
out = []
for it in items:
it = it.strip()
if not it:
continue
# 抓 t 值
mt = re.search(r"t\s*=\s*(\d+)", it)
if not mt:
continue
t = int(mt.group(1))
# 抓 bbox:先定位一对括号内容,兼容 [] 或 ()
mb = re.search(r"\[\s*([\d\.\s,,]+)\s*\]|\(\s*([\d\.\s,,]+)\s*\)", it)
if not mb:
out.append({"t": t, "bbox": None})
continue
nums_str = (mb.group(1) or mb.group(2) or "").replace(",", ",")
nums = [x for x in re.split(r"\s*,\s*", nums_str) if x]
if len(nums) != 4:
out.append({"t": t, "bbox": None})
continue
try:
x1, y1, x2, y2 = map(float, nums)
except Exception:
out.append({"t": t, "bbox": None})
continue
ok = (0.0 <= x1 < x2 <= 1.0) and (0.0 <= y1 < y2 <= 1.0)
out.append({"t": t, "bbox": [x1, y1, x2, y2]} if ok else {"t": t, "bbox": None})
return out
# 从第二个元素开始,成对读取:"任务Xboxing序列:" + 下一段(括号体)
for i in range(1, len(parts), 2):
head = parts[i]
body = parts[i + 1] if i + 1 < len(parts) else ""
idx = _task_key_to_idx(head)
key = f"任务{idx}"
tasks_json[key] = parse_seq(body)
# 与 t_list 对齐(以采样顺序为准),缺失帧用 bbox=None 占位
def align(seq: List[Dict]) -> List[Dict]:
by_t = {e["t"]: e for e in seq}
aligned = []
for t in t_list:
e = by_t.get(t)
aligned.append(e if e else {"t": t, "bbox": None})
return aligned
tasks_json = {k: align(v) for k, v in tasks_json.items()}
return enhanced, tasks_json
|