From 05c7a8b70a8289ba4c778f30d48f94816090575b Mon Sep 17 00:00:00 2001 From: ericwyuan Date: Sat, 29 Aug 2026 22:42:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(fam-edge):=20=E9=97=AD=E9=9B=86=E4=BA=BA?= =?UTF-8?q?=E7=89=A9=E8=AF=86=E5=88=AB=E6=96=B0=E5=A2=9E=E5=A5=B6=E5=A5=B6?= =?UTF-8?q?=EF=BC=88=E6=88=90=E5=B9=B4=E5=A5=B3=E6=80=A7=E6=8C=89=E5=B9=B4?= =?UTF-8?q?=E9=BE=84=E6=AE=B5=E5=8C=BA=E5=88=86=E5=AA=B3=E5=A6=87/?= =?UTF-8?q?=E5=A5=B6=E5=A5=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 家里从 4 人变成 5 人后,"媳妇=唯一成年女性"这条免费规则的前提被打破—— 人物管理页面已经能看到一条 label="奶奶" 但从未稳定命中的记录(只出现过 1 次,特征都没落库),说明现在系统大概率把奶奶也误判成了媳妇。 先用免费的年龄段字段区分(老年=奶奶,其余=媳妇),不引入额外的模型调用 (区别于爷爷/爸爸那种"两个成年男性区分不开必须靠视觉比对参考图"的方案) ——如果后续观察发现年龄段判断不稳定导致误判,再考虑改成视觉比对。 新增 3 个单元测试覆盖(中年→媳妇/老年→奶奶/年龄段缺失默认媳妇),fam-edge 全量 142/142 通过。已部署 Oracle 并重启验证。 Co-Authored-By: Claude Sonnet 5 --- fam-edge/src/fam_edge/person_identifier.py | 9 +++---- fam-edge/src/fam_edge/video_processor.py | 19 ++++++++++----- fam-edge/tests/test_video_processor.py | 28 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/fam-edge/src/fam_edge/person_identifier.py b/fam-edge/src/fam_edge/person_identifier.py index 8e4168a..cd01eea 100644 --- a/fam-edge/src/fam_edge/person_identifier.py +++ b/fam-edge/src/fam_edge/person_identifier.py @@ -1,14 +1,15 @@ """ -PersonIdentifier - 闭集人物识别(家里固定 4 个人:爷爷/爸爸/媳妇/汤圆) +PersonIdentifier - 闭集人物识别(家里固定 5 个人:爷爷/爸爸/媳妇/奶奶/汤圆) 背景(2026-08-22): 原来靠大模型每次视频分析自己编的"人物A/B/C"临时 uid + 一段 性别/年龄/衣着文字描述做跨视频合并,原理上就不可靠——文字描述会因光线/角度/换衣服 对不上,反复出现张冠李戴(用户原话:"现在的识别全是错的")。人脸向量方案也验证 过,家庭监控这种大广角/远距离/糊画面下同人内部相似度经常比不同人还低,此路不通。 -现在改成基于已知这个家庭只有 4 个固定成员的闭集规则: - - 汤圆(幼儿/儿童)、媳妇(唯一成年女性):Gemini 每次分析已经会标性别/年龄段, - 这两条命中率验证下来接近 100%,直接用,不需要额外模型调用。 +现在改成基于已知这个家庭固定成员的闭集规则: + - 汤圆(幼儿/儿童)、媳妇/奶奶(成年女性按年龄段区分:老年=奶奶,其余=媳妇, + 2026-08-29 新增):Gemini 每次分析已经会标性别/年龄段,命中率验证下来接近 + 100%,直接用,不需要额外模型调用。 - 爷爷、爸爸(两个成年男性,纯外观规则/人脸向量都区分不开):改用视觉大模型 "看图比对"——给几张已确认身份的参考图 + 待判断的截图,直接问模型这是谁。 实测 NVIDIA nemotron-omni 在留出测试集上 6/6 全对,Gemini flash-lite 7/8, diff --git a/fam-edge/src/fam_edge/video_processor.py b/fam-edge/src/fam_edge/video_processor.py index 3317dbb..8ba27ec 100644 --- a/fam-edge/src/fam_edge/video_processor.py +++ b/fam-edge/src/fam_edge/video_processor.py @@ -203,7 +203,7 @@ class VideoProcessor: self.motion_keep_audio = bool(seg.get('keep_audio', True)) self.motion_min_duration = float(seg.get('min_duration_sec', 1)) self.motion_grace_sec = int(seg.get('unfinished_grace_sec', 10)) - # 闭集人物识别(家里固定 4 人):汤圆/媳妇 用性别年龄规则;爷爷/爸爸 用 + # 闭集人物识别(家里固定 5 人):汤圆/媳妇/奶奶 用性别年龄规则;爷爷/爸爸 用 # person_identifier 视觉大模型比对,每片段每个 uid 只调一次 self.person_identifier = PersonIdentifier(self.config.get('person_identifier', {})) adapters = build_adapters(self.config.get('models', [])) @@ -526,12 +526,19 @@ class VideoProcessor: norm_events: List[Dict]) -> Dict[str, tuple]: """闭集人物识别:返回 {uid: (canonical_name, source)}。 - 汤圆(幼儿/儿童特征)、媳妇(唯一成年女性)、赤膊成年人(家里只有爸爸 - 会赤膊)靠 Gemini 已经产出的性别/年龄/衣着字段直接判断(source='rule'), - 验证过命中率接近 100%,不需要额外模型调用。爷爷/爸爸两个成年男性穿戴 - 整齐时外观规则/人脸向量都区分不开(验证过),改用视觉大模型比对参考图 + 汤圆(幼儿/儿童特征)、媳妇/奶奶(成年女性按年龄段区分:老年=奶奶, + 其余=媳妇)、赤膊成年人(家里只有爸爸会赤膊)靠 Gemini 已经产出的 + 性别/年龄/衣着字段直接判断(source='rule'),验证过命中率接近 + 100%,不需要额外模型调用。爷爷/爸爸两个成年男性穿戴整齐时外观规则/ + 人脸向量都区分不开(验证过),改用视觉大模型比对参考图 (source='auto_id'),每个 uid 只取本片段内最大 bbox 的一次出现判断 一次,不逐事件重复调用。 + + 2026-08-29 新增奶奶:家里从 4 人变成 5 人后,"媳妇=唯一成年女性" + 这条规则的前提被打破了(现在有两个成年女性)。先用免费的年龄段 + 字段区分(老年=奶奶,参考 Gemini 对"爷爷"的判断也是老年)——如果 + 后续观察发现年龄段判断不稳定导致误判,再考虑改成跟爷爷/爸爸一样 + 的视觉比对方案。 """ resolved: Dict[str, tuple] = {} for uid, feats in uid_features.items(): @@ -541,7 +548,7 @@ class VideoProcessor: if age_band in ('幼儿', '儿童'): resolved[uid] = ('汤圆', 'rule') elif gender == '女': - resolved[uid] = ('媳妇', 'rule') + resolved[uid] = ('奶奶' if age_band == '老年' else '媳妇', 'rule') elif gender == '男': if any(k in clothing for k in self._SHIRTLESS_KEYWORDS): resolved[uid] = ('爸爸', 'rule') diff --git a/fam-edge/tests/test_video_processor.py b/fam-edge/tests/test_video_processor.py index 60080b0..31ff6cb 100644 --- a/fam-edge/tests/test_video_processor.py +++ b/fam-edge/tests/test_video_processor.py @@ -111,6 +111,34 @@ def test_shirtless_child_still_resolves_to_child_not_dad(): assert resolved == {"人物A": ("汤圆", "rule")} +# ---------------------------------------------------------------------- +# 闭集人物识别:成年女性按年龄段区分媳妇/奶奶(2026-08-29 新增,家里从 4 人 +# 变成 5 人后,"媳妇=唯一成年女性"这条规则的前提被打破) +# ---------------------------------------------------------------------- + +def test_middle_aged_female_resolves_to_wife_without_vlm_call(): + vp = _bare_processor(_RaisingPersonIdentifier()) + resolved = vp._resolve_closed_set_identities( + 1, {"人物A": {"gender": "女", "age_band": "中年", "clothing": "粉色上衣"}}, []) + assert resolved == {"人物A": ("媳妇", "rule")} + + +def test_elderly_female_resolves_to_grandma_without_vlm_call(): + vp = _bare_processor(_RaisingPersonIdentifier()) + resolved = vp._resolve_closed_set_identities( + 1, {"人物A": {"gender": "女", "age_band": "老年", "clothing": "红色上衣"}}, []) + assert resolved == {"人物A": ("奶奶", "rule")} + + +def test_female_with_unknown_age_band_defaults_to_wife(): + """核心诉求: age_band 缺失/模型没给出明确判断时,不能因为不确定就放弃 + 识别——默认归到媳妇(原有行为),只有明确判断为"老年"才算奶奶。""" + vp = _bare_processor(_RaisingPersonIdentifier()) + resolved = vp._resolve_closed_set_identities( + 1, {"人物A": {"gender": "女", "age_band": "unknown", "clothing": ""}}, []) + assert resolved == {"人物A": ("媳妇", "rule")} + + def test_clothed_adult_male_still_falls_through_to_vlm(monkeypatch): """核心诉求: 没有赤膊关键词的正常穿戴场景,行为不变——照常走视觉大模型 比对(这里用假的 _best_crop_for_uid 避免真的需要 norm_events 数据)。"""