feat: 强化多视角图片一致性 + 修复下载逻辑 + 技术文档

- 新增品类专属背面/侧面描述(BACK_VIEW_HINTS/SIDE_VIEW_HINTS)
- 强化一致性前缀策略,按视角定制相机位置描述
- 更新视角映射提示词为纯摄影术语
- 修复前端下载逻辑:改用fetch直接下载当前视角图片
- HTTPS改HTTP修复外网URL访问
- 新增多视角一致性与3D视频生成技术文档
This commit is contained in:
2026-03-28 19:51:08 +08:00
parent 1d94ec114a
commit 2ef126e445
8 changed files with 942 additions and 286 deletions

View File

@@ -3,8 +3,11 @@
处理设计相关的业务逻辑,支持 AI 多视角生图 + mock 降级
"""
import os
import uuid
import logging
from typing import List, Optional, Tuple
import httpx
from sqlalchemy.orm import Session
from sqlalchemy import desc
@@ -150,8 +153,26 @@ async def _generate_ai_images(
# 调用 AI 生图
# 后续视角传入 seedKolors或参考图 URLSeedream保持一致性
ref_url = first_remote_url if idx > 0 else None
# 后续视角在提示词前加一致性约束前缀(根据视角名称定制)
final_prompt = prompt_text
if idx > 0:
# 根据视角名称生成更具体的一致性前缀
view_angle_map = {
"正面图": "moving the camera to face the object directly from the front",
"侧面图": "moving the camera 90 degrees to the left side of the object",
"背面图": "moving the camera 180 degrees to see the reverse/back side of the object",
}
angle_desc = view_angle_map.get(view_name, "changing the camera angle")
consistency_prefix = (
f"Photograph the EXACT SAME jade object from the reference image, {angle_desc}. "
"The object does NOT move or change - only the camera position changes. "
"The shape, size, color, material texture, and all physical features must remain IDENTICAL. "
)
final_prompt = consistency_prefix + prompt_text
remote_url, returned_seed = await ai_generator.generate_image(
prompt_text, model, seed=shared_seed, ref_image_url=ref_url
final_prompt, model, seed=shared_seed, ref_image_url=ref_url
)
# 第一张图保存信息供后续视角复用
@@ -161,8 +182,9 @@ async def _generate_ai_images(
shared_seed = returned_seed
logger.info(f"多视角生图: seed={shared_seed}, ref_url={remote_url[:60]}...")
# 直接使用远程 URL不下载到本地节省服务器存储空间
image_url = remote_url
# 下载到本地持久化存储远程URL是临时链接会过期失效
image_url = await _download_image_to_local(remote_url, design.id, idx)
logger.info(f"视角[{view_name}] 已下载到本地: {image_url}")
# 创建 DesignImage 记录
design_image = DesignImage(
@@ -182,6 +204,34 @@ async def _generate_ai_images(
design.status = "completed"
async def _download_image_to_local(remote_url: str, design_id: int, idx: int) -> str:
"""
下载远程 AI 生成的图片到本地 uploads/designs/ 目录
第三方AI服务生成的图片URL是临时链接会过期失效必须下载到本地持久化
Returns:
本地图片 URL 路径,如 /uploads/designs/123_0_xxxx.png
"""
designs_dir = os.path.join(settings.UPLOAD_DIR, "designs")
os.makedirs(designs_dir, exist_ok=True)
filename = f"{design_id}_{idx}_{uuid.uuid4().hex[:8]}.png"
local_path = os.path.join(designs_dir, filename)
try:
async with httpx.AsyncClient(timeout=60, follow_redirects=True) as client:
resp = await client.get(remote_url)
resp.raise_for_status()
with open(local_path, "wb") as f:
f.write(resp.content)
logger.info(f"图片下载完成: {len(resp.content)} 字节 -> {local_path}")
except Exception as e:
logger.error(f"图片下载失败回退使用远程URL: {e}")
return remote_url # 下载失败时回退使用远程URL
return f"/uploads/designs/{filename}"
def _generate_mock_fallback(
db: Session,
design: Design,