Files
yingqing/core/backend/apps/ai/model_library.py
T
seaislee1209andClaude Opus 4.8 016c859004 feat(core): 人物正面氛围图按统一模板后端拼装(全身/纯色背景)
新增 build_person_frontal_prompt:电商真人模特,氛围正面全身照,<脚本提取/用户描述>,自然妆容,柔和影棚光,真实质感,单人,纯色背景。管线人物立绘(generate_base_asset kind=person)与模特库 seed(model_library)都改走它,不再把原始描述裸丢生图、不再半身/简洁背景。三视图 THREE_VIEW_PROMPT 保持不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 22:17:47 +08:00

93 lines
4.0 KiB
Python

"""模特库生成:用 gpt-image-2 预生成电商真人模特。
流程(与用户定的 SOP 一致):
1) 先出 9:16 竖屏氛围正面图(image_generation);
2) 以正面图为参考,出 16:9 白底三视图(image_edit,提示词锁角色一致性)。
两张都落 TOS,存为 person 类 Asset(metadata.kind="model"),进模特库/资产库。
可被管理命令(seed_demo_models)或后端业务复用。模型走默认 image 模型(当前 = tokenssr:gpt-image-2)。
"""
from __future__ import annotations
import uuid
from io import BytesIO
from apps.ai.models import ModelConfig
from apps.ai.services import _asset_preview_url, build_person_frontal_prompt, build_provider, get_default_model
from apps.assets.models import Asset, AssetFile
from apps.assets.storage import TosStorage
THREE_VIEW_PROMPT = (
"参考图1角色,生成角色三视图,从左往右依次为:胸像特写,全身正面,全身侧面,全身背面,白色背景"
)
def _store_model_asset(*, team, user, media: str, name: str, brief: str, view: str) -> Asset:
"""把生成的图片(url/base64)落 TOS,存为 person 类模特资产(非项目维度)。"""
from apps.ai.providers import VolcanoArkProvider # media_to_bytes 复用
fileobj, content_type = VolcanoArkProvider.media_to_bytes(media)
suffix = ".png"
if "jpeg" in (content_type or ""):
suffix = ".jpg"
elif "webp" in (content_type or ""):
suffix = ".webp"
asset_id = uuid.uuid4()
object_key = f"teams/{team.id}/models/{asset_id}{suffix}"
raw = fileobj.getvalue()
stored = TosStorage().upload_fileobj(fileobj=BytesIO(raw), object_key=object_key, content_type=content_type or "image/png")
asset = Asset.objects.create(
id=asset_id,
team=team,
created_by=user,
name=name,
asset_type=Asset.Type.IMAGE,
source=Asset.Source.AI_GENERATED,
category=Asset.Category.PERSON,
metadata={"kind": "model", "brief": brief, "view": view},
)
AssetFile.objects.create(
asset=asset,
object_key=stored.object_key,
bucket=stored.bucket,
content_type=stored.content_type,
size_bytes=stored.size_bytes,
is_primary=True,
)
return asset
def generate_model(*, team, user, brief: str, name: str | None = None) -> dict:
"""生成一个电商模特(9:16 氛围正面图 + 16:9 白底三视图)。返回 {"frontal":Asset,"three_view":Asset}。"""
model_config = get_default_model(ModelConfig.Capability.IMAGE)
if model_config is None:
raise ValueError("没有可用的图像模型(image capability)")
provider = build_provider(model_config)
if not hasattr(provider, "image_edit"):
raise ValueError(f"当前图像模型 {model_config.provider.name}:{model_config.name} 不支持参考图三视图(image_edit)")
label = name or brief[:16]
# 1) 9:16 氛围正面图(画幅由 size 控制;提示词用统一的人物正面模板:全身 / 纯色背景)
frontal_prompt = build_person_frontal_prompt(brief)
resp = provider.image_generation(model=model_config.name, prompt=frontal_prompt, size="1024x1536")
frontal = _store_model_asset(
team=team, user=user, media=provider.extract_first_media_url(resp),
name=f"{label}·正面氛围", brief=brief, view="frontal",
)
# 2) 16:9 白底三视图(以正面图为参考,锁角色一致性)
frontal_url = _asset_preview_url(frontal)
resp2 = provider.image_edit(model=model_config.name, prompt=THREE_VIEW_PROMPT, images=[frontal_url], size="1536x1024")
three_view = _store_model_asset(
team=team, user=user, media=provider.extract_first_media_url(resp2),
name=f"{label}·三视图", brief=brief, view="three_view",
)
return {"frontal": frontal, "three_view": three_view}
# 演示用默认模特画像(电商常用人设)
DEFAULT_MODEL_BRIEFS = [
"26岁都市白领女性,知性温柔,黑色及肩直发,米色针织衫",
"30岁阳光运动男性,短发,健康肤色,浅灰色休闲卫衣",
"22岁元气学生女生,马尾,清透妆,浅蓝色衬衫",
]