diff --git a/apps/worker/worker_app/tasks/viral_video.py b/apps/worker/worker_app/tasks/viral_video.py index 678c3b8d0..58272d7eb 100644 --- a/apps/worker/worker_app/tasks/viral_video.py +++ b/apps/worker/worker_app/tasks/viral_video.py @@ -444,6 +444,121 @@ def _analyze_single_image( _parts.append(f"{_skin}肤色") _parts.append(f"{_face}脸型") _parts.append(f"身着{_outfit}") + if _pose != "无法判断": + _parts.append(f"姿态{_pose}") + if _expr != "无法判断": + _parts.append(f"表情{_expr}") + else: + _parts.append("表情自然") + # #2186: 智能回填——VLM有时省略hair/outfit等外貌属性,但product.name/features/colors里已有相关信息 + # 从product名字和features中提取服装关键词回填outfit + if _outfit in ("日常服装", "无法判断"): + for _ppn in product_nodes: + _pn = (_ppn.get("attrs") or {}).get("name", "") or "" + _pf = (_ppn.get("attrs") or {}).get("features", "") or "" + _ptxt = _pn + " " + _pf + # 服装关键词识别(常见上装/下装/裙装/套装) + _cloth_kws = [ + "衬衫", + "T恤", + "毛衣", + "针织衫", + "卫衣", + "外套", + "西装", + "夹克", + "风衣", + "大衣", + "羽绒服", + "马甲", + "背心", + "连衣裙", + "半身裙", + "短裙", + "长裙", + "牛仔裤", + "休闲裤", + "西裤", + "运动裤", + "短裤", + "旗袍", + "汉服", + "制服", + "polo衫", + "POLO衫", + "针织", + "毛衫", + "开衫", + "帽衫", + "皮夹克", + "皮衣", + ] + for _ckw in _cloth_kws: + if _ckw in _ptxt: + # 提取含关键词的短语(关键词前后4字) + _ci = _ptxt.find(_ckw) + _start = max(0, _ci - 6) + _end = min(len(_ptxt), _ci + len(_ckw) + 2) + _outfit_extract = _ptxt[_start:_end].strip(" ,,。.、") + # 清理掉品牌名/产品名词的干扰(只取服装描述部分) + _outfit_extract = re.sub( + r"^[\w一-龥]{0,2}(牌|品牌|的|款|女|男|新|装|大|小|长|短|厚|薄)", + "", + _outfit_extract, + ) + if len(_outfit_extract) >= 2: + _outfit = _outfit_extract + break + if _outfit not in ("日常服装", "无法判断"): + break + # 从color标签中提取头发颜色回填hair + if _hair in ("自然发型", "无法判断"): + _hair_color = "" + _color_nodes = [n for n in nodes if n["tag"] == "color"] + _hair_kws_map = { + "黑": "黑色", + "棕": "棕色", + "金": "金色", + "栗": "栗色", + "红": "红色", + "白": "白色", + "灰": "灰色", + "蓝": "蓝色", + "黄": "黄色", + "紫": "紫色", + } + for _cn in _color_nodes: + _cname = (_cn.get("attrs") or {}).get("name", "") or "" + # 小占比颜色更可能是发色(非主色的小面积色),且名称含头发/黑/棕/金等 + _ccov = 0.0 + try: + _ccov = float((_cn.get("attrs") or {}).get("coverage", "0") or 0) + except Exception: + pass + for _hk, _hv in _hair_kws_map.items(): + if _hk in _cname and _ccov < 0.3: + _hair_color = _hv + break + if _hair_color: + break + if _hair_color: + _hair = f"{_hair_color}头发" + else: + _hair = "自然发型" + # 重新拼装_parts(回填后) + _parts = [] + if _gender != "无法判断": + _g = _gender + ("性" if not _gender.endswith("性") else "") + _parts.append(_g) + else: + _parts.append("成年人") + if _age != "无法判断": + _parts.append(_age) + _parts.append("人物") + _parts.append(_hair) + _parts.append(f"{_skin}肤色") + _parts.append(f"{_face}脸型") + _parts.append(f"身着{_outfit}") if _pose != "无法判断": _parts.append(f"姿态{_pose}") if _expr != "无法判断": @@ -452,7 +567,7 @@ def _analyze_single_image( _parts.append("表情自然") portrait_prompt = ",".join(_parts) logger.info( - "[爆款视频] 图片 #%d 解析: count=%d gender=%s age=%s hair=%s skin=%s face=%s outfit=%s pose=%s expr=%s → %s", + "[爆款视频] 图片 #%d 解析(回填后): count=%d gender=%s age=%s hair=%s skin=%s face=%s outfit=%s pose=%s expr=%s → %s", idx, _count, _gender, diff --git a/packages/application/viral_video/prompts.py b/packages/application/viral_video/prompts.py index 4e41733a9..bf131b1e1 100644 --- a/packages/application/viral_video/prompts.py +++ b/packages/application/viral_video/prompts.py @@ -65,7 +65,10 @@ hair/skin_tone/face_shape/outfit四项绝对禁止填“无法判断”,必须 - outfit:必须描述可见穿着,如“米色翻领衬衫”“白色T恤”“黑色连衣裙” 即使局部被遮挡也要根据可见部分合理推断;确实看不清时按最接近的直观印象描述。 -其他非人物属性看不到或无法判断时填“无法判断”,布尔值填false,不要留空标签。""" +其他非人物属性看不到或无法判断时填“无法判断”,布尔值填false,不要留空标签。 + +【有人物场景输出参考(女性手持商品示例,必须写全10个属性,禁止省略)】 +""" _IMAGE_ANALYSIS_USER = """请分析以下商品图片,共 {image_count} 张。 所属行业:{industry}