Files
qwen-image/ARCHETECTURE.svg
2026-06-26 17:28:17 +02:00

375 lines
29 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1400" height="920" viewBox="0 0 1400 920" xmlns="http://www.w3.org/2000/svg" font-family="'Inter','Helvetica Neue',Arial,sans-serif">
<defs>
<!-- Arrow markers -->
<marker id="arr-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0,8 3,0 6" fill="#60a5fa"/>
</marker>
<marker id="arr-orange" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0,8 3,0 6" fill="#fb923c"/>
</marker>
<marker id="arr-teal" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0,8 3,0 6" fill="#2dd4bf"/>
</marker>
<marker id="arr-green" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0,8 3,0 6" fill="#4ade80"/>
</marker>
<marker id="arr-gray" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0,8 3,0 6" fill="#64748b"/>
</marker>
<!-- Drop shadow -->
<filter id="shadow" x="-5%" y="-5%" width="110%" height="120%">
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000" flood-opacity="0.4"/>
</filter>
<filter id="shadow-sm" x="-5%" y="-5%" width="110%" height="120%">
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-color="#000" flood-opacity="0.3"/>
</filter>
<!-- Zone backgrounds -->
<linearGradient id="grad-browser" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#1e3a5f" stop-opacity="0.9"/>
<stop offset="100%" stop-color="#1e3a5f" stop-opacity="0.6"/>
</linearGradient>
<linearGradient id="grad-api" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#3b1f6b" stop-opacity="0.9"/>
<stop offset="100%" stop-color="#3b1f6b" stop-opacity="0.6"/>
</linearGradient>
<linearGradient id="grad-comfy" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#7c2d12" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#431407" stop-opacity="0.7"/>
</linearGradient>
<linearGradient id="grad-ml" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#134e4a" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#0d3330" stop-opacity="0.7"/>
</linearGradient>
<linearGradient id="grad-db" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#14532d" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#0c3a1e" stop-opacity="0.7"/>
</linearGradient>
<linearGradient id="grad-storage" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#1e293b" stop-opacity="0.9"/>
<stop offset="100%" stop-color="#0f172a" stop-opacity="0.8"/>
</linearGradient>
</defs>
<!-- ═══ Background ═══ -->
<rect width="1400" height="920" fill="#0f172a"/>
<!-- ═══ Title ═══ -->
<text x="700" y="38" text-anchor="middle" font-size="20" font-weight="700" fill="#f1f5f9" letter-spacing="0.5">Qwen-Image-Edit Rapid-AIO v23 — System Architecture</text>
<text x="700" y="58" text-anchor="middle" font-size="12" fill="#64748b">RTX A6000 · CUDA 12.4 · FastAPI :8500 · ComfyUI :8188 · PostgreSQL :5433</text>
<!-- ══════════════════════════════════════════════════
ZONE 1 — Browser / Frontend
══════════════════════════════════════════════════ -->
<rect x="30" y="72" width="1340" height="130" rx="10" fill="url(#grad-browser)" stroke="#3b82f6" stroke-width="1.2" filter="url(#shadow)"/>
<text x="48" y="91" font-size="10" font-weight="600" fill="#93c5fd" letter-spacing="1.2" text-transform="uppercase">BROWSER</text>
<!-- car.html box -->
<rect x="50" y="100" width="1300" height="88" rx="8" fill="#1e3a5f" stroke="#60a5fa" stroke-width="1" filter="url(#shadow-sm)"/>
<text x="70" y="122" font-size="13" font-weight="700" fill="#93c5fd">car.html</text>
<text x="150" y="122" font-size="11" fill="#64748b">— Single-Page Application (no build step)</text>
<!-- Sub-components row -->
<rect x="68" y="130" width="115" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="125" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Gallery View</text>
<text x="125" y="163" text-anchor="middle" font-size="9" fill="#64748b">group grid · open studio</text>
<rect x="194" y="130" width="138" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="263" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Studio Viewer</text>
<text x="263" y="163" text-anchor="middle" font-size="9" fill="#64748b">filmstrip · angle bar · crop</text>
<rect x="342" y="130" width="148" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="416" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Generate Tab</text>
<text x="416" y="163" text-anchor="middle" font-size="9" fill="#64748b">angles · poses · wireframe</text>
<rect x="500" y="130" width="148" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="574" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Faceswap Tab</text>
<text x="574" y="163" text-anchor="middle" font-size="9" fill="#64748b">source face · video target</text>
<rect x="658" y="130" width="148" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="732" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Segment Tab</text>
<text x="732" y="163" text-anchor="middle" font-size="9" fill="#64748b">SAM2 · rembg BG removal</text>
<rect x="816" y="130" width="148" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="890" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Info Tab</text>
<text x="890" y="163" text-anchor="middle" font-size="9" fill="#64748b">preferred · face-book · archive</text>
<rect x="974" y="130" width="148" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="1048" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Privacy Overlay</text>
<text x="1048" y="163" text-anchor="middle" font-size="9" fill="#64748b">disguised lock screen (P)</text>
<rect x="1132" y="130" width="200" height="46" rx="5" fill="#172554" stroke="#2563eb" stroke-width="0.8"/>
<text x="1232" y="148" text-anchor="middle" font-size="10" font-weight="600" fill="#93c5fd">Paste / Upload Handler</text>
<text x="1232" y="163" text-anchor="middle" font-size="9" fill="#64748b">add to group · skip_poses</text>
<!-- Arrow Browser → FastAPI -->
<line x1="700" y1="202" x2="700" y2="228" stroke="#60a5fa" stroke-width="1.5" marker-end="url(#arr-blue)"/>
<rect x="630" y="206" width="140" height="18" rx="3" fill="#0f172a" fill-opacity="0.8"/>
<text x="700" y="218" text-anchor="middle" font-size="9" fill="#60a5fa">HTTP fetch / EventSource</text>
<!-- ══════════════════════════════════════════════════
ZONE 2 — FastAPI
══════════════════════════════════════════════════ -->
<rect x="30" y="228" width="1340" height="178" rx="10" fill="url(#grad-api)" stroke="#a855f7" stroke-width="1.2" filter="url(#shadow)"/>
<text x="48" y="248" font-size="10" font-weight="600" fill="#d8b4fe" letter-spacing="1.2">FASTAPI :8500 — edit_api.py</text>
<!-- Endpoint groups -->
<!-- Images / Groups -->
<rect x="50" y="256" width="230" height="136" rx="7" fill="#2e1065" stroke="#7c3aed" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="165" y="274" text-anchor="middle" font-size="11" font-weight="700" fill="#c4b5fd">Images &amp; Groups</text>
<text x="65" y="292" font-size="9.5" fill="#a78bfa">GET /images</text>
<text x="65" y="307" font-size="9.5" fill="#a78bfa">GET /images/{filename}</text>
<text x="65" y="322" font-size="9.5" fill="#a78bfa">DELETE /images/{filename}</text>
<text x="65" y="337" font-size="9.5" fill="#a78bfa">POST /images/{f}/set-preferred</text>
<text x="65" y="352" font-size="9.5" fill="#a78bfa">POST /images/{f}/archive|unarchive</text>
<text x="65" y="367" font-size="9.5" fill="#a78bfa">POST /images/{f}/crop | /autocrop</text>
<text x="65" y="382" font-size="9.5" fill="#a78bfa">GET /groups | /names | /config</text>
<!-- Upload / Batch -->
<rect x="294" y="256" width="230" height="136" rx="7" fill="#2e1065" stroke="#7c3aed" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="409" y="274" text-anchor="middle" font-size="11" font-weight="700" fill="#c4b5fd">Upload &amp; Batch Generation</text>
<text x="309" y="292" font-size="9.5" fill="#a78bfa">POST /upload</text>
<text x="320" y="307" font-size="9.5" fill="#7c6fcf"> group_id · skip_poses</text>
<text x="309" y="322" font-size="9.5" fill="#a78bfa">POST /batch → async job</text>
<text x="320" y="337" font-size="9.5" fill="#7c6fcf"> filenames · prompts · poses</text>
<text x="320" y="352" font-size="9.5" fill="#7c6fcf"> wireframe_ref · wireframe_time</text>
<text x="309" y="367" font-size="9.5" fill="#a78bfa">GET /jobs/{id} ← poll progress</text>
<text x="309" y="382" font-size="9.5" fill="#a78bfa">GET /wireframe/frame/{name}?t=</text>
<!-- Face / BG -->
<rect x="538" y="256" width="230" height="136" rx="7" fill="#2e1065" stroke="#7c3aed" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="653" y="274" text-anchor="middle" font-size="11" font-weight="700" fill="#c4b5fd">Face &amp; Background</text>
<text x="553" y="292" font-size="9.5" fill="#a78bfa">POST /faceswap</text>
<text x="553" y="307" font-size="9.5" fill="#a78bfa">POST /remove-background/{f}</text>
<text x="564" y="322" font-size="9.5" fill="#7c6fcf"> rembg U2Net</text>
<text x="553" y="337" font-size="9.5" fill="#a78bfa">POST /remove-background-sam/{f}</text>
<text x="564" y="352" font-size="9.5" fill="#7c6fcf"> SAM2 point-prompt</text>
<text x="553" y="367" font-size="9.5" fill="#a78bfa">POST /images/{f}/extract-face</text>
<text x="564" y="382" font-size="9.5" fill="#7c6fcf"> insightface crop → DB</text>
<!-- Pipeline logic -->
<rect x="782" y="256" width="572" height="136" rx="7" fill="#2e1065" stroke="#7c3aed" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="1068" y="274" text-anchor="middle" font-size="11" font-weight="700" fill="#c4b5fd">Generation Pipeline (_run_pipeline)</text>
<!-- Pipeline flow boxes -->
<rect x="798" y="282" width="130" height="38" rx="4" fill="#1e1040" stroke="#6d28d9" stroke-width="0.7"/>
<text x="863" y="297" text-anchor="middle" font-size="9" font-weight="600" fill="#c4b5fd">Prompt Cleaning</text>
<text x="863" y="311" text-anchor="middle" font-size="8.5" fill="#7c6fcf">strip transparent kws</text>
<line x1="928" y1="301" x2="948" y2="301" stroke="#a855f7" stroke-width="1" marker-end="url(#arr-teal)"/>
<rect x="948" y="282" width="130" height="38" rx="4" fill="#1e1040" stroke="#6d28d9" stroke-width="0.7"/>
<text x="1013" y="297" text-anchor="middle" font-size="9" font-weight="600" fill="#c4b5fd">ComfyUI Queue</text>
<text x="1013" y="311" text-anchor="middle" font-size="8.5" fill="#7c6fcf">workflow_qwen_edit.json</text>
<line x1="1078" y1="301" x2="1098" y2="301" stroke="#a855f7" stroke-width="1" marker-end="url(#arr-teal)"/>
<rect x="1098" y="282" width="130" height="38" rx="4" fill="#1e1040" stroke="#6d28d9" stroke-width="0.7"/>
<text x="1163" y="297" text-anchor="middle" font-size="9" font-weight="600" fill="#c4b5fd">Qwen Inference</text>
<text x="1163" y="311" text-anchor="middle" font-size="8.5" fill="#7c6fcf">GGUF · VAE → PNG</text>
<line x1="1228" y1="301" x2="1248" y2="301" stroke="#a855f7" stroke-width="1" marker-end="url(#arr-teal)"/>
<rect x="1248" y="282" width="90" height="38" rx="4" fill="#1e1040" stroke="#6d28d9" stroke-width="0.7"/>
<text x="1293" y="297" text-anchor="middle" font-size="9" font-weight="600" fill="#c4b5fd">SAM2</text>
<text x="1293" y="311" text-anchor="middle" font-size="8.5" fill="#7c6fcf">BG removal</text>
<!-- Batch worker note -->
<text x="798" y="348" font-size="9" fill="#6d28d9">Batch worker: foreach (filename, prompt) → wireframe frame extract → pipeline → save → DB upsert</text>
<text x="798" y="363" font-size="9" fill="#6d28d9">Job progress polled via GET /jobs/{id} · each item updates per-slot status dict</text>
<text x="798" y="378" font-size="9" fill="#6d28d9">Camera angles: CAMERA_ANGLES[12] → prompt injected, pose=null, group_id tracked</text>
<!-- Arrows FastAPI → sub-services -->
<!-- To ComfyUI -->
<line x1="300" y1="406" x2="300" y2="450" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arr-orange)"/>
<rect x="232" y="415" width="136" height="16" rx="3" fill="#0f172a" fill-opacity="0.85"/>
<text x="300" y="427" text-anchor="middle" font-size="9" fill="#fb923c">POST /prompt · GET /view</text>
<!-- To ML post-proc -->
<line x1="700" y1="406" x2="700" y2="450" stroke="#2dd4bf" stroke-width="1.5" marker-end="url(#arr-teal)"/>
<rect x="636" y="415" width="128" height="16" rx="3" fill="#0f172a" fill-opacity="0.85"/>
<text x="700" y="427" text-anchor="middle" font-size="9" fill="#2dd4bf">in-process call</text>
<!-- To PostgreSQL -->
<line x1="1140" y1="406" x2="1140" y2="450" stroke="#4ade80" stroke-width="1.5" marker-end="url(#arr-green)"/>
<rect x="1074" y="415" width="132" height="16" rx="3" fill="#0f172a" fill-opacity="0.85"/>
<text x="1140" y="427" text-anchor="middle" font-size="9" fill="#4ade80">psycopg2 · upsert / query</text>
<!-- ══════════════════════════════════════════════════
ZONE 3a — ComfyUI
══════════════════════════════════════════════════ -->
<rect x="30" y="450" width="530" height="248" rx="10" fill="url(#grad-comfy)" stroke="#ea580c" stroke-width="1.2" filter="url(#shadow)"/>
<text x="50" y="470" font-size="10" font-weight="600" fill="#fed7aa" letter-spacing="1.2">COMFYUI :8188 — Qwen GGUF Inference</text>
<!-- Workflow node strip -->
<rect x="50" y="478" width="490" height="52" rx="6" fill="#431407" stroke="#c2410c" stroke-width="0.8"/>
<text x="295" y="494" text-anchor="middle" font-size="10" font-weight="600" fill="#fed7aa">workflow_qwen_edit.json — Node Graph</text>
<rect x="58" y="500" width="80" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="98" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">NODE_POSITIVE</text>
<rect x="144" y="500" width="80" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="184" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">NODE_NEGATIVE</text>
<rect x="230" y="500" width="80" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="270" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">NODE_LATENT</text>
<rect x="316" y="500" width="80" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="356" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">NODE_KSAMPLER</text>
<rect x="402" y="500" width="60" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="432" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">image1</text>
<rect x="468" y="500" width="64" height="22" rx="3" fill="#7c2d12" stroke="#ea580c" stroke-width="0.6"/>
<text x="500" y="514" text-anchor="middle" font-size="8.5" fill="#fed7aa">image2 (wf)</text>
<!-- Model cards -->
<rect x="50" y="542" width="155" height="72" rx="6" fill="#431407" stroke="#c2410c" stroke-width="0.8" filter="url(#shadow-sm)"/>
<text x="127" y="560" text-anchor="middle" font-size="10" font-weight="700" fill="#fed7aa">Qwen GGUF</text>
<text x="127" y="576" text-anchor="middle" font-size="8.5" fill="#fdba74">Rapid-NSFW-v23_Q8_0.gguf</text>
<text x="127" y="592" text-anchor="middle" font-size="8.5" fill="#fdba74">~8 GB VRAM</text>
<text x="127" y="607" text-anchor="middle" font-size="8.5" fill="#fdba74">Euler / karras sampler</text>
<rect x="216" y="542" width="155" height="72" rx="6" fill="#431407" stroke="#c2410c" stroke-width="0.8" filter="url(#shadow-sm)"/>
<text x="293" y="560" text-anchor="middle" font-size="10" font-weight="700" fill="#fed7aa">Text Encoder</text>
<text x="293" y="576" text-anchor="middle" font-size="8.5" fill="#fdba74">qwen_2.5_vl_7b_fp8</text>
<text x="293" y="592" text-anchor="middle" font-size="8.5" fill="#fdba74">_scaled.safetensors</text>
<text x="293" y="607" text-anchor="middle" font-size="8.5" fill="#fdba74">FP8 quantised</text>
<rect x="382" y="542" width="138" height="72" rx="6" fill="#431407" stroke="#c2410c" stroke-width="0.8" filter="url(#shadow-sm)"/>
<text x="451" y="560" text-anchor="middle" font-size="10" font-weight="700" fill="#fed7aa">VAE Decoder</text>
<text x="451" y="576" text-anchor="middle" font-size="8.5" fill="#fdba74">qwen_image_vae</text>
<text x="451" y="592" text-anchor="middle" font-size="8.5" fill="#fdba74">.safetensors</text>
<text x="451" y="607" text-anchor="middle" font-size="8.5" fill="#fdba74">latent → RGB PNG</text>
<!-- Wireframe note -->
<rect x="50" y="626" width="490" height="60" rx="6" fill="#431407" stroke="#c2410c" stroke-width="0.8"/>
<text x="295" y="644" text-anchor="middle" font-size="10" font-weight="600" fill="#fed7aa">Wireframe Pose Guide</text>
<text x="295" y="660" text-anchor="middle" font-size="9" fill="#fdba74">/mnt/zim/tour-comfy/wireframe/*.mp4 → OpenCV frame extract → image2 slot</text>
<text x="295" y="675" text-anchor="middle" font-size="9" fill="#fdba74">No ControlNet installed — Qwen uses image2 as visual layout reference</text>
<!-- ══════════════════════════════════════════════════
ZONE 3b — ML Post-processing
══════════════════════════════════════════════════ -->
<rect x="576" y="450" width="440" height="248" rx="10" fill="url(#grad-ml)" stroke="#0d9488" stroke-width="1.2" filter="url(#shadow)"/>
<text x="594" y="470" font-size="10" font-weight="600" fill="#99f6e4" letter-spacing="1.2">ML POST-PROCESSING — in-process Python</text>
<!-- SAM2 -->
<rect x="594" y="478" width="195" height="100" rx="6" fill="#0d3330" stroke="#0f766e" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="691" y="496" text-anchor="middle" font-size="11" font-weight="700" fill="#2dd4bf">SAM2</text>
<text x="691" y="511" text-anchor="middle" font-size="8.5" fill="#5eead4">sam2.1_hiera_base_plus.pt</text>
<text x="600" y="528" font-size="8.5" fill="#5eead4">FG: 12 pts — hair → shoes</text>
<text x="600" y="542" font-size="8.5" fill="#5eead4">BG: 7 pts — corners + edges</text>
<text x="600" y="556" font-size="8.5" fill="#5eead4">Coverage check 5 %92 %</text>
<text x="600" y="570" font-size="8.5" fill="#5eead4">Gaussian blur edge (r=1)</text>
<!-- rembg -->
<rect x="800" y="478" width="200" height="100" rx="6" fill="#0d3330" stroke="#0f766e" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="900" y="496" text-anchor="middle" font-size="11" font-weight="700" fill="#2dd4bf">rembg (U2Net)</text>
<text x="900" y="511" text-anchor="middle" font-size="8.5" fill="#5eead4">Fallback BG removal</text>
<text x="806" y="528" font-size="8.5" fill="#5eead4">Used when SAM2 unavailable</text>
<text x="806" y="542" font-size="8.5" fill="#5eead4">or coverage out of range</text>
<text x="806" y="556" font-size="8.5" fill="#5eead4">Also: Segment tab button</text>
<!-- insightface -->
<rect x="594" y="590" width="195" height="96" rx="6" fill="#0d3330" stroke="#0f766e" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="691" y="608" text-anchor="middle" font-size="11" font-weight="700" fill="#2dd4bf">insightface buffalo_l</text>
<text x="600" y="625" font-size="8.5" fill="#5eead4">Faceswap: source → video frames</text>
<text x="600" y="639" font-size="8.5" fill="#5eead4">Face extract: bbox + 50 % pad</text>
<text x="600" y="653" font-size="8.5" fill="#5eead4">Saves {gid}_face.png → DB</text>
<text x="600" y="667" font-size="8.5" fill="#5eead4">Triggered on set-preferred</text>
<!-- GFPGAN -->
<rect x="800" y="590" width="200" height="96" rx="6" fill="#0d3330" stroke="#0f766e" stroke-width="0.9" filter="url(#shadow-sm)"/>
<text x="900" y="608" text-anchor="middle" font-size="11" font-weight="700" fill="#2dd4bf">GFPGAN</text>
<text x="806" y="625" font-size="8.5" fill="#5eead4">Face restoration</text>
<text x="806" y="639" font-size="8.5" fill="#5eead4">Applied post-faceswap</text>
<text x="806" y="653" font-size="8.5" fill="#5eead4">Optional quality upgrade</text>
<!-- ══════════════════════════════════════════════════
ZONE 3c — PostgreSQL
══════════════════════════════════════════════════ -->
<rect x="1030" y="450" width="340" height="248" rx="10" fill="url(#grad-db)" stroke="#16a34a" stroke-width="1.2" filter="url(#shadow)"/>
<text x="1048" y="470" font-size="10" font-weight="600" fill="#bbf7d0" letter-spacing="1.2">POSTGRESQL 192.168.1.160:5433 db=dv</text>
<rect x="1048" y="478" width="302" height="200" rx="6" fill="#0c3a1e" stroke="#15803d" stroke-width="0.8" filter="url(#shadow-sm)"/>
<text x="1199" y="496" text-anchor="middle" font-size="11" font-weight="700" fill="#4ade80">person table</text>
<text x="1055" y="514" font-size="9" fill="#86efac">filename TEXT PRIMARY KEY</text>
<text x="1055" y="529" font-size="9" fill="#86efac">group_id TEXT</text>
<text x="1055" y="544" font-size="9" fill="#86efac">group_name TEXT</text>
<text x="1055" y="559" font-size="9" fill="#86efac">sort_order INTEGER (0 = preferred)</text>
<text x="1055" y="574" font-size="9" fill="#86efac">prompt TEXT — generation prompt</text>
<text x="1055" y="589" font-size="9" fill="#86efac">pose TEXT</text>
<text x="1055" y="604" font-size="9" fill="#86efac">hidden BOOLEAN</text>
<text x="1055" y="619" font-size="9" fill="#86efac">archived BOOLEAN</text>
<text x="1055" y="634" font-size="9" fill="#86efac">has_background BOOLEAN</text>
<text x="1055" y="649" font-size="9" fill="#86efac">source_refs TEXT (JSON array)</text>
<text x="1055" y="664" font-size="9" fill="#86efac">content_type TEXT (image | video)</text>
<!-- ══════════════════════════════════════════════════
ZONE 4 — Storage
══════════════════════════════════════════════════ -->
<rect x="30" y="716" width="980" height="118" rx="10" fill="url(#grad-storage)" stroke="#334155" stroke-width="1.2" filter="url(#shadow)"/>
<text x="50" y="736" font-size="10" font-weight="600" fill="#94a3b8" letter-spacing="1.2">FILE SYSTEM</text>
<rect x="50" y="744" width="280" height="76" rx="6" fill="#1e293b" stroke="#475569" stroke-width="0.8"/>
<text x="190" y="762" text-anchor="middle" font-size="10" font-weight="600" fill="#cbd5e1">Output Directory</text>
<text x="60" y="778" font-size="9" fill="#94a3b8">/mnt/zim/tour-comfy/output/</text>
<text x="60" y="793" font-size="9" fill="#64748b">PNGs · sidecars (.nobg.png) · face crops</text>
<text x="60" y="808" font-size="9" fill="#64748b">timestamped — 20260622_HHMMSS_N_src.png</text>
<rect x="344" y="744" width="280" height="76" rx="6" fill="#1e293b" stroke="#475569" stroke-width="0.8"/>
<text x="484" y="762" text-anchor="middle" font-size="10" font-weight="600" fill="#cbd5e1">Wireframe Videos</text>
<text x="354" y="778" font-size="9" fill="#94a3b8">/mnt/zim/tour-comfy/wireframe/</text>
<text x="354" y="793" font-size="9" fill="#64748b">*.mp4 pose reference videos</text>
<text x="354" y="808" font-size="9" fill="#64748b">Frame extracted via OpenCV at t∈[0,1]</text>
<rect x="638" y="744" width="360" height="76" rx="6" fill="#1e293b" stroke="#475569" stroke-width="0.8"/>
<text x="818" y="762" text-anchor="middle" font-size="10" font-weight="600" fill="#cbd5e1">Configuration</text>
<text x="648" y="778" font-size="9" fill="#94a3b8">tour-comfy/config.json</text>
<text x="648" y="793" font-size="9" fill="#64748b">output_dir · comfy_url · host · port · max_area</text>
<text x="648" y="808" font-size="9" fill="#64748b">R/W via GET /config · POST /config</text>
<!-- ══════════════════════════════════════════════════
ZONE 5 — Background services
══════════════════════════════════════════════════ -->
<rect x="1026" y="716" width="344" height="118" rx="10" fill="url(#grad-storage)" stroke="#334155" stroke-width="1.2" filter="url(#shadow)"/>
<text x="1046" y="736" font-size="10" font-weight="600" fill="#94a3b8" letter-spacing="1.2">BACKGROUND SERVICES — systemd</text>
<rect x="1046" y="744" width="145" height="76" rx="6" fill="#1e293b" stroke="#475569" stroke-width="0.8"/>
<text x="1119" y="762" text-anchor="middle" font-size="10" font-weight="600" fill="#cbd5e1">watcher.py</text>
<text x="1052" y="778" font-size="9" fill="#94a3b8">monitors output dir</text>
<text x="1052" y="793" font-size="9" fill="#64748b">auto-processes drops</text>
<text x="1052" y="808" font-size="9" fill="#64748b">watcher.lock · watcher.log</text>
<rect x="1202" y="744" width="155" height="76" rx="6" fill="#1e293b" stroke="#475569" stroke-width="0.8"/>
<text x="1280" y="762" text-anchor="middle" font-size="10" font-weight="600" fill="#cbd5e1">Systemd Units</text>
<text x="1208" y="778" font-size="9" fill="#94a3b8">comfyui-backend</text>
<text x="1208" y="793" font-size="9" fill="#94a3b8">comfyui-api</text>
<text x="1208" y="808" font-size="9" fill="#64748b">start.sh · stop.sh</text>
<!-- Storage arrows from FastAPI -->
<line x1="300" y1="698" x2="180" y2="716" stroke="#475569" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arr-gray)"/>
<line x1="700" y1="698" x2="484" y2="716" stroke="#475569" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arr-gray)"/>
<!-- ══════════════════════════════════════════════════
Legend
══════════════════════════════════════════════════ -->
<rect x="30" y="848" width="1340" height="56" rx="8" fill="#0f172a" stroke="#1e293b" stroke-width="1"/>
<text x="50" y="866" font-size="9" font-weight="600" fill="#64748b" letter-spacing="1">LEGEND</text>
<line x1="114" y1="870" x2="144" y2="870" stroke="#60a5fa" stroke-width="1.5" marker-end="url(#arr-blue)"/>
<text x="150" y="874" font-size="9" fill="#94a3b8">HTTP (fetch)</text>
<line x1="254" y1="870" x2="284" y2="870" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arr-orange)"/>
<text x="290" y="874" font-size="9" fill="#94a3b8">ComfyUI API</text>
<line x1="390" y1="870" x2="420" y2="870" stroke="#2dd4bf" stroke-width="1.5" marker-end="url(#arr-teal)"/>
<text x="426" y="874" font-size="9" fill="#94a3b8">In-process call</text>
<line x1="526" y1="870" x2="556" y2="870" stroke="#4ade80" stroke-width="1.5" marker-end="url(#arr-green)"/>
<text x="562" y="874" font-size="9" fill="#94a3b8">PostgreSQL (psycopg2)</text>
<line x1="698" y1="870" x2="728" y2="870" stroke="#64748b" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arr-gray)"/>
<text x="734" y="874" font-size="9" fill="#94a3b8">File I/O</text>
<text x="800" y="866" font-size="9" fill="#64748b" letter-spacing="0.5">TRANSPARENT BG FLOW: prompt → strip kw → Qwen (solid BG) → SAM2 point-prompt (12 FG + 7 BG pts) → coverage check → Gaussian edge</text>
<text x="800" y="882" font-size="9" fill="#64748b" letter-spacing="0.5">FACE FLOW: set-preferred → insightface detect → pad bbox → crop → {gid}_face.png → DB upsert → studio Info tab face-book thumbnail</text>
</svg>