Files
qwen-image/AGENTS.md
2026-06-28 00:51:07 +02:00

21 KiB
Raw Blame History

Qwen-Image-Edit Rapid-AIO v23 — System Documentation

Overview

A self-hosted AI image studio for generative editing, multi-view character rendering, background removal, face swapping, and group-based asset management. It runs the Qwen 2.5 VL GGUF model through ComfyUI and exposes a feature-rich browser frontend backed by a FastAPI service.

Browser (car.html SPA)
        │  HTTP / fetch
        ▼
FastAPI :8500  (edit_api.py)
   ├─── ComfyUI :8188  ← Qwen GGUF model
   ├─── SAM2 / rembg   ← background removal
   ├─── insightface    ← faceswap + face extraction
   ├─── GFPGAN         ← face restoration
   └─── PostgreSQL     ← metadata (192.168.1.160:5433)
              │
        File system  (/mnt/zim/tour-comfy/output/, wireframe/)

Hardware

Component Spec
GPU NVIDIA RTX A6000 (48 GB VRAM)
CUDA 12.4
PyTorch 2.6.0+cu124
ComfyUI latest pinned (a6000-comfy/)

Services

1. ComfyUI Backend — :8188

  • Script: a6000-comfy/run_comfyui.sh
  • Systemd: comfyui-backend
  • Purpose: Executes the Qwen GGUF model via workflow_qwen_edit.json

Loaded models

Role File
GGUF LLM Qwen-Rapid-NSFW-v23_Q8_0.gguf
Text encoder qwen_2.5_vl_7b_fp8_scaled.safetensors
VAE qwen_image_vae.safetensors

Workflow nodes (workflow_qwen_edit.json):

  • NODE_POSITIVE — positive text prompt
  • NODE_NEGATIVE — negative prompt
  • NODE_LATENT — latent dimensions (width × height)
  • NODE_KSAMPLER — sampler params (Euler/karras, seed, steps, CFG)
  • image1 — reference image (base64 uploaded to ComfyUI /upload/image)
  • image2 — optional wireframe pose guide (extracted from wireframe video)

2. FastAPI API — :8500

  • Script: tour-comfy/edit_api.py
  • Systemd: comfyui-api
  • DB: PostgreSQL at 192.168.1.160:5433, database dv, table person

Endpoints Library

