feat(v3): 人物模块重构 - 大模型结构化特征值替代 OpenCV 帧定位。prompt 增加 person_appearances(uid+7特征+action)并重写合并 prompt(稳定特征优先比对);gemini 透传特征字段;oracle_db events/people 加 person_appearances_json/features_json/display_uid + _merge_features;video_processor 去 cv2 改 ffprobe 校验、_store_result 聚合 uid 特征落 people、删缩略图/事件截图;person_service 聚合特征后基于特征文本 LLM 合并;api_gateway 删 3 个图接口;NAS 镜像+DDL 加新字段;fam-ui 特征卡替代头像、事件时间线人物特征块

This commit is contained in:
ericwyuan
2026-08-21 18:06:21 +08:00
parent 2ba3478291
commit ff01d14c79
12 changed files with 463 additions and 257 deletions

View File

@@ -12,7 +12,13 @@
"events": [ # 有用时间点 + 画面信息
{"timestamp": "2026-08-21 08:15:30", # 绝对北京时间event_start_time 推算)
"description": str,
"people": [str],
"people": [str], # 该时刻出现的人物标识
"person_appearances": [ # 该时刻每个人物的结构化特征
{"uid": str, # 与 people 数组里的标识一致
"features": { # 客观可见特征,看不清写 "unknown"
"gender, age_band, build, hair, clothing, face, distinguishing"
},
"action": str}],
"is_attention_event": bool}, ...],
"people_mentioned": [str], # 本视频出现的人物标识/真名
}

View File

@@ -295,7 +295,7 @@ class GeminiAdapter(BaseModelAdapter):
@staticmethod
def _normalize(result: dict) -> dict:
"""统一字段名frame_details -> events兼容旧结构"""
"""统一字段名frame_details -> events兼容旧结构,保留 person_appearances 特征"""
events = result.get('events')
if events is None and 'frame_details' in result:
events = []
@@ -308,13 +308,25 @@ class GeminiAdapter(BaseModelAdapter):
})
if events is None:
events = []
# 透传 events 内全部字段(含 person_appearances + features不丢特征
norm_events = []
for ev in events:
if not isinstance(ev, dict):
continue
item = dict(ev) # 保留原模型输出的所有字段(含 person_appearances
# 保证 people 字段为字符串数组
people = ev.get('people') or []
if isinstance(people, str):
people = [people]
item['people'] = [str(p) for p in people if p]
norm_events.append(item)
people = result.get('people_mentioned') or result.get('entities_json') or []
if isinstance(people, list) and people and isinstance(people[0], dict):
people = [p.get('person', '') for p in people]
people = [p.get('person', '') or p.get('uid', '') for p in people]
people = [p for p in people if p]
return {
"global_summary": result.get('global_summary', ''),
"events": events,
"events": norm_events,
"people_mentioned": people,
}