From 6480d69c6383f2b1614397bae533e70c273c7d96 Mon Sep 17 00:00:00 2001 From: kang Date: Thu, 14 May 2026 06:28:04 +0800 Subject: [PATCH] auto-save 2026-05-14 06:27 (~4) --- .memory/worklog.json | 27 +++++++++++++-------------- api/main.py | 33 +++++++++++++++++++++++++++------ web/components/lightbox.tsx | 23 +++++++++-------------- web/components/nodes/index.tsx | 2 +- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index aed135a..71299f2 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,19 +1,5 @@ { "entries": [ - { - "files_changed": 2, - "hash": "e6b8615", - "message": "auto-save 2026-05-12 17:23 (~2)", - "ts": "2026-05-12T17:23:21+08:00", - "type": "commit" - }, - { - "files_changed": 6, - "hash": "6a9abea", - "message": "auto-save 2026-05-12 17:28 (~6)", - "ts": "2026-05-12T17:28:54+08:00", - "type": "commit" - }, { "files_changed": 1, "hash": "f3ec026", @@ -3351,6 +3337,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 2 项未提交变更 · 最近提交:auto-save 2026-05-14 06:16 (~5)", "files_changed": 2 + }, + { + "ts": "2026-05-14T06:22:32+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 06:22 (~2)", + "hash": "2d36ada", + "files_changed": 2 + }, + { + "ts": "2026-05-13T22:23:14Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 06:22 (~2)", + "files_changed": 1 } ] } diff --git a/api/main.py b/api/main.py index 6fcd747..6bfccac 100644 --- a/api/main.py +++ b/api/main.py @@ -448,7 +448,7 @@ def run(cmd: list[str], cwd: Path | None = None) -> str: # ---- 启发式选帧工具 ---- import imagehash import numpy as np -from PIL import Image, ImageEnhance, ImageFilter, ImageOps +from PIL import Image, ImageChops, ImageEnhance, ImageFilter, ImageOps def _sharpness_from_gray(g: np.ndarray) -> float: @@ -592,6 +592,7 @@ def _normalize_asset_image( size: AssetSize, background: AssetBackground = "white", square: bool = False, + fill_subject: bool = False, ) -> tuple[int, int]: import io as _io target_w, target_h = _asset_target_size(source_path, size, square=square) @@ -599,7 +600,25 @@ def _normalize_asset_image( out_path.parent.mkdir(parents=True, exist_ok=True) with Image.open(_io.BytesIO(img_bytes)) as raw: img = raw.convert("RGB") - img.thumbnail((target_w, target_h), Image.Resampling.LANCZOS) + if fill_subject: + diff = ImageChops.difference(img, Image.new("RGB", img.size, bg)) + mask = diff.convert("L").point(lambda px: 255 if px > 18 else 0) + bbox = mask.getbbox() + if bbox: + left, top, right, bottom = bbox + pad_x = round((right - left) * 0.06) + pad_y = round((bottom - top) * 0.06) + img = img.crop(( + max(0, left - pad_x), + max(0, top - pad_y), + min(img.width, right + pad_x), + min(img.height, bottom + pad_y), + )) + max_w = max(1, round(target_w * 0.92)) + max_h = max(1, round(target_h * 0.94)) + img.thumbnail((max_w, max_h), Image.Resampling.LANCZOS) + else: + img.thumbnail((target_w, target_h), Image.Resampling.LANCZOS) canvas = Image.new("RGB", (target_w, target_h), bg) canvas.paste(img, ((target_w - img.width) // 2, (target_h - img.height) // 2)) canvas.save(out_path, "JPEG", quality=95) @@ -699,6 +718,8 @@ SUBJECT_VIEW_LABELS: dict[str, str] = { "back": "背面", "left": "左侧", "right": "右侧", + "three_quarter_left": "左前 45°", + "three_quarter_right": "右前 45°", "side": "侧面", "side_walk": "侧面走路", "top": "顶部视角", @@ -727,10 +748,10 @@ def _subject_view_labels(kind: SubjectKind, requested: list[str] | None = None) return [ ("front", "正面站立"), ("back", "背面站立"), - ("side", "侧面站立"), - ("side_walk", "侧面走路"), - ("expression_neutral", "中性表情"), - ("expression_relaxed", "放松表情"), + ("left", "左侧站立"), + ("right", "右侧站立"), + ("three_quarter_left", "左前 45° 站立"), + ("three_quarter_right", "右前 45° 站立"), ] return [ ("front", "正面"), diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index 087944a..cfe44cd 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -36,15 +36,10 @@ const OBJECT_VIEW_OPTIONS = [ const LIVING_VIEW_OPTIONS = [ ["front", "正面"], ["back", "背面"], - ["side", "侧面"], - ["side_walk", "走路"], - ["expression_happy", "喜"], - ["expression_angry", "怒"], - ["expression_sad", "哀"], - ["expression_relaxed", "乐/放松"], - ["action_sit", "坐"], - ["action_hold", "手持"], - ["action_use", "使用"], + ["left", "左侧"], + ["right", "右侧"], + ["three_quarter_left", "左前 45°"], + ["three_quarter_right", "右前 45°"], ] type LightboxTab = "clean" | "scene" | "subject" | "review" @@ -266,7 +261,7 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o views, }) onJobUpdate?.(updated) - toast.success(`统一主体资产包已生成 · ${views.length} 张 · ${subjectReferenceFrameIndices.length} 帧参考`) + toast.success(`统一主体重绘完成 · ${views.length} 张 · ${subjectReferenceFrameIndices.length} 帧参考`) } catch (e) { toast.error("主体资产包生成失败:" + (e instanceof Error ? e.message : String(e))) } finally { @@ -924,7 +919,7 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o → {subjectReferenceLabel}
- 当前工作流只保留一个主体。多张关键帧用于补齐这个主体的角度、动作和表情;场景图会按每张关键帧单独生成。 + 当前工作流只保留一个主体。多张关键帧用于重绘这个主体的六张标准站立资产图;场景图会按每张关键帧单独生成。
{!hasUnifiedSubject && ( @@ -1053,7 +1048,7 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o
-
统一主体资产包
+
统一主体重绘图
{subjectReferenceLabel} @@ -1084,7 +1079,7 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o onClick={() => handleGenerateSubjectPackage(e.id, frameIndex)} disabled={isSubjectGenerating || activeViews.length === 0} className="rounded bg-violet-500/70 px-1.5 py-1 text-[10px] font-medium text-white transition hover:bg-violet-400 disabled:cursor-wait disabled:opacity-45 inline-flex items-center justify-center gap-1" - title="用多张关键帧参考生成同一个主体的多视角 / 动作 / 表情资产" + title="用多张关键帧参考重绘同一个主体的六张标准站立图" > {isSubjectGenerating ? : } {isSubjectGenerating ? "生成" : "生成"} @@ -1150,7 +1145,7 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o )}
- 主体候选来自识别结果;确认唯一主体、类型和视角后,使用参考帧生成统一主体资产包。 + 主体候选来自识别结果;确认唯一主体和类型后,使用参考帧重新绘制纯背景、占满画面的主体图。
diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index a0e6db0..c9c1c93 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -1561,7 +1561,7 @@ export function VisualLabNode({ data, selected }: any) { onClick={(e) => { e.stopPropagation(); openFirstFrame() }} disabled={!job || frames.length === 0} className="min-h-14 rounded-md border border-white/10 px-2 py-2 text-left transition hover:border-violet-300/50 hover:bg-violet-400/10 disabled:opacity-35" - title="用多张关键帧生成统一主体的多视角 / 动作 / 表情资产包" + title="用多张关键帧参考重绘统一主体的六张标准站立图" >