diff --git a/fam-ui/src/app.py b/fam-ui/src/app.py index c8567e6..a5ba6ac 100644 --- a/fam-ui/src/app.py +++ b/fam-ui/src/app.py @@ -810,7 +810,31 @@ elif page == "👤 人物管理": f'(与事件时间轴"出现人物"统计一致)', unsafe_allow_html=True) + _meta_cache = {} + + def _load_meta(event_id): + """读 event 的 meta.json(frame_index -> face_count),缺失返回空 dict""" + if event_id not in _meta_cache: + mpath = os.path.join(FRAME_DIR, f'event_{event_id}', 'meta.json') + try: + with open(mpath) as f: + _meta_cache[event_id] = json.load(f) or {} + except (OSError, ValueError): + _meta_cache[event_id] = {} + return _meta_cache[event_id] + def person_photo(frames): + """头像优先挑有人脸的帧(face_count 最大),其次按时间序第一张""" + best, best_faces = None, -1 + for eid, fidx, _ in frames: + meta = _load_meta(eid) + faces = int(meta.get(str(fidx), 0) or 0) + if faces > best_faces: + b64 = load_frame_b64(eid, fidx) + if b64: + best, best_faces = b64, faces + if best: + return best for eid, fidx, _ in frames: b64 = load_frame_b64(eid, fidx) if b64: @@ -835,6 +859,25 @@ elif page == "👤 人物管理": except Exception as e: st.error(f"异常: {e}") + def do_merge(src_key, tgt_key): + """把 src 并入 tgt(判断是同一人时)""" + try: + resp = requests.post( + f"{_core_url}/api/member/merge", + json={"source": src_key, "target": tgt_key}, + timeout=15) + if resp.status_code == 200: + result = resp.json() + st.success( + f"已合并:{src_key} → {result.get('target', tgt_key)}," + f"明细 {result.get('updated_event_details_count', 0)} 条 / " + f"事件 {result.get('updated_monitor_events_count', 0)} 条") + st.rerun() + else: + st.error(f"合并失败: {resp.status_code} {resp.text[:150]}") + except Exception as e: + st.error(f"合并异常: {e}") + if not person_frames: st.markdown( '