diff --git a/README.md b/README.md index 0b63494..e1440a9 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,78 @@ # ComfyUI Multi Prompt Generator -Generate multiple images from a prompt list in a single run, with built-in upscaling and LUT support. +[English](#english) | [한국어](#한국어) -![preview](https://github.com/mrm987/ComfyUI_Multi_Prompt_Generator/assets/preview.png) +--- -## Features +## English -- **Batch Generation** — Input a list of prompts (one per line), generate all at once -- **Auto Upscaling** — 2-pass upscale with model upscaler + img2img refinement -- **64x Alignment** — Automatically adjusts dimensions to prevent edge artifacts -- **LUT Support** — Apply .cube LUT files with adjustable strength -- **Live Preview** — See results during generation (toggleable) -- **Auto Save** — Images saved with auto-numbered filenames based on first tag +A ComfyUI custom node that processes a list of prompts in a single run to generate multiple images. +Includes upscaling, LUT application, and real-time preview. -## Installation +### Features -### Option 1: ComfyUI Manager (Coming Soon) -Search for "Multi Prompt Generator" in ComfyUI Manager. +- **Batch Generation** — Process line-separated prompt list at once +- **2-Pass Upscale** — Upscale model + img2img refinement +- **Size Alignment** — 64/8 pixel alignment to prevent edge artifacts +- **LUT Support** — Apply .cube LUT files +- **Real-time Preview** — Check results during generation, cancel if needed +- **Auto Save** — Automatic filename based on first tag + +### Installation -### Option 2: Manual Install ```bash cd ComfyUI/custom_nodes git clone https://github.com/mrm987/ComfyUI_Multi_Prompt_Generator.git ``` Restart ComfyUI. -## Usage +### Usage -1. Search for **"Multi Prompt Generator"** in the node menu +1. Search node: **"Multi Prompt Generator"** 2. Connect: Model, CLIP, VAE, Empty Latent, (optional) Upscale Model -3. Set your base prompt and negative prompt -4. Enter prompt list — one variation per line: +3. Enter prompt list — one per line: ``` smile, happy, bright eyes -angry, furrowed brow, clenched teeth -sad, downcast eyes, closed mouth -crying, tears, watery eyes +angry, furrowed brow +sad, downcast eyes +crying, tears ``` -## Inputs +### Inputs + +#### Required | Input | Type | Description | |-------|------|-------------| | model | MODEL | Checkpoint model | | clip | CLIP | CLIP model | | vae | VAE | VAE model | -| latent | LATENT | Empty latent image | +| latent | LATENT | Empty Latent Image | | base_prompt | STRING | Base prompt (combined with each line) | | negative_prompt | STRING | Negative prompt | -| prompt_list | STRING | One prompt variation per line | -| seed | INT | Random seed | +| prompt_list | STRING | Prompt list (line-separated) | +| seed | INT | Seed | | steps | INT | Sampling steps (default: 30) | | cfg | FLOAT | CFG scale (default: 5.0) | -| enable_upscale | BOOL | Enable 2-pass upscaling (default: True) | -| enable_preview | BOOL | Show preview during generation (default: True) | +| enable_upscale | BOOL | Enable upscale (default: True) | | save_prefix | STRING | Output folder name (default: "MultiPrompt") | -### Optional Inputs +#### Optional -| Input | Type | Description | -|-------|------|-------------| -| upscale_model | UPSCALE_MODEL | Upscale model (e.g., 2x-AnimeSharp) | -| scale_factor | FLOAT | Downscale after upscale (default: 0.7) | -| upscale_steps | INT | 2nd pass steps (default: 15) | -| upscale_cfg | FLOAT | 2nd pass CFG (default: 5.0) | -| upscale_denoise | FLOAT | 2nd pass denoise (default: 0.5) | -| lut_name | COMBO | LUT file from comfyui-propost/LUTs | -| lut_strength | FLOAT | LUT intensity (default: 0.3) | +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| upscale_model | UPSCALE_MODEL | - | Upscale model | +| scale_factor | FLOAT | 0.7 | Downscale ratio after upscale | +| upscale_steps | INT | 15 | 2nd pass steps | +| upscale_cfg | FLOAT | 5.0 | 2nd pass CFG | +| upscale_denoise | FLOAT | 0.5 | 2nd pass denoise | +| size_alignment | COMBO | "64" | Size alignment (64 / 8 / none) | +| lut_name | COMBO | "None" | LUT file selection | +| lut_strength | FLOAT | 0.3 | LUT strength | +| enable_preview | BOOL | True | Real-time preview | -## Output +### Output -Images are saved to: ``` output/[save_prefix]/01_smile_00001.png output/[save_prefix]/02_angry_00001.png @@ -80,23 +82,125 @@ output/[save_prefix]/03_sad_00001.png Filename format: `[index]_[first_tag]_[counter].png` -## Pipeline +### Pipeline ``` -For each prompt line: - 1. Combine: base_prompt + line +For each prompt: + 1. Combine base_prompt + current line 2. KSampler (1st pass) - 3. Preview (if enabled) - 4. Upscale Model → Resize to 64x → KSampler (2nd pass) - 5. Apply LUT (if selected) - 6. Preview update (if enabled) + 3. Show preview + 4. Upscale Model → Size alignment → KSampler (2nd pass) + 5. Apply LUT + 6. Update preview 7. Save ``` -## Requirements +### Notes -- ComfyUI -- (Optional) [comfyui-propost](https://github.com/Beinsezii/comfyui-propost) for LUT files +- **size_alignment**: 64 is safest. Use 64 if you see white edges +- **LUT files**: Auto-detected from `comfyui-propost/LUTs/` or `models/luts/` folder +- **Preview**: Check 1st pass result, cancel with Cancel button if not satisfied + +--- + +## 한국어 + +프롬프트 리스트를 한 번에 처리해서 여러 이미지를 생성하는 ComfyUI 커스텀 노드. +업스케일, LUT 적용, 실시간 프리뷰 기능 포함. + +### 기능 + +- **다중 프롬프트 배치 생성** — 줄바꿈으로 구분된 프롬프트 리스트를 한 번에 처리 +- **2-Pass 업스케일** — 업스케일 모델 + img2img 리파인 +- **크기 정렬** — 64/8배수 정렬로 테두리 아티팩트 방지 +- **LUT 적용** — .cube LUT 파일 지원 +- **실시간 프리뷰** — 생성 중 결과 확인, 마음에 안 들면 취소 가능 +- **자동 저장** — 첫 태그 기반 파일명 자동 생성 + +### 설치 + +```bash +cd ComfyUI/custom_nodes +git clone https://github.com/mrm987/ComfyUI_Multi_Prompt_Generator.git +``` +ComfyUI 재시작. + +### 사용법 + +1. 노드 검색: **"Multi Prompt Generator"** +2. 연결: Model, CLIP, VAE, Empty Latent, (선택) Upscale Model +3. 프롬프트 리스트 입력 — 한 줄에 하나씩: +``` +smile, happy, bright eyes +angry, furrowed brow +sad, downcast eyes +crying, tears +``` + +### 입력 + +#### 필수 + +| Input | Type | Description | +|-------|------|-------------| +| model | MODEL | 체크포인트 모델 | +| clip | CLIP | CLIP 모델 | +| vae | VAE | VAE 모델 | +| latent | LATENT | Empty Latent Image | +| base_prompt | STRING | 기본 프롬프트 (각 줄과 결합됨) | +| negative_prompt | STRING | 네거티브 프롬프트 | +| prompt_list | STRING | 프롬프트 리스트 (줄바꿈 구분) | +| seed | INT | 시드 | +| steps | INT | 샘플링 스텝 (기본: 30) | +| cfg | FLOAT | CFG 스케일 (기본: 5.0) | +| enable_upscale | BOOL | 업스케일 활성화 (기본: True) | +| save_prefix | STRING | 저장 폴더명 (기본: "MultiPrompt") | + +#### 선택 + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| upscale_model | UPSCALE_MODEL | - | 업스케일 모델 | +| scale_factor | FLOAT | 0.7 | 업스케일 후 다운스케일 비율 | +| upscale_steps | INT | 15 | 2차 샘플링 스텝 | +| upscale_cfg | FLOAT | 5.0 | 2차 CFG | +| upscale_denoise | FLOAT | 0.5 | 2차 디노이즈 | +| size_alignment | COMBO | "64" | 크기 정렬 (64 / 8 / none) | +| lut_name | COMBO | "None" | LUT 파일 선택 | +| lut_strength | FLOAT | 0.3 | LUT 강도 | +| enable_preview | BOOL | True | 실시간 프리뷰 | + +### 출력 + +``` +output/[save_prefix]/01_smile_00001.png +output/[save_prefix]/02_angry_00001.png +output/[save_prefix]/03_sad_00001.png +... +``` + +파일명: `[순번]_[첫태그]_[카운터].png` + +### 파이프라인 + +``` +각 프롬프트마다: + 1. base_prompt + 현재 줄 결합 + 2. KSampler (1차) + 3. 프리뷰 표시 + 4. Upscale Model → 크기 정렬 → KSampler (2차) + 5. LUT 적용 + 6. 프리뷰 업데이트 + 7. 저장 +``` + +### 참고 + +- **size_alignment**: 64가 가장 안전. 흰 테두리 생기면 64 사용 +- **LUT 파일**: `comfyui-propost/LUTs/` 또는 `models/luts/` 폴더의 .cube 파일 자동 인식 +- **프리뷰**: 1차 결과 확인 후 마음에 안 들면 Cancel로 중단 가능 + +--- ## License diff --git a/__init__.py b/__init__.py index 640f09c..7ebe69b 100644 --- a/__init__.py +++ b/__init__.py @@ -180,7 +180,6 @@ class MultiPromptGenerator: "steps": ("INT", {"default": 30, "min": 1, "max": 200}), "cfg": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 30.0, "step": 0.1}), "enable_upscale": ("BOOLEAN", {"default": True}), - "enable_preview": ("BOOLEAN", {"default": True}), "save_prefix": ("STRING", {"default": "MultiPrompt"}), }, "optional": { @@ -189,8 +188,10 @@ class MultiPromptGenerator: "upscale_steps": ("INT", {"default": 15, "min": 1, "max": 200}), "upscale_cfg": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 30.0, "step": 0.1}), "upscale_denoise": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + "size_alignment": (["64", "8", "none"],), "lut_name": (get_lut_files(),), "lut_strength": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 1.0, "step": 0.01}), + "enable_preview": ("BOOLEAN", {"default": True}), }, "hidden": { "unique_id": "UNIQUE_ID", @@ -284,20 +285,25 @@ class MultiPromptGenerator: s = torch.clamp(s.movedim(-3, -1), min=0, max=1.0) return s - def resize_to_64(self, image, scale_factor): - """이미지를 64배수 크기로 리사이즈""" + def resize_with_alignment(self, image, scale_factor, alignment="64"): + """이미지를 지정된 배수 크기로 리사이즈""" batch, height, width, channels = image.shape new_width = int(width * scale_factor) new_height = int(height * scale_factor) - # 64배수로 반올림 - new_width = ((new_width + 32) // 64) * 64 - new_height = ((new_height + 32) // 64) * 64 - - # 최소 크기 보장 - new_width = max(64, new_width) - new_height = max(64, new_height) + # 배수 정렬 + if alignment == "64": + new_width = ((new_width + 32) // 64) * 64 + new_height = ((new_height + 32) // 64) * 64 + new_width = max(64, new_width) + new_height = max(64, new_height) + elif alignment == "8": + new_width = ((new_width + 4) // 8) * 8 + new_height = ((new_height + 4) // 8) * 8 + new_width = max(8, new_width) + new_height = max(8, new_height) + # "none"이면 그대로 # BCHW로 변환 후 리사이즈 samples = image.movedim(-1, 1) @@ -348,10 +354,10 @@ class MultiPromptGenerator: }) def generate(self, model, clip, vae, latent, base_prompt, negative_prompt, prompt_list, - seed, steps, cfg, enable_upscale, enable_preview, save_prefix, + seed, steps, cfg, enable_upscale, save_prefix, upscale_model=None, scale_factor=0.7, upscale_steps=15, - upscale_cfg=5.0, upscale_denoise=0.5, - lut_name="None", lut_strength=0.3, unique_id=None): + upscale_cfg=5.0, upscale_denoise=0.5, size_alignment="64", + lut_name="None", lut_strength=0.3, enable_preview=True, unique_id=None): # prompt_list 파싱 lines = [line.strip() for line in prompt_list.strip().split("\n") if line.strip()] @@ -406,7 +412,7 @@ class MultiPromptGenerator: upscaled = self.upscale_with_model(upscale_model, image) # 64배수로 리사이즈 - resized = self.resize_to_64(upscaled, scale_factor) + resized = self.resize_with_alignment(upscaled, scale_factor, size_alignment) # VAE 인코딩 → 2차 샘플링 → 디코딩 latent_up = self.encode_vae(vae, resized) @@ -431,7 +437,14 @@ class MultiPromptGenerator: all_images.append(image) print(f"[MultiPrompt] Complete! Generated {len(all_images)} images.") - return {"ui": {"images": []}, "result": (all_images,)} + + # 마지막 프리뷰 정보 반환 (UI에 유지) + ui_images = [] + if enable_preview and unique_id is not None and len(lines) > 0: + last_preview = f"multiprompt_preview_{unique_id}_{len(lines)-1}.png" + ui_images = [{"filename": last_preview, "subfolder": "", "type": "temp"}] + + return {"ui": {"images": ui_images}, "result": (all_images,)} def _get_next_counter(self, prefix): """다음 카운터 번호"""