Core Generation & Upload
Method Path Purpose
POST /upload Upload image; optionally add to existing group (group_id + skip_poses)
POST /batch Queue multi-prompt generation job (async)
GET /batch/{job_id} Poll batch job status / per-item progress
DELETE /batch/{job_id} Cancel active batch job
POST /multi-ref Queue a generation job with multiple reference images
POST /refine-prompt Refine a generation prompt using the external uncensored chat LLM
POST /edit Edit uploaded image
POST /generate-scenery Generate scenery background based on video frame + reference image
GET /scenery/library Return all scenery images grouped by source video reference
Image Editing & Canvas Expansion (Padding & Crop)
Method Path Purpose
POST /images/{filename}/crop Crop to pixel rect; as_copy:true crops a referenced copy instead of modifying in-place
POST /images/{filename}/autocrop Auto-trim transparent borders in-place
POST /images/{filename}/pad Expand image canvas with custom padding on each side, optional background fills (transparent, black, white), and optional Qwen-based Outpainting (outpaint:true)
POST /images/{filename}/rotate Rotate clockwise in 90° steps (lossless transpose, in-place)
POST /images/{filename}/invert-alpha Invert the alpha channel (recovers background removal errors)
POST /images/{filename}/duplicate Copy image into the same group with timestamp-based filename and source_refs=[original]
POST /restore-background/{filename} Flatten RGBA to opaque RGB via white composite background
Background Removal
Method Path Purpose
POST /remove-background/{filename} rembg BG removal, overwrites in-place; sets has_background=false + refreshes static data
POST /remove-background-sam/{filename} SAM2 BG removal → .nobg.png sidecar
POST /remove-background/group/{group_id} Queue background removal for all group members as an async background task
GET /sam2/check Return whether SAM2 background remover is available
Metadata, Custom Labels & Group Management
Method Path Purpose
GET /images List all images with DB metadata
GET /images/{filename} Serve image file
GET /groups List all groups with members
GET /group-names Return map of custom group names
POST /group-names/{group_id} Save custom group name
POST /groups/merge Merge several groups into one group
POST /groups/extract Extract a file into its own solo group
GET /groups/{group_id}/order Return order of members in a group
POST /groups/{group_id}/order Set custom sort order of group members
GET /similar/{filename} Find visually similar images using database embeddings
GET /names Map filename → person name
POST /names/{filename} Map image file to custom character name
Visibility, Archiving & Deletion
Method Path Purpose
POST /images/{filename}/hidden Toggle studio visibility (soft-hide)
POST /images/{filename}/archive Soft-archive image (hidden from default view)
POST /images/{filename}/unarchive Restore soft-archived image
DELETE /images/{filename} Delete image file + DB record
POST /groups/{group_id}/archive Soft-archive an entire group
POST /groups/{group_id}/unarchive Restore an archived group
DELETE /groups/{group_id} Delete entire group files and DB records
2D Body Pose Tools (rtmlib / RTMPose)
Method Path Purpose
GET /pose/check Report pose-estimator availability + backend (rtmlib or mediapipe)
POST /images/{filename}/pose Estimate COCO-17 body keypoints (caches descriptor for similar-search)
GET /pose/similar/{filename} Rank library images by pose similarity to this image
POST /pose/similar Rank library images by similarity to a supplied (edited) skeleton
POST /pose/index Build pose descriptors for the whole library (daemon thread)
GET /pose/index/status Poll pose-index build progress
POST /pose/from-wireframe Extract frame from a wireframe video and estimate body pose keypoints
POST /pose/render Render keypoints as OpenPose skeleton image (base64)
POST /generate-with-pose Generate image from a specific pose skeleton (avoids wireframe bleed-through)
GET /poses Load pose library from poses.md
POST /poses Create / update / rename a pose (old_name to rename) → rewrites poses.md
DELETE /poses/{name} Delete a pose from poses.md
3D Turntable & Orbit Previews
Method Path Purpose
POST /orbit Queue an orbit preview generation. Engine depth (fast 2.5D card parallax) or qwen (turntable)
GET /turntable/status Return background turntable generation state/progress summary
GET /turntable/status/{group_id} Return turntable state for a specific group
POST /turntable/pause Pause background turntable generation
POST /turntable/resume Resume background turntable generation
DELETE /turntable/{group_id} Reset turntable cache & records for group to force re-generation
Face Processing (insightface)
Method Path Purpose
POST /faceswap insightface video faceswap
GET /faceswap/check Report available face enhancement backends
POST /images/{filename}/extract-face Extract & save face crop via insightface (background task)
POST /faces/similar Find groups with visually similar faces using embeddings
POST /faces/index Build face embedding index for all preferred faces in library
GET /faces/index/status Poll face-indexing progress status
GET /faces/{group_id} Check if face crop exists for group
Videos & Wireframe Guides
Method Path Purpose
GET /videos List all available wireframe guide videos
GET /wireframe/frame/{video_name} Extract frame at ?t= (01) from wireframe video
GET /wireframe/duration/{video_name} Get video duration (seconds) via ffprobe
POST /wireframe/trim Trim a wireframe video to given start/end offsets
POST /wireframe/frame Extract frame at timestamp, returns base64 PNG
POST /generate-video Stitch list of output images into a short looping MP4 video
Database, Config & System Diagnostics
Method Path Purpose
GET/POST /config Read / write config.json
GET /db/inconsistencies Retrieve consistency report or run a check with ?run_now=true
POST /db/repair Run repair actions (restore, import file, assign group, permanently delete)
POST /db/cleanup Delete DB records of files that are missing on disk
GET /health API health check status

Generation Pipeline (_run_pipeline)

prompt
  │
  ├─ "transparent background" keywords detected?
  │     YES → strip keywords from Qwen prompt (keeps `is_transparent` flag)
  │            set negative: "deformed anatomy, watermark, logo"
  │
  ├─ Canvas Expansion / Outpainting requested?
  │     YES → if outpaint=True, append/use natural outpainting instructions:
  │           "Naturally outpaint and extend the borders of the image to complete the scene."
  │
  ├─ Upload reference image(s) to ComfyUI /upload/image
  │     image1 = source image
  │     image2 = wireframe pose guide frame (optional)
  │
  ├─ Patch workflow graph nodes (prompt, size, seed, sampler)
  ├─ POST to ComfyUI /prompt  →  poll /history until done
  ├─ Fetch PNG from ComfyUI /view
  │
  └─ is_transparent?
       YES → _apply_transparency_sam2(png_bytes)
              ├─ SAM2 point-prompt segmentation
              │    FG: 12 points (hair→shoes, lateral spread for ¾ poses)
              │    BG: 7 points (corners + edge midpoints)
              │    Coverage check: 5%92% (else fallback)
              │    Gaussian blur radius=1 for soft edges
              └─ fallback: _apply_transparency (rembg U2Net)

Batch Worker (_batch_worker)

Runs in a background thread pool. One thread per batch job item.

For each (filename, prompt) pair:
  1. Load source image from output dir
  2. If wireframe_ref: extract frame at wireframe_time via OpenCV VideoCapture
  3. Call _run_pipeline(pil, prompt, seed, max_area, extra_images=[wireframe_frame])
  4. Save PNG to output dir (timestamped filename)
  5. upsert_person(new_filename, group_id, prompt, source_refs=[original])
  6. Update job progress dict (polled via /jobs/{id})

Camera Angle Presets

Eight absolute + four relative angle prompts defined as CAMERA_ANGLES in car.html. Each triggers a /batch request with pose=null and the angle prompt injected.

Name Direction
Front Straight-on frontal
¾ Left / ¾ Right 45° rotation
Side L / Side R 90° profile
Back Rear view
High / Low Bird's-eye / worm's-eye
↺ 45° / ↻ 45° Relative +45° left / right
↺ 90° / ↻ 90° Relative +90° left / right

Background Removal

Two strategies are available:

Configurable BG removal — config.json key "bg_removal"

Set to "rembg" (default) or "sam2".

"rembg" mode

  • Strips transparent-related keywords from the Qwen prompt entirely → Qwen renders with a natural real background
  • _apply_transparency (rembg U2Net) post-processes the result
  • Most reliable on complex Qwen-generated backgrounds (streets, shadows, cobblestones, etc.)

"sam2" mode

  • Replaces transparent keywords with "black background" in the Qwen prompt → Qwen renders the person against a pure black background
  • Negative prompt discourages any real-background content
  • _apply_transparency_sam2 (SAM2 bbox) post-processes: pure black gives maximal contrast → reliable segmentation
  • Tested: produces clean full-body mask at ~74% coverage including hair, shoes, and earrings

Explicit "black background" in prompt

  • Regardless of the configured method, always routes to SAM2 — the user has already set up the ideal SAM2 input
  • Prompt is passed to Qwen unchanged

SAM2 implementation (_apply_transparency_sam2)

  • Model: sam2.1_hiera_base_plus.pt at ~/.sam/sam2.1_hiera_base_plus.pt
  • Bbox [1%, 1%, 99%, 99%], multimask_output=True, best-score mask, coverage check 5%92%, Gaussian edge blur radius=1
  • Also called by the explicit /remove-background-sam/{filename} endpoint

Face Processing (insightface)

Model: buffalo_l

Faceswap (/faceswap):

  • Reads source face from reference image
  • Swaps into each frame of a target video via OpenCV
  • Optionally runs GFPGAN face restoration post-swap

Face extraction (_extract_face_bg background task):

  • Triggered on set-preferred or explicit extract-face endpoint
  • Detects largest face via insightface, adds padding (50% sides, 200% top headroom)
  • Saves as {group_id}_face.png in output dir
  • DB-registered for display in the studio Info tab face-book thumbnail

Database Schema (person table)

Column Type Purpose
filename TEXT PK Unique image filename
filepath TEXT Absolute file path
group_id TEXT Group membership
group_name TEXT Human-readable group label
sort_order INTEGER Position within group (0 = preferred)
name TEXT Person / character name
prompt TEXT Generation prompt used
pose TEXT Pose tag
hidden BOOLEAN Hidden from studio preview
archived BOOLEAN Soft-deleted
has_background BOOLEAN Whether BG was removed
has_clothing BOOLEAN Content classification flag
source_refs TEXT (JSON) Original filenames this was derived from
content_type TEXT image or video
faceswap_source_video TEXT Source video for faceswapped clips
face_embedding vector(512) Face recognition embedding for visually matching character faces
is_source BOOLEAN Whether the file is a primary reference/source asset

Frontend (car.html)

Single-page application (~5000 lines). No build step — pure HTML/CSS/JS.

Modes:

  • Gallery — grid of all groups; click to open Studio
  • Studio — filmstrip + large viewer + sidebar tabs
  • Database Consistency (trash.html SPA) — dedicated panel for identifying legacy/orphaned records, untracked files, and manual restore/delete of archived assets

Sidebar tabs:

Tab Purpose
Generate Camera angle picker, pose selector (with inline edit/add/delete → poses.md), wireframe guide, auto-growing prompt override with history autocomplete, fine-tune textareas, multi-ref thumbnails (click to deselect), batch submit
Info Metadata, preferred toggle, face-book thumbnail, hide/archive/delete, manual crop/expand padding (copy or in-place), rotate ±90°, No-BG / Invert-α, duplicate, Pose preview (draggable + similar-pose search)
Faceswap Source face selector, target video, preview quality toggle
Segment SAM2 and rembg BG removal buttons
Scenery Scenery video reference for generation

Key Studio Features

Canvas Expansion & Manual Padding (Interactive Tooling)

The Info sidebar and dedicated toolbar provide professional, reactive manual padding controls:

  • Percent/Pixel Harmony: Seamlessly accepts both absolute integer pixel values (e.g., 100px) and percentage-based inputs (e.g., 10% or 0.1), ensuring perfect formatting and suffix preservation.
  • Quick Adjustment Presets: Rapidly adjust margins with single-click actions for +10%, +20%, +50%, +100px, or Reset to clear all.
  • Uniform Locking: Enabling the Uniform option synchronizes changes on any single padding field to all four sides simultaneously.
  • Intelligent Previews: Dynamically renders real-time color-coded canvas border previews based on the selected fill type (Amber dashed for transparent, translucent black for black, and translucent white for white).
  • Shortcuts: Confirm cropping or padding instantly with Enter, or dismiss/cancel the operation using Escape.

Reactive Layout Optimization

To prevent desyncs during image transitions, crops, canvas expansion, or 2.2x zooming:

  • A ResizeObserver is registered on the primary studio viewer image (lbImg).
  • Automatically triggers a layout and geometry recalculation, cleanly and immediately adjusting the background transparency checkerboard grid and padding visual overlays to match the actual visual boundaries of the contained image.

Key state variables:

  • lbCurrentGid — active group ID
  • lbIdx — current filmstrip position
  • _followLatestGid — auto-follow newly generated image
  • _sbSelectedAngles — selected camera angle names
  • _sbWireframeRef / _sbWireframeTime — wireframe video + frame time
  • privacyMode — privacy overlay toggle (key: P)

Wireframe Pose Guide

Wireframe videos stored in /mnt/zim/tour-comfy/wireframe/.
The studio Generate tab shows a video picker + time scrubber. On generation:

  1. Backend calls GET /wireframe/frame/{name}?t=0.5 to extract the chosen frame
  2. Frame passed as image2 in the ComfyUI workflow (second visual reference)
  3. No ControlNet installed — Qwen uses the wireframe as a layout hint via image2 slot

Pose Tools (2D body pose)

A posenet-style toolset built on a feature-detected estimator. Preferred backend is rtmlib (RTMPose, ONNX via the already-installed onnxruntime); mediapipe is a fallback. If neither is installed the endpoints return 501 and the UI shows an install hint.

  • Install (A6000 venv): ~/comfyui/venv/bin/pip install rtmlib. Models (~150 MB) auto- download to ~/.cache/rtmlib on first use.
  • CUDA libs: onnxruntime-gpu needs the venv's nvidia-*-cu12 libs on the loader path. a6000-comfy/start_api.sh adds them to LD_LIBRARY_PATH at launch (torch finds them via RPATH, onnxruntime does not).

Output format: COCO-17 keypoints (POSE_KEYPOINT_NAMES) + POSE_SKELETON edge list, returned in image pixels with per-joint score.

Studio overlay (car.html): the Pose button overlays the skeleton on the viewer (letterbox-aware canvas). Joints are draggable to refine/explore a pose. Overlay toolbar: Reset (re-detect) and Similar pose (rank the library by the current — possibly edited — skeleton). Results render as a thumbnail strip; clicking one opens that image's group.

Similarity model (edit_api.py):

  • _pose_descriptor(keypoints) → translation/scale-invariant vector: centered on hip midpoint (fallback shoulders), scaled by torso length, 17×(x,y) + visibility mask. None if < 6 joints.
  • _pose_distance(a, b) → weighted L2 over jointly-visible joints, taking the min of the direct and the left-right-mirrored comparison so mirrored poses match.
  • Pose index: descriptors are cached in <output>/_data/poses_index.json. It fills incrementally whenever /images/{f}/pose runs, or in bulk via /pose/index (daemon thread, batched writes every 50, progress via /pose/index/status).

File Layout

qwen-image-edit-rapid-aio-nsfw-v23/
├── tour-comfy/
│   ├── car.html          # frontend SPA (Studio Interface)
│   ├── trash.html        # frontend Database Consistency Panel SPA
│   ├── edit_api.py       # FastAPI backend
│   ├── database.py       # PostgreSQL helpers
│   ├── watcher.py        # output dir auto-processor
│   ├── config.json       # output_dir, comfy_url, etc.
│   └── workflow_qwen_edit.json  # ComfyUI graph template
├── a6000-comfy/          # ComfyUI install (symlinked to ~/comfyui)
├── AGENTS.md             # this file (System Documentation)
├── ARCHETECTURE.svg      # system architecture diagram
├── backlog.md            # feature backlog
└── requirements.txt      # Python deps

Configuration (config.json)

Key Default Purpose
output_dir ../output Where generated PNGs are saved
comfy_url http://127.0.0.1:8188 ComfyUI base URL
host 0.0.0.0 FastAPI bind host
port 8500 FastAPI bind port
max_area 1048576 Max pixel budget (1 MP)

Background Services

Service Script Purpose
comfyui-backend a6000-comfy/run_comfyui.sh Qwen GGUF inference
comfyui-api tour-comfy/start_api.sh FastAPI HTTP server
watcher tour-comfy/start_watcher.sh Monitors output dir, auto-processes dropped images