Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fe919558f | ||
|
|
6948b2c766 | ||
|
|
2771dd17d0 | ||
|
|
f145728829 | ||
|
|
1e146fa465 | ||
|
|
c70c8f926b | ||
|
|
64fab713a5 | ||
|
|
b95f51910c | ||
|
|
6b04947fc6 | ||
|
|
8a491bfb94 | ||
|
|
994c620cbd | ||
|
|
d1ee798b60 | ||
|
|
59aa08dff0 | ||
|
|
aa8f4e81ac | ||
|
|
f590861bd5 | ||
|
|
7514e28048 | ||
|
|
e8e2d2ad1d | ||
|
|
7708715590 | ||
|
|
c9c79988f1 | ||
|
|
9ce5bbb706 | ||
|
|
8b630eb6eb | ||
|
|
840c962fc9 | ||
|
|
6a41445b8a | ||
|
|
b71f8e59b4 | ||
|
|
d696d48146 | ||
|
|
6a0f424f28 | ||
|
|
ba4bb687f1 | ||
|
|
3bd8a431ac | ||
|
|
0ce81f2a49 | ||
|
|
9e7d0ee646 | ||
|
|
75e606579c | ||
|
|
6440a7f433 | ||
|
|
b3dc7e7551 | ||
|
|
2522687dd5 | ||
|
|
f3ef9dedf9 | ||
|
|
f53442c8d5 | ||
|
|
24051fb151 | ||
|
|
a76a5da0c5 | ||
|
|
21a6b155e8 | ||
|
|
8bc20d5e00 | ||
|
|
84b63adb11 | ||
|
|
725fb2ba37 | ||
|
|
4f19674299 | ||
|
|
558084416d | ||
|
|
a984dcb118 | ||
|
|
3f9193997a | ||
|
|
7372428114 | ||
|
|
ac6eece747 | ||
|
|
95b65ed1b1 | ||
|
|
30cfd2f2e4 | ||
|
|
2e3a566bb4 | ||
|
|
8fe1a9d312 | ||
|
|
4fcbfc7b5b | ||
|
|
651ddc2225 | ||
|
|
bd4606f27a | ||
|
|
8a72294f27 | ||
|
|
9ddd5f613e | ||
|
|
39c90bbf62 | ||
|
|
6ad4a18537 | ||
|
|
5e7d472738 | ||
|
|
d73f3db265 | ||
|
|
cd06069b52 | ||
|
|
114d091965 | ||
|
|
06da285a09 | ||
|
|
8f52a1d378 | ||
|
|
c7962bf784 | ||
|
|
a895d2a758 | ||
|
|
34ded92d8d | ||
|
|
1b87239553 | ||
|
|
cbc9f3abfd | ||
|
|
7d46147887 | ||
|
|
c5fe6351ea | ||
|
|
ac7a5a1f2f | ||
|
|
73ab537e86 | ||
|
|
6fd5f60383 | ||
|
|
9c9b5555fc | ||
|
|
450a25e5e8 | ||
|
|
e7080f9574 | ||
|
|
2086f00603 | ||
|
|
896729ccd4 | ||
|
|
207860edc3 | ||
|
|
4074473b90 | ||
|
|
11c1490140 | ||
|
|
7c79712fc8 | ||
|
|
fb35a1d032 | ||
|
|
060fcc4475 | ||
|
|
aedb908a3f | ||
|
|
f9718ea3e1 | ||
|
|
0bafe2117a | ||
|
|
2c1a23777d | ||
|
|
dde4c82463 | ||
|
|
72cb484c40 | ||
|
|
0d15d15cf3 | ||
|
|
1393a46d67 | ||
|
|
27421363bf | ||
|
|
01400a541d | ||
|
|
c981354387 | ||
|
|
c92c8bfb1f | ||
|
|
9334025801 | ||
|
|
144893ecd2 | ||
|
|
bce8505ce2 | ||
|
|
0992657e54 | ||
|
|
2fa036992b | ||
|
|
9432a34c38 | ||
|
|
d4e8ee28fb | ||
|
|
34b8e83831 | ||
|
|
e29d480a1a |
@@ -26,6 +26,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install .[dev]
|
||||
pip install torch --extra-index-url https://download.pytorch.org/whl/cpu
|
||||
- name: Run Linting
|
||||
run: |
|
||||
ruff check .
|
||||
|
||||
@@ -11,3 +11,5 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: comfy-org/node-diff@main
|
||||
with:
|
||||
base_ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
@@ -100,3 +100,9 @@ cookiecutter-pypackage-env/
|
||||
*.code-workspace
|
||||
.vscode/
|
||||
/.vscode
|
||||
|
||||
# Claude Code
|
||||
CLAUDE.md
|
||||
.claude/
|
||||
.playwright-mcp/
|
||||
debug_screenshots/
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
||||
|
||||
## Project overview
|
||||
|
||||
LanPaint is a ComfyUI extension that implements a training-free diffusion inpainting sampler based on Langevin dynamics ("think mode"). It lets any diffusion model iterate multiple times within each denoising step before committing to an output, improving inpainting quality without a specialized model.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Lint
|
||||
ruff check .
|
||||
|
||||
# Format
|
||||
ruff format .
|
||||
|
||||
# Type check (requires mypy)
|
||||
mypy
|
||||
```
|
||||
|
||||
There is no build step — this is installed directly as a ComfyUI custom node by cloning into `custom_nodes/LanPaint`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Entry point and ComfyUI integration
|
||||
|
||||
`__init__.py` is the ComfyUI entry point. When ComfyUI loads this module, it imports `NODE_CLASS_MAPPINGS` and `NODE_DISPLAY_NAME_MAPPINGS` from `src/LanPaint/nodes.py`. The `WEB_DIRECTORY = "./web"` tells ComfyUI where to find the frontend JS.
|
||||
|
||||
When imported **without** ComfyUI (e.g., in CI), `_install_lightweight_runtime_stubs()` creates dummy `torch`, `comfy`, `nodes`, and `comfyui_version` modules so `nodes.py` can still be imported for node discovery. The real stubs are in `src/LanPaint/types.py` (`LangevinState` NamedTuple).
|
||||
|
||||
### Core algorithm (`src/LanPaint/lanpaint.py`)
|
||||
|
||||
`LanPaint.__call__()` is the main algorithm. It runs `n_steps` Langevin dynamics sub-iterations within each outer denoising step:
|
||||
|
||||
1. Replaces the masked region with the noise-scaled known latent (`scale_latent_inpaint`)
|
||||
2. In each inner step, computes a score function via `score_model()` — which calls the diffusion model to get `x_0` and `x_0_BIG` (high-CFG) predictions — then runs one Langevin sub-step
|
||||
3. After iterations, denoises the result to produce the final `x_0` output
|
||||
|
||||
The `LanPaintEarlyStopper` (`src/LanPaint/earlystop.py`) can terminate inner iterations early based on semantic convergence or a custom distance function, contributed by `@godnight10061`.
|
||||
|
||||
### Monkey-patching mechanism (`src/LanPaint/nodes.py`)
|
||||
|
||||
`override_sample_function()` is a context manager that temporarily replaces three functions on ComfyUI's `comfy.samplers` module:
|
||||
- `CFGGuider.outer_sample` → `CFGGuider_LanPaint.outer_sample` (handles mask preparation and WAN22 video models)
|
||||
- `CFGGuider.predict_noise` → `CFGGuider_LanPaint.predict_noise` (dual CFG output — normal + BIG)
|
||||
- `KSAMPLER.sample` → `KSAMPLER.sample` (injects `LanPaint` as the paint method for inpainting steps)
|
||||
|
||||
These are monkey-patches, not subclass overrides, because ComfyUI internally constructs `CFGGuider` and `KSAMPLER` instances directly. The monkey-patches are scoped to a single `nodes.common_ksampler()` call.
|
||||
|
||||
### Sampler nodes
|
||||
|
||||
There are four sampler nodes, two "basic" and two "advanced":
|
||||
|
||||
- **LanPaint_KSampler** / **LanPaint_KSamplerAdvanced** — use `nodes.common_ksampler()`. The advanced variant exposes all LanPaint hyperparameters (Lambda, StepSize, Beta, Friction, EarlyStop, InnerThreshold, InnerPatience).
|
||||
- **LanPaint_SamplerCustom** / **LanPaint_SamplerCustomAdvanced** — use `comfy.sample.sample_custom()` / `guider.sample()`, for use with custom sigmas/samplers/guiders.
|
||||
|
||||
All sampler nodes attach LanPaint parameters to `model` (the model patcher object) and set `model_options["video_inpainting"]` for video mode.
|
||||
|
||||
Additional nodes:
|
||||
- **LanPaint_MaskBlend** — blends before/after images with a Gaussian-smoothed mask for seamless boundaries
|
||||
- **LanPaint_UpSale_LatentNoiseMask** — generates a checkerboard noise mask (currently commented out in `NODE_CLASS_MAPPINGS`)
|
||||
|
||||
### Numerical utilities (`src/LanPaint/utils.py`)
|
||||
|
||||
`StochasticHarmonicOscillator` simulates the Langevin dynamics step analytically. It computes the exact mean and covariance of the position/velocity after time `t` and samples from a multivariate normal. The module also contains numerically stable implementations of `(e^x - 1)/x`, `(e^x - 1 - x)/x^2`, hyperbolic functions, and helper coefficients (`zeta1`, `zeta2`, `Zcoefs`).
|
||||
|
||||
### Dual CFG
|
||||
|
||||
LanPaint uses two classifier-free guidance scales simultaneously:
|
||||
- `cfg` — the standard CFG scale used for the known-region score
|
||||
- `cfg_BIG` — a second (often higher) CFG scale used for the masked-region score via `score_model()`. In "Prompt First" mode, `cfg_BIG = 0*cfg - 0.5 = -0.5`, which effectively disables the second guidance.
|
||||
|
||||
### Frontend (`web/lanpaint_info.js`)
|
||||
|
||||
A ComfyUI extension that adds a "More Info, Bug Report, Star on GitHub" button to each LanPaint sampler node in the UI.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
`COMFYUI_VERSION_060_OR_NEWER` gates behavior differences for mask reshaping between ComfyUI versions < 0.6.0 and >= 0.6.0, which changed the latent tensor dimension convention.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests use pytest. The test suite is designed to run without ComfyUI installed — `conftest.py` adds the project root to `sys.path`, and `_install_lightweight_runtime_stubs()` provides dummy modules. The primary integration test (`test_package_imports_without_comfy`) validates that the package can be imported and node mappings are present.
|
||||
|
||||
CI uses `comfy-org/node-diff` to validate backwards compatibility of node interfaces on PRs.
|
||||
|
||||
## Running ComfyUI on this machine
|
||||
|
||||
### Installed locations
|
||||
|
||||
| What | Path | Version |
|
||||
|---|---|---|
|
||||
| User data (custom_nodes, models, output) | `E:\CompyUI` | — |
|
||||
| ComfyUI Desktop app (Electron shell) | `C:\Users\scraed\AppData\Local\Programs\@comfyorgcomfyui-electron` | 0.21.1 bundled |
|
||||
| **Actual ComfyUI (used by Desktop)** | `C:\Users\scraed\ComfyUI-Installs\ComfyUI\ComfyUI\` | **0.24.1** |
|
||||
| Python venv | `E:\CompyUI\.venv` | Python 3.12.6 |
|
||||
| Shared models | `C:\Users\scraed\ComfyUI-Shared\models\` | — |
|
||||
| Extra models (Y drive) | `Y:\ComfyData\models\` | — |
|
||||
|
||||
### Launch headless
|
||||
|
||||
The ComfyUI at `ComfyUI-Installs` is the one to use — the bundled Electron version (0.21.1) is outdated and lacks nodes like `Ideogram4Scheduler`, `DualModelGuider`, `CFGOverride`.
|
||||
|
||||
```powershell
|
||||
$env:PYTHONUTF8=1
|
||||
$env:PYTHONIOENCODING='utf-8'
|
||||
& E:\CompyUI\.venv\Scripts\python.exe `
|
||||
C:\Users\scraed\ComfyUI-Installs\ComfyUI\ComfyUI\main.py `
|
||||
--base-directory E:\CompyUI `
|
||||
--listen 127.0.0.1 --port 8188 `
|
||||
--disable-auto-launch
|
||||
```
|
||||
|
||||
The `PYTHONUTF8` and `PYTHONIOENCODING` env vars are required on this machine (Chinese Windows, GBK codec chokes on emoji in custom node logs).
|
||||
|
||||
### Model paths
|
||||
|
||||
ComfyUI loads `extra_model_paths.yaml` from **the same directory as `main.py`**, not from `--base-directory`. The config at `C:\Users\scraed\ComfyUI-Installs\ComfyUI\ComfyUI\extra_model_paths.yaml` points to both the Shared and Y-drive model collections.
|
||||
|
||||
### GPU
|
||||
|
||||
Dual NVIDIA RTX A6000 (49GB VRAM each), PyTorch 2.9.1+cu130.
|
||||
|
||||
### Workflows
|
||||
|
||||
Saved workflows are at `E:\CompyUI\user\default\workflows\`. There are 74 workflows covering LanPaint inpainting, Qwen image edit, Flux, HunYuan, HiDream, Wan video, and Ideogram4 generation.
|
||||
|
||||
### Running a workflow — practical notes
|
||||
|
||||
**Workflow format:** LanPaint examples are PNGs with embedded workflow JSON (`PIL.Image.open(png).info['workflow']`). Save to `E:\CompyUI\user\default\workflows\` to load them from the web UI.
|
||||
|
||||
**Common errors and their fixes:**
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|---|---|---|
|
||||
| Models missing | `extra_model_paths.yaml` not at the ComfyUI root (same dir as `main.py`) | Create one with `base_path` sections pointing to `C:\Users\scraed\ComfyUI-Shared\` and `Y:\ComfyData\` |
|
||||
| Nodes missing | UI-only nodes (`MarkdownNote`, `PreviewAny`) | Usually safe to ignore |
|
||||
| Missing image input | `LoadImage` node needs a mask | Upload `Masked_Load_Me_in_Loader.png` from the example folder |
|
||||
| "Failed to convert input to FLOAT" | `widgets_values` array is too short for the current node signature — params were added/inserted since the workflow was created | Expand `widgets_values` to match the current number of non-linked inputs, using correct types (INT, FLOAT, STRING, COMBO) |
|
||||
|
||||
**Testing workflow changes:** The easiest way to verify a LanPaint workflow works is to load it in the headless ComfyUI web interface (Playwright or manually), upload the mask if needed, and click Run. Monitor the page title for `[N%]` progress or grep the server log for "Prompt executed".
|
||||
|
||||
**UTF-8 on Chinese Windows:** Always set `$env:PYTHONUTF8=1` and `$env:PYTHONIOENCODING='utf-8'` before launching — the GBK codec cannot handle emoji in custom node logs.
|
||||
|
||||
### Workflow JSON debugging
|
||||
|
||||
**Link format differs by context:** Top-level `links` are arrays `[id, from_node, from_slot, to_node, to_slot, "TYPE"]`. Subgraph links (in `definitions.subgraphs[].links`) are dicts `{"id": N, "origin_id": N, ...}`. Mixing formats causes silent failures.
|
||||
|
||||
**ComfyUI clears link fields on save:** After loading+saving a workflow in ComfyUI, `inputs[].link` and `outputs[].links` fields on nodes are often reset to `null`/`[]` — even though the correct links still exist in the `links` array. This makes the frontend render nodes as disconnected. After any ComfyUI save, audit and restore these fields.
|
||||
|
||||
**Subgraph instance ↔ definition name matching:** Instance inputs (on the subgraph node) must have `name` matching the definition `inputs[].name`. Mismatch causes "No link found in parent graph" errors.
|
||||
|
||||
**Virtual nodes:** Inside subgraphs, `-10` = input node, `-20` = output node. Links from `-10` use the slot matching the definition input index.
|
||||
|
||||
**Node ID uniqueness:** IDs must be unique across top-level AND all subgraph nodes combined. Duplicates cause silent connection failures.
|
||||
|
||||
**VAE dimension alignment:** Always use `VAEEncode → VAEDecode → GetImageSize` to derive target dimensions before the actual encode path that feeds the sampler. VAEs require input dimensions divisible by a model-specific factor (e.g., 8). The round-trip forces alignment and captures the clean dimensions for all downstream `ImageScale` nodes. Without this, the mask and latent may have mismatched dimensions in `SetLatentNoiseMask`, causing cryptic errors. Don't remove this pattern unless you fully understand the VAE's input constraints.
|
||||
|
||||
**Widget value ordering when replacing nodes:** When swapping a node (e.g., `KSampler` → `LanPaint_KSampler`), the old widget values array does not map 1:1 to the new node's `INPUT_TYPES`. Always clear the old array and set widget values to exactly match the new node's non-linked inputs — in the correct order, with the correct count. Appending new params to stale old values shifts everything and produces NaN in the UI.
|
||||
|
||||
### Workflow PNG metadata conventions
|
||||
|
||||
Example directories follow this pattern:
|
||||
| File | Metadata |
|
||||
|------|----------|
|
||||
| `Masked_Load_Me_in_Loader.png` | Plain PNG, no metadata |
|
||||
| `Original_No_Mask.png` | Plain PNG, no metadata |
|
||||
| `InPainted_Drag_Me_to_ComfyUI.png` | Must have embedded `workflow` + `prompt` tEXt chunks (auto from SaveImage) |
|
||||
|
||||
**Strip metadata:** `img = Image.open(src); img.save(dst, 'PNG')` — Pillow drops tEXt chunks on re-save.
|
||||
|
||||
**example_workflows/:** Each workflow gets a `.json` + `.jpg` pair. The `.jpg` is a preview derived from the output PNG: `img.convert('RGB').save('name.jpg', 'JPEG', quality=95)`.
|
||||
|
||||
### Kill server safely
|
||||
|
||||
Match the ComfyUI install path to avoid killing other Python programs:
|
||||
|
||||
```powershell
|
||||
Get-CimInstance Win32_Process -Filter "Name='python.exe'" | Where-Object { $_.CommandLine -match 'ComfyUI-Installs.*main\.py' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
||||
```
|
||||
|
||||
### Playwright/Browser automation
|
||||
|
||||
- **Beforeunload dialog:** ComfyUI shows `系统可能不会保存您所做的更改` when navigating away from a modified workflow. Call `browser_handle_dialog(accept=true)` to dismiss.
|
||||
- **File choosers:** LoadImage nodes with IMAGEUPLOAD widgets spawn file chooser modals on page load. Dismiss with `browser_file_upload(paths=[])` — there may be multiple.
|
||||
- **IndexedDB cache:** After modifying a workflow JSON on disk, the frontend may load a cached version. Close the workflow tab and re-open it (hard refresh alone is not sufficient).
|
||||
- **Run button:** Use `page.getByTestId('queue-button').click()` — more reliable than text matching.
|
||||
- **Progress:** `page.evaluate('() => document.title')` — format `[N%][M%] Node`. Completed when title returns to `*WorkflowName - ComfyUI`.
|
||||
- **LoadImage via JS:** `app.graph.getNodeById(id).widgets.find(w => w.name === 'image').callback('filename.png')` to set the image without file picker.
|
||||
|
||||
## Commit conventions
|
||||
|
||||
Do NOT include the `Co-Authored-By: Codex <noreply@anthropic.com>` trailer in commit messages. All commits should be attributed solely to the git user.
|
||||
@@ -7,13 +7,19 @@
|
||||
[](https://huggingface.co/charrywhite/LanPaint)
|
||||
[](https://scraed.github.io/scraedBlog/)
|
||||
[](https://github.com/scraed/LanPaint/stargazers)
|
||||
[](https://discord.gg/aCGZutBV)
|
||||
[](https://discord.gg/yN5wYDE6W4)
|
||||
</div>
|
||||
|
||||
|
||||
Universally applicable inpainting ability for every model. LanPaint sampler lets the model "think" through multiple iterations before denoising, enabling you to invest more computation time for superior inpainting quality.
|
||||
|
||||
This is the official implementation of ["LanPaint: Training-Free Diffusion Inpainting with Asymptotically Exact and Fast Conditional Sampling"](https://arxiv.org/abs/2502.03491), accepted by TMLR. The repository is for ComfyUI extension. Local Python benchmark code is published here: [LanPaintBench](https://github.com/scraed/LanPaintBench).
|
||||
This is the official implementation of ["LanPaint: Training-Free Diffusion Inpainting with Asymptotically Exact and Fast Conditional Sampling"](https://arxiv.org/abs/2502.03491), accepted by TMLR.
|
||||
|
||||
The repository is for ComfyUI extension.
|
||||
|
||||
Diffusers Support: [LanPaint-Diffusers](https://github.com/charrywhite/LanPaint-diffusers) by [@charrywhite](https://github.com/charrywhite/)
|
||||
|
||||
Benchmark code for paper reproduce: [LanPaintBench](https://github.com/scraed/LanPaintBench).
|
||||
|
||||
## Citation
|
||||
|
||||
@@ -31,14 +37,33 @@ note={}
|
||||
```
|
||||
**🎉 NEW 2026: Join our discord!**
|
||||
|
||||
[Join our Discord](https://discord.gg/aCGZutBV) to share experiences, discuss features, and explore future development.
|
||||
[Join our Discord](https://discord.gg/yN5wYDE6W4) to share experiences, discuss features, and explore future development.
|
||||
|
||||
`v1.5.0` fixes an important hidden bug that reduced performance and could blur images (especially with `z-image-base`) and also boosts overall LanPaint performance across other models.
|
||||
If your inpainting results have wierd (glowing / broken) mask boundary, check this [issue](https://github.com/scraed/LanPaint/issues/80).
|
||||
|
||||
**🎬 NEW: LanPaint now supports MiniMax H3 video + audio inpainting!**
|
||||
|
||||
| Masked Input (paint in the editor) | Mask (visible overlay) | Inpainted Result |
|
||||
|:----------------------------------:|:----------------------:|:----------------:|
|
||||
|  |  |  |
|
||||
|
||||
Check our latest [MiniMax H3 Example](#minimax-h3-video--audio-inpainting-av-pipeline): paint per-frame video masks and audio intervals in one editor, inpaint video + audio in a single pass, and export the masks into the video file itself.
|
||||
|
||||
**🎬 NEW: LanPaint now supports inpainting and outpainting based on Z-Image!**
|
||||
|
||||
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
**🎬 NEW: LanPaint now supports Z-Image-Base too!**
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
|
||||
**🎬 NEW: LanPaint now supports video inpainting and outpainting based on Wan 2.2!**
|
||||
|
||||
@@ -52,7 +77,25 @@ note={}
|
||||
|
||||
</div>
|
||||
|
||||
Check our latest [Wan 2.2 Video Examples](#video-examples-beta), [Wan 2.2 Image Examples](#example-wan22-inpaintlanpaint-k-sampler-5-steps-of-thinking), and
|
||||
**🎬 NEW: LanPaint now supports Anima!**
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
**🎬 NEW: LanPaint now supports Ideogram4!**
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
**🎬 NEW: LanPaint now supports Krea2!**
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
Check our latest [Krea2 Example](#example-krea2-inpaintlanpaint-k-sampler-3-steps-of-thinking), [Ideogram4 Example](#example-ideogram4-inpaintlanpaint-custom-sampler-advanced-5-steps-of-thinking), [Anima Example](#example-anima-inpaintlanpaint-k-sampler-5-steps-of-thinking), [Wan 2.2 Video Examples](#video-examples-beta), [Wan 2.2 Image Examples](#example-wan22-inpaintlanpaint-k-sampler-5-steps-of-thinking), and
|
||||
[Qwen Image Edit 2509](#example-qwen-edit-2509-inpaint) support.
|
||||
|
||||
|
||||
@@ -67,7 +110,12 @@ Check our latest [Wan 2.2 Video Examples](#video-examples-beta), [Wan 2.2 Image
|
||||
- [Resource Consumption](#resource-consumption)
|
||||
- [Image Examples](#image-examples)
|
||||
- [Flux.2.Dev](#example-flux2dev-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
- [Flux 2 klein](#example-flux-2-klein-inpaintlanpaint-k-sampler-2-steps-of-thinking)
|
||||
- [Z-image](#example-z-image-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
- [Z-image-base](#example-z-image-base-inpaintlanpaint-k-sampler-3-steps-of-thinking)
|
||||
- [Ideogram4](#example-ideogram4-inpaintlanpaint-custom-sampler-advanced-5-steps-of-thinking)
|
||||
- [Krea2](#example-krea2-inpaintlanpaint-k-sampler-3-steps-of-thinking)
|
||||
- [Anima](#example-anima-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
- [Hunyuan T2I](#example-hunyuan-t2i-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
- [Wan 2.2 T2I](#example-wan22-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
- [Wan 2.2 T2I with reference](#example-wan22-partial-inpaintlanpaint-k-sampler-5-steps-of-thinking)
|
||||
@@ -90,13 +138,16 @@ Check our latest [Wan 2.2 Video Examples](#video-examples-beta), [Wan 2.2 Image
|
||||
|
||||
## Features
|
||||
|
||||
- **Universal Compatibility** – Works instantly with almost any model (**Z-image, Hunyuan, Wan 2.2, Qwen Image/Edit, HiDream, SD 3.5, Flux-series, SDXL, SD 1.5 or custom LoRAs**) and ControlNet.
|
||||
- **Universal Compatibility** – Works instantly with almost any model (**Ideogram4, Krea2, Z-image, Z-image-base, Hunyuan, Wan 2.2, Qwen Image/Edit, Anima, HiDream, SD 3.5, Flux-series, SDXL, SD 1.5 or custom LoRAs**) and ControlNet.
|
||||

|
||||
- **No Training Needed** – Works out of the box with your existing model.
|
||||
- **Easy to Use** – Same workflow as standard ComfyUI KSampler.
|
||||
- **Flexible Masking** – Supports any mask shape, size, or position for inpainting/outpainting.
|
||||
- **No Workarounds** – Generates 100% new content (no blending or smoothing) without relying on partial denoising.
|
||||
- **Beyond Inpainting** – You can even use it as a simple way to generate consistent characters.
|
||||
- **Video Mask Editor** – Paint per-frame inpainting masks directly on a video inside ComfyUI (`LanPaint_VideoMaskEditor`): pick the video file, open the editor, paint masks on keyframes, and the masks in between are interpolated automatically with a live preview. Mask = 1 regenerates, 0 keeps.
|
||||
- **MiniMax H3 Video + Audio Inpainting** – Inpaint video **and audio** in one pass: paint per-frame video masks and audio intervals in the same editor, encode both streams into one nested latent (`LanPaint_AVEncode`), sample, then `LanPaint_AVDecode` merges the inpainted video/audio back into the original with a mask-blended boundary, preserving the source fps and bit depth.
|
||||
- **Masks Live in the Video** – The editor can export the painted masks into the video file itself (mp4 metadata, via a new "masked" copy - the original is never modified). Share that single mp4 and the recipient gets the masks back automatically when they load it.
|
||||
|
||||
**Warning**: LanPaint has degraded performance on distillation models, such as Flux.dev, due to a similar [issue with LORA training](https://medium.com/@zhiwangshi28/why-flux-lora-so-hard-to-train-and-how-to-overcome-it-a0c70bc59eaf). Please use low flux guidance (1.0-2.0) to mitigate this [issue](https://github.com/scraed/LanPaint/issues/30).
|
||||
|
||||
@@ -162,6 +213,30 @@ Extend your videos beyond their original boundaries with LanPaint's video outpai
|
||||
|
||||
You need to follow the ComfyUI version of [Wan2.2 T2V workflow](https://docs.comfy.org/tutorials/video/wan/wan2_2) to download and install the T2V model.
|
||||
|
||||
### MiniMax H3 Video + Audio Inpainting (AV pipeline)
|
||||
|
||||
LanPaint's AV pipeline inpaints video **and audio** together with the MiniMax H3 model. Paint both masks in one editor session, run a single sampler pass on the nested AV latent, and get back a merged video at the original fps and bit depth.
|
||||
|
||||
*Example: MiniMax H3, 864x480, 124 frames, LanPaint Sampler Custom (Advanced)*
|
||||
|
||||
| Masked Input (paint in the editor) | Mask (visible overlay) | Inpainted Result |
|
||||
|:----------------------------------:|:----------------------:|:----------------:|
|
||||
|  |  |  |
|
||||
|
||||

|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_29) · [Workflow JSON](https://github.com/scraed/LanPaint/blob/master/example_workflows/MiniMax_H3_AV_EncodeDecode_Inpaint.json)
|
||||
|
||||
**How it works:**
|
||||
1. `LanPaint_VideoMaskEditor` – pick the video, open the editor, paint per-frame video masks on keyframes (SDF-interpolated in between) and drag audio intervals on the waveform. Mask = 1 regenerates, 0 keeps.
|
||||
2. `LanPaint_AVEncode` – encodes the video frames and the audio track into one nested latent with the masks attached.
|
||||
3. Run the sampler as usual (the audio stream runs on its own shifted sigma schedule).
|
||||
4. `LanPaint_AVDecode` – decodes the nested latent, merges the inpainted video with the original (mask-blended boundary) and the inpainted audio inside the masked intervals (with a short crossfade), and writes the result at the original fps and bit depth.
|
||||
|
||||
**Export masks into the video:** the editor's **Export mask video** button remuxes a new `<name>_masked.mp4` copy with the masks embedded in the file metadata (the original file is never modified). Open that file in the editor later - or share it with someone - and the masks are restored automatically.
|
||||
|
||||
Download the models from [MiniMax H3 on Hugging Face](https://huggingface.co/MiniMaxAI/MiniMax-H3) and follow the [ComfyUI MiniMax H3 docs](https://docs.comfy.org/tutorials/video/minimax/minimax_h3).
|
||||
|
||||
### Resource Consumption
|
||||
|
||||
|
||||
@@ -229,6 +304,38 @@ You need to follow the ComfyUI version of [Wan2.2 T2V workflow](https://docs.com
|
||||
|
||||
## Image Examples
|
||||
|
||||
### Example Anima: InPaint(LanPaint K Sampler, 5 steps of thinking)
|
||||
We are excited to announce that LanPaint now supports inpainting with the Anima text-to-image model.
|
||||
|
||||
<details open>
|
||||
<summary>View Original / Masked / Inpainted Comparison</summary>
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
</details>
|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_26)
|
||||
|
||||
[Model Used in This Example](https://huggingface.co/circlestone-labs/Anima)
|
||||
|
||||
### Example Ideogram4: InPaint(LanPaint Custom Sampler Advanced, 5 steps of thinking)
|
||||
We are excited to announce that LanPaint now supports inpainting with the Ideogram4 text-to-image model.
|
||||
|
||||
<details open>
|
||||
<summary>View Original / Masked / Inpainted Comparison</summary>
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
</details>
|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_27)
|
||||
|
||||
[Model Used in This Example](https://huggingface.co/Comfy-Org/Ideogram-4)
|
||||
|
||||
### Example Hunyuan T2I: InPaint(LanPaint K Sampler, 5 steps of thinking)
|
||||
We are excited to announce that LanPaint now supports inpainting with Hunyuan text to image generation.
|
||||
|
||||
@@ -273,6 +380,40 @@ LanPaint also supports inpainting with the Z-image text-to-image model.
|
||||
|
||||
You can download the Z-image model for ComfyUI from [Z-image](https://docs.comfy.org/zh-CN/tutorials/image/z-image/z-image-turbo).
|
||||
|
||||
### Example Z-image-base: InPaint(LanPaint K Sampler, 3 steps of thinking)
|
||||
LanPaint also supports inpainting with the Z-image-base model.
|
||||
|
||||
**Warning (stability)**: Z-image-base can easily diverge with LanPaint. Start with **small `LanPaint_StepSize`** and **fewer thinking iterations** (lower `LanPaint_NumSteps`) and increase gradually only if stable.
|
||||
|
||||
<details open>
|
||||
<summary>View Original / Masked / Inpainted Comparison</summary>
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
</details>
|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_25)
|
||||
|
||||
Workflow template (JSON): [Z_image_base_Inpaint.json](https://github.com/scraed/LanPaint/blob/master/example_workflows/Z_image_base_Inpaint.json)
|
||||
|
||||
### Example Krea2: InPaint(LanPaint K Sampler, 3 steps of thinking)
|
||||
We are excited to announce that LanPaint now supports inpainting with the Krea2 Turbo text-to-image model.
|
||||
|
||||
<details open>
|
||||
<summary>View Original / Masked / Inpainted Comparison</summary>
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
</details>
|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_28)
|
||||
|
||||
[Model Used in This Example](https://huggingface.co/Comfy-Org/Krea-2)
|
||||
|
||||
### Example Wan2.2: Partial InPaint(LanPaint K Sampler, 5 steps of thinking)
|
||||
Sometimes we don't want to inpaint completely new content, but rather let the inpainted image reference the original image. One option to achieve this is to inpaint with an edit model like Qwen Image Edit. Another option is to perform a partial inpaint: allowing the diffusion process to start at some middle steps rather than from 0.
|
||||
|
||||
@@ -342,6 +483,22 @@ You need to follow the ComfyUI version of [SD 3.5 workflow](https://comfyui-wiki
|
||||
|
||||
(Note: Prompt First mode is disabled on Flux.2.Dev. As it does not use CFG guidance.)
|
||||
|
||||
### Example Flux 2 klein: InPaint(LanPaint K Sampler, 2 steps of thinking)
|
||||
|
||||
<details open>
|
||||
<summary>View Original / Masked / Inpainted Comparison</summary>
|
||||
|
||||
| Original | Masked | Inpainted |
|
||||
|:--------:|:------:|:---------:|
|
||||
|  |  |  |
|
||||
|
||||
</details>
|
||||
|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_24)
|
||||
|
||||
[Model Used in This Example](https://docs.comfy.org/zh-CN/tutorials/flux/flux-2-klein). If you have quality problem on Comfy 0.11 and 0.12, check [this issue](https://github.com/scraed/LanPaint/issues/80).
|
||||
|
||||
|
||||
### Example Flux: InPaint(LanPaint K Sampler, 5 steps of thinking)
|
||||

|
||||
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_7)
|
||||
@@ -462,6 +619,21 @@ Submit a PR to add your tutorial/video here, or open an [Issue](https://github.c
|
||||
[Working togather with crop&stitch](https://github.com/scraed/LanPaint/issues/46)
|
||||
|
||||
## Updates
|
||||
- 2026/08/12
|
||||
- `v2.1.0`: Significantly accelerated LanPaint using a new schedule mechanism.
|
||||
- Fix bugs for MiniMax H3 on the latest ComfyUI.
|
||||
- 2026/08/09
|
||||
- Add MiniMax H3 video + audio inpainting support (Example_29): paint per-frame video masks and audio intervals in one editor session, encode both streams into a nested AV latent, sample once, and decode back with the source fps and bit depth preserved.
|
||||
- The mask editor can export the masks into the video itself (mp4 metadata) - share a single video file and the masks travel with it.
|
||||
- 2026/06/27
|
||||
- Add Krea2 inpainting support with LanPaint KSampler (Example_28).
|
||||
- Add Ideogram4 inpainting support with LanPaint Custom Sampler Advanced (Example_27).
|
||||
- 2026/05/20
|
||||
- Add Anima inpainting and outpainting support (Example_26).
|
||||
- 2026/03/02
|
||||
- `v1.5.0`: Fixed a hidden bug that hurt performance and caused image blur (especially on `z-image-base`), and improved overall LanPaint performance on other models too.
|
||||
- 2026/01/30
|
||||
- Add Z-image-base documentation and Example_25 workflow images.
|
||||
- 2025/08/08
|
||||
- Add Qwen image support
|
||||
- 2025/06/21
|
||||
|
||||
@@ -10,7 +10,103 @@ __author__ = """LanPaint"""
|
||||
__email__ = "czhengac@connect.ust.hk"
|
||||
__version__ = "0.0.1"
|
||||
|
||||
from .src.LanPaint.nodes import NODE_CLASS_MAPPINGS
|
||||
from .src.LanPaint.nodes import NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
def _install_lightweight_runtime_stubs() -> None:
|
||||
"""Install lightweight stubs so tooling can import this package without ComfyUI.
|
||||
|
||||
This is used by CI tooling (e.g., comfy-org/node-diff) that imports NODE_CLASS_MAPPINGS
|
||||
in an environment where ComfyUI isn't installed.
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
|
||||
# `src/LanPaint/nodes.py` uses `torch.Tensor` in type annotations.
|
||||
try:
|
||||
import torch # noqa: F401
|
||||
except ModuleNotFoundError:
|
||||
torch_mod = types.ModuleType("torch")
|
||||
|
||||
class Tensor: # noqa: N801 (match torch naming)
|
||||
pass
|
||||
|
||||
torch_mod.Tensor = Tensor
|
||||
torch_mod.nn = types.SimpleNamespace(functional=types.SimpleNamespace())
|
||||
sys.modules["torch"] = torch_mod
|
||||
|
||||
if "comfyui_version" not in sys.modules:
|
||||
comfyui_version_mod = types.ModuleType("comfyui_version")
|
||||
comfyui_version_mod.__version__ = "0.0.0"
|
||||
sys.modules["comfyui_version"] = comfyui_version_mod
|
||||
|
||||
sys.modules.setdefault("nodes", types.ModuleType("nodes"))
|
||||
sys.modules.setdefault("latent_preview", types.ModuleType("latent_preview"))
|
||||
|
||||
if "comfy" not in sys.modules:
|
||||
comfy_mod = types.ModuleType("comfy")
|
||||
comfy_mod.__path__ = []
|
||||
|
||||
comfy_utils_mod = types.ModuleType("comfy.utils")
|
||||
|
||||
def repeat_to_batch_size(tensor, batch_size): # type: ignore[no-untyped-def]
|
||||
if getattr(tensor, "shape", ())[0] == batch_size:
|
||||
return tensor
|
||||
return tensor
|
||||
|
||||
comfy_utils_mod.repeat_to_batch_size = repeat_to_batch_size
|
||||
|
||||
comfy_samplers_mod = types.ModuleType("comfy.samplers")
|
||||
|
||||
class DummyKSAMPLER: # noqa: N801 (match ComfyUI naming)
|
||||
pass
|
||||
|
||||
class KSampler: # noqa: N801 (match ComfyUI naming)
|
||||
SCHEDULERS = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "beta", "linear_quadratic", "kl_optimal", "AYS"]
|
||||
|
||||
comfy_samplers_mod.KSAMPLER = DummyKSAMPLER
|
||||
comfy_samplers_mod.KSampler = KSampler
|
||||
|
||||
comfy_model_base_mod = types.ModuleType("comfy.model_base")
|
||||
|
||||
class ModelType: # noqa: N801 (match ComfyUI naming)
|
||||
FLUX = "FLUX"
|
||||
FLOW = "FLOW"
|
||||
|
||||
class WAN22: # noqa: N801 (match ComfyUI naming)
|
||||
pass
|
||||
|
||||
comfy_model_base_mod.ModelType = ModelType
|
||||
comfy_model_base_mod.WAN22 = WAN22
|
||||
|
||||
comfy_mod.utils = comfy_utils_mod
|
||||
comfy_mod.samplers = comfy_samplers_mod
|
||||
comfy_mod.model_base = comfy_model_base_mod
|
||||
|
||||
sys.modules["comfy"] = comfy_mod
|
||||
sys.modules["comfy.utils"] = comfy_utils_mod
|
||||
sys.modules["comfy.samplers"] = comfy_samplers_mod
|
||||
sys.modules["comfy.model_base"] = comfy_model_base_mod
|
||||
|
||||
|
||||
try:
|
||||
from .src.LanPaint.nodes import NODE_CLASS_MAPPINGS
|
||||
from .src.LanPaint.nodes import NODE_DISPLAY_NAME_MAPPINGS
|
||||
except ModuleNotFoundError:
|
||||
_install_lightweight_runtime_stubs()
|
||||
from .src.LanPaint.nodes import NODE_CLASS_MAPPINGS
|
||||
from .src.LanPaint.nodes import NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Server routes — registered only when running inside ComfyUI (the ``server``
|
||||
# module and ``folder_paths`` are ComfyUI internals not available in CI/tests).
|
||||
# ---------------------------------------------------------------------------
|
||||
try:
|
||||
from server import PromptServer # noqa: F811 (re-export for convenience)
|
||||
|
||||
from .src.LanPaint.videometa import register_routes
|
||||
|
||||
register_routes(PromptServer.instance)
|
||||
except Exception:
|
||||
pass # not running inside ComfyUI — routes are not needed
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 293 KiB |
|
After Width: | Height: | Size: 412 KiB |
|
After Width: | Height: | Size: 280 KiB |
@@ -0,0 +1,671 @@
|
||||
{
|
||||
"id": "978d3a45-3d13-43c6-8ef9-89dc3e74d6ba",
|
||||
"revision": 0,
|
||||
"last_node_id": 84,
|
||||
"last_link_id": 229,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 78,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
314.8565368652344,
|
||||
255.63235473632812
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 194
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
195
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"cute anime girl with massive fluffy fennec ears and a big fluffy tail blonde messy long hair blue eyes wearing a maid outfit with a long black gold leaf pattern dress and a white apron mouth open placing a fancy black forest cake with candles on top of a dinner table of an old dark Victorian mansion lit by candlelight with a bright window to the foggy forest and very expensive stuff everywhere there are paintings on the walls"
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 80,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
362.7684020996094,
|
||||
481.0662536621094
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 196
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
210
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Negative Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
""
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"type": "FluxGuidance",
|
||||
"pos": [
|
||||
529.1380615234375,
|
||||
156.20236206054688
|
||||
],
|
||||
"size": [
|
||||
211.60000610351562,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "conditioning",
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 195
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
207
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "FluxGuidance"
|
||||
},
|
||||
"widgets_values": [
|
||||
3.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-66.25785064697266,
|
||||
484.081787109375
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "MODEL",
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
197
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "CLIP",
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
194,
|
||||
196
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "VAE",
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
222,
|
||||
226
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux1-dev-fp8.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
1005.0399780273438,
|
||||
296.9553527832031
|
||||
],
|
||||
"size": [
|
||||
413.6495666503906,
|
||||
596
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 197
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 207
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 210
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 224
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
225
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522",
|
||||
"Node name for S&R": "LanPaint_KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
30,
|
||||
1,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. Recommend steps 50, LanPaint NumSteps 1-20 depending on the difficulty of task. LanPaint_EndSigma = 3.0 for anime style, 0.6 for realistic style. For more information, visit https://github.com/scraed/LanPaint",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
1075.49169921875,
|
||||
1167.2703857421875
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 229
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
45.954593658447266,
|
||||
1150.45556640625
|
||||
],
|
||||
"size": [
|
||||
266.13720703125,
|
||||
487.1314697265625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
221,
|
||||
227
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
221,
|
||||
227
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.27",
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Example_7_Masked.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 83,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
635.0,
|
||||
317.0
|
||||
],
|
||||
"size": [
|
||||
195.3150390625,
|
||||
66
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 221
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 222
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"shape": 7,
|
||||
"type": "MASK",
|
||||
"link": 223
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "latent",
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
224
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 84,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
1355.0,
|
||||
317.0
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
118
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "samples",
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 225
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 226
|
||||
},
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"shape": 7,
|
||||
"type": "IMAGE",
|
||||
"link": 227
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"shape": 7,
|
||||
"type": "MASK",
|
||||
"link": 228
|
||||
},
|
||||
{
|
||||
"localized_name": "blend_overlap",
|
||||
"name": "blend_overlap",
|
||||
"shape": 7,
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
229
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
194,
|
||||
77,
|
||||
1,
|
||||
78,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
195,
|
||||
78,
|
||||
0,
|
||||
79,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
196,
|
||||
77,
|
||||
1,
|
||||
80,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
197,
|
||||
77,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
207,
|
||||
79,
|
||||
0,
|
||||
73,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
210,
|
||||
80,
|
||||
0,
|
||||
73,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
221,
|
||||
75,
|
||||
0,
|
||||
83,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
222,
|
||||
77,
|
||||
2,
|
||||
83,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
223,
|
||||
75,
|
||||
1,
|
||||
83,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
224,
|
||||
83,
|
||||
0,
|
||||
73,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
225,
|
||||
73,
|
||||
0,
|
||||
84,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
226,
|
||||
77,
|
||||
2,
|
||||
84,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
227,
|
||||
75,
|
||||
0,
|
||||
84,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
228,
|
||||
75,
|
||||
1,
|
||||
84,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
229,
|
||||
84,
|
||||
0,
|
||||
48,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Mask image for inpainting.",
|
||||
"bounding": [
|
||||
36.04227828979492,
|
||||
989.7313232421875,
|
||||
278.89093017578125,
|
||||
669.3414916992188
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Convert Latents for LanPaint",
|
||||
"bounding": [
|
||||
286.0640563964844,
|
||||
714.343505859375,
|
||||
489.16796875,
|
||||
197.81044006347656
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Load Model and Set Prompts",
|
||||
"bounding": [
|
||||
-78.9311294555664,
|
||||
176.08712768554688,
|
||||
1006.1721801757812,
|
||||
514.258544921875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Inpaint with the LanPaint KSampler",
|
||||
"bounding": [
|
||||
960.8922729492188,
|
||||
179.17588806152344,
|
||||
474.8909606933594,
|
||||
630.4742431640625
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "LanPaint OutPut",
|
||||
"bounding": [
|
||||
1085.6029052734375,
|
||||
994.0775756835938,
|
||||
345.4561767578125,
|
||||
669.4969482421875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"title": "LanPaint",
|
||||
"bounding": [
|
||||
-262.59381103515625,
|
||||
140.46656799316406,
|
||||
1737.328857421875,
|
||||
797.4443359375
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.35049389948139237,
|
||||
"offset": [
|
||||
348.866804381099,
|
||||
308.65057628971834
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.27.10",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -1,786 +0,0 @@
|
||||
{
|
||||
"id": "978d3a45-3d13-43c6-8ef9-89dc3e74d6ba",
|
||||
"revision": 0,
|
||||
"last_node_id": 82,
|
||||
"last_link_id": 220,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 66,
|
||||
"type": "SetLatentNoiseMask",
|
||||
"pos": [
|
||||
514.1915893554688,
|
||||
781.9396362304688
|
||||
],
|
||||
"size": [
|
||||
264.5999755859375,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 216
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 189
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
186
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "SetLatentNoiseMask"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
314.8565368652344,
|
||||
255.63235473632812
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 194
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
195
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"cute anime girl with massive fluffy fennec ears and a big fluffy tail blonde messy long hair blue eyes wearing a maid outfit with a long black gold leaf pattern dress and a white apron mouth open placing a fancy black forest cake with candles on top of a dinner table of an old dark Victorian mansion lit by candlelight with a bright window to the foggy forest and very expensive stuff everywhere there are paintings on the walls"
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 80,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
362.7684020996094,
|
||||
481.0662536621094
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 196
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
210
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Negative Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
""
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"type": "FluxGuidance",
|
||||
"pos": [
|
||||
529.1380615234375,
|
||||
156.20236206054688
|
||||
],
|
||||
"size": [
|
||||
211.60000610351562,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "conditioning",
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 195
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
207
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "FluxGuidance"
|
||||
},
|
||||
"widgets_values": [
|
||||
3.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-66.25785064697266,
|
||||
484.081787109375
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "MODEL",
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
197
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "CLIP",
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
194,
|
||||
196
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "VAE",
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
200,
|
||||
203
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.26",
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux1-dev-fp8.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
1005.0399780273438,
|
||||
296.9553527832031
|
||||
],
|
||||
"size": [
|
||||
413.6495666503906,
|
||||
596
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 197
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 207
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 210
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 186
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
187
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522",
|
||||
"Node name for S&R": "LanPaint_KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
30,
|
||||
1,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. Recommend steps 50, LanPaint NumSteps 1-20 depending on the difficulty of task. LanPaint_EndSigma = 3.0 for anime style, 0.6 for realistic style. For more information, visit https://github.com/scraed/LanPaint",
|
||||
"🖼️ Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"type": "VAEEncode",
|
||||
"pos": [
|
||||
200.1034698486328,
|
||||
776.3561401367188
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "pixels",
|
||||
"type": "IMAGE",
|
||||
"link": 188
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 203
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
216
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEEncode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
1075.49169921875,
|
||||
1167.2703857421875
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 103
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"type": "LanPaint_MaskBlend",
|
||||
"pos": [
|
||||
1773.0189208984375,
|
||||
1222.9478759765625
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"link": 219
|
||||
},
|
||||
{
|
||||
"name": "image2",
|
||||
"type": "IMAGE",
|
||||
"link": 218
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 220
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
217
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "4d3d5d17f0105b673df92da5b084cce567c9c712",
|
||||
"Node name for S&R": "LanPaint_MaskBlend"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 82,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
2106.45166015625,
|
||||
1090.682861328125
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 217
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1211.46484375,
|
||||
1065.318359375
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 187
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 200
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
103,
|
||||
218
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
45.954593658447266,
|
||||
1150.45556640625
|
||||
],
|
||||
"size": [
|
||||
266.13720703125,
|
||||
487.1314697265625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
188,
|
||||
219
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
189,
|
||||
220
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.27",
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"clipspace/clipspace-mask-8865503.5.png [input]",
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
103,
|
||||
8,
|
||||
0,
|
||||
48,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
186,
|
||||
66,
|
||||
0,
|
||||
73,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
187,
|
||||
73,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
188,
|
||||
75,
|
||||
0,
|
||||
65,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
189,
|
||||
75,
|
||||
1,
|
||||
66,
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
194,
|
||||
77,
|
||||
1,
|
||||
78,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
195,
|
||||
78,
|
||||
0,
|
||||
79,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
196,
|
||||
77,
|
||||
1,
|
||||
80,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
197,
|
||||
77,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
200,
|
||||
77,
|
||||
2,
|
||||
8,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
203,
|
||||
77,
|
||||
2,
|
||||
65,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
207,
|
||||
79,
|
||||
0,
|
||||
73,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
210,
|
||||
80,
|
||||
0,
|
||||
73,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
216,
|
||||
65,
|
||||
0,
|
||||
66,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
217,
|
||||
81,
|
||||
0,
|
||||
82,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
218,
|
||||
8,
|
||||
0,
|
||||
81,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
219,
|
||||
75,
|
||||
0,
|
||||
81,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
220,
|
||||
75,
|
||||
1,
|
||||
81,
|
||||
2,
|
||||
"MASK"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Mask image for inpainting.",
|
||||
"bounding": [
|
||||
36.04227828979492,
|
||||
989.7313232421875,
|
||||
278.89093017578125,
|
||||
669.3414916992188
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Convert Latents for LanPaint",
|
||||
"bounding": [
|
||||
286.0640563964844,
|
||||
714.343505859375,
|
||||
489.16796875,
|
||||
197.81044006347656
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Load Model and Set Prompts",
|
||||
"bounding": [
|
||||
-78.9311294555664,
|
||||
176.08712768554688,
|
||||
1006.1721801757812,
|
||||
514.258544921875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Inpaint with the LanPaint KSampler",
|
||||
"bounding": [
|
||||
960.8922729492188,
|
||||
179.17588806152344,
|
||||
474.8909606933594,
|
||||
630.4742431640625
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "LanPaint OutPut",
|
||||
"bounding": [
|
||||
1085.6029052734375,
|
||||
994.0775756835938,
|
||||
345.4561767578125,
|
||||
669.4969482421875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"title": "LanPaint",
|
||||
"bounding": [
|
||||
-262.59381103515625,
|
||||
140.46656799316406,
|
||||
1737.328857421875,
|
||||
797.4443359375
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.35049389948139237,
|
||||
"offset": [
|
||||
348.866804381099,
|
||||
308.65057628971834
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.27.10",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
After Width: | Height: | Size: 303 KiB |
@@ -0,0 +1,998 @@
|
||||
{
|
||||
"id": "054799b4-76bf-4b4e-b7b7-615d2914285b",
|
||||
"revision": 0,
|
||||
"last_node_id": 119,
|
||||
"last_link_id": 274,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 88,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
180,
|
||||
440
|
||||
],
|
||||
"size": [
|
||||
350,
|
||||
110
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Select a fp8 weight_dtype if you are running out of memory."
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
180,
|
||||
310
|
||||
],
|
||||
"size": [
|
||||
350,
|
||||
82
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
250
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"hunyuan_video_t2v_720p_bf16.safetensors",
|
||||
"default"
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
},
|
||||
{
|
||||
"id": 94,
|
||||
"type": "DualCLIPLoader",
|
||||
"pos": [
|
||||
-160,
|
||||
-60
|
||||
],
|
||||
"size": [
|
||||
350,
|
||||
130
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
252,
|
||||
255
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "DualCLIPLoader",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"clip_l.safetensors",
|
||||
"llava_llama3_fp8_scaled.safetensors",
|
||||
"hunyuan_video",
|
||||
"default"
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
},
|
||||
{
|
||||
"id": 114,
|
||||
"type": "FluxGuidance",
|
||||
"pos": [
|
||||
653.5101318359375,
|
||||
-73.20354461669922
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 258
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
259
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.65",
|
||||
"Node name for S&R": "FluxGuidance"
|
||||
},
|
||||
"widgets_values": [
|
||||
6
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
171.56072998046875,
|
||||
795.9495239257812
|
||||
],
|
||||
"size": [
|
||||
274.080078125,
|
||||
314
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
266,
|
||||
272
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
266,
|
||||
272
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.59",
|
||||
"Node name for S&R": "LoadImage",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Masked_Load_Me_in_Loader (8).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
226.1235809326172,
|
||||
-161.8229522705078
|
||||
],
|
||||
"size": [
|
||||
310,
|
||||
240
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 252
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
258
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Close up Portrait of a futuristic cowgirl wearing jeans rides a mechanical horse across a vast cyberpunk desert. The dunes shimmer with holographic projections, glowing in blues and purples. cacti and two massive moons hang low on the horizon, illuminating the dusty air. Cyberpunk western fusion, cinematic, 8K, highly detailed, atmospheric perspective, Blade Runner style."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
233.61778259277344,
|
||||
122.75636291503906
|
||||
],
|
||||
"size": [
|
||||
319.00726318359375,
|
||||
88
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 255
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
254
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
""
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 92,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
1340,
|
||||
250
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
170
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Use the tiled decode node by default because most people will need it.\n\nLower the tile_size and overlap if you run out of memory."
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 116,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
1324.0262451171875,
|
||||
583.7811889648438
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
170
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Decrease LanPaint_NumSteps to accelerate"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"type": "VAEDecodeTiled",
|
||||
"pos": [
|
||||
1341.9100341796875,
|
||||
49.04500961303711
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
150
|
||||
],
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 248
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 237
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
251
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.18",
|
||||
"Node name for S&R": "VAEDecodeTiled",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
64,
|
||||
64,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 86,
|
||||
"type": "SaveAnimatedWEBP",
|
||||
"pos": [
|
||||
1661.5201416015625,
|
||||
68.0674819946289
|
||||
],
|
||||
"size": [
|
||||
380,
|
||||
366
|
||||
],
|
||||
"flags": {},
|
||||
"order": 16,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 251
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.18",
|
||||
"Node name for S&R": "SaveAnimatedWEBP",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI",
|
||||
24,
|
||||
false,
|
||||
80,
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 110,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
829.425537109375,
|
||||
218.98995971679688
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
596
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 239
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 259
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 254
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 269
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
248,
|
||||
270
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "6109df6591a4cf2bc9d3b113d03f7297fa9248e9",
|
||||
"Node name for S&R": "LanPaint_KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
534861079790570,
|
||||
"randomize",
|
||||
20,
|
||||
1,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
910,
|
||||
-230
|
||||
],
|
||||
"size": [
|
||||
350.65509033203125,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
237,
|
||||
267,
|
||||
271
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.18",
|
||||
"Node name for S&R": "VAELoader",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"hunyuan_video_vae_bf16.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"type": "ModelSamplingSD3",
|
||||
"pos": [
|
||||
613.9942626953125,
|
||||
59.9338493347168
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 250
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
239
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.18",
|
||||
"Node name for S&R": "ModelSamplingSD3",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
7
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 117,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
1670.939697265625,
|
||||
-104.6227798461914
|
||||
],
|
||||
"size": [
|
||||
531.0060424804688,
|
||||
550.45361328125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 274
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.65"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 118,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
459.4,
|
||||
239.0
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
150
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 266
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 267
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 268
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
269
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 119,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
1179.4,
|
||||
239.0
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
200
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 270
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 271
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 272
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 273
|
||||
},
|
||||
{
|
||||
"name": "blend_overlap",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
274
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [
|
||||
9
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
237,
|
||||
78,
|
||||
0,
|
||||
85,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
239,
|
||||
81,
|
||||
0,
|
||||
110,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
248,
|
||||
110,
|
||||
0,
|
||||
85,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
250,
|
||||
103,
|
||||
0,
|
||||
81,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
251,
|
||||
85,
|
||||
0,
|
||||
86,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
252,
|
||||
94,
|
||||
0,
|
||||
104,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
254,
|
||||
113,
|
||||
0,
|
||||
110,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
255,
|
||||
94,
|
||||
0,
|
||||
113,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
258,
|
||||
104,
|
||||
0,
|
||||
114,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
259,
|
||||
114,
|
||||
0,
|
||||
110,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
266,
|
||||
105,
|
||||
0,
|
||||
118,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
267,
|
||||
78,
|
||||
0,
|
||||
118,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
268,
|
||||
105,
|
||||
1,
|
||||
118,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
269,
|
||||
118,
|
||||
0,
|
||||
110,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
270,
|
||||
110,
|
||||
0,
|
||||
119,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
271,
|
||||
78,
|
||||
0,
|
||||
119,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
272,
|
||||
105,
|
||||
0,
|
||||
119,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
273,
|
||||
105,
|
||||
1,
|
||||
119,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
274,
|
||||
119,
|
||||
0,
|
||||
117,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Text Encoding",
|
||||
"bounding": [
|
||||
-190,
|
||||
-140,
|
||||
730,
|
||||
340
|
||||
],
|
||||
"color": "#8A8",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "VAE Decoding & Output",
|
||||
"bounding": [
|
||||
1320,
|
||||
-140,
|
||||
1110,
|
||||
600
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Sampling Process",
|
||||
"bounding": [
|
||||
570,
|
||||
-140,
|
||||
730,
|
||||
980
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "HunyuanVideo Model",
|
||||
"bounding": [
|
||||
170,
|
||||
230,
|
||||
370.5005798339844,
|
||||
330.7360534667969
|
||||
],
|
||||
"color": "#88A",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"title": "Guidance",
|
||||
"bounding": [
|
||||
590,
|
||||
-90,
|
||||
680,
|
||||
260
|
||||
],
|
||||
"color": "#8AA",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"title": "sampling",
|
||||
"bounding": [
|
||||
590,
|
||||
200,
|
||||
680,
|
||||
440
|
||||
],
|
||||
"color": "#b06634",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.4870776476432723,
|
||||
"offset": [
|
||||
85.03251058761712,
|
||||
779.6524460150363
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.27.10",
|
||||
"groupNodes": {},
|
||||
"ue_links": [],
|
||||
"links_added_by_ue": [],
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
Before Width: | Height: | Size: 670 KiB |
|
After Width: | Height: | Size: 598 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 674 KiB |
|
Before Width: | Height: | Size: 674 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 438 KiB |
|
After Width: | Height: | Size: 393 KiB |
@@ -0,0 +1,731 @@
|
||||
{
|
||||
"id": "11cce4ab-536b-4f42-a95c-0be437d04ace",
|
||||
"revision": 0,
|
||||
"last_node_id": 137,
|
||||
"last_link_id": 364,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 74,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
276.219970703125,
|
||||
179.55892944335938
|
||||
],
|
||||
"size": [
|
||||
388.97625732421875,
|
||||
572
|
||||
],
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 325
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 323
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 324
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 359
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
360
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_KSampler",
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
20,
|
||||
4,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting",
|
||||
"lanpaint_star_button"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
807.1268310546875,
|
||||
868.395263671875
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 20,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 364
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 117,
|
||||
"type": "CLIPLoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
177.9814910888672
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
110
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
318,
|
||||
319
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"directory": "text_encoders"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"qwen_image",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 122,
|
||||
"type": "ModelSamplingAuraFlow",
|
||||
"pos": [
|
||||
-34.14249038696289,
|
||||
-43.64523696899414
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 320
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
325
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ModelSamplingAuraFlow",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
3.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 119,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
37.98154830932617
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
90
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
320
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_image_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_fp8_e4m3fn.safetensors",
|
||||
"directory": "diffusion_models"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_image_fp8_e4m3fn.safetensors",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 128,
|
||||
"type": "MarkdownNote",
|
||||
"pos": [
|
||||
715.929931640625,
|
||||
371.1071472167969
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
190
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"title": "KSampler settings",
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Decrease **LanPaint_NumSteps** for faster generation. \n"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 123,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-935.2606748101325,
|
||||
786.6170753867922
|
||||
],
|
||||
"size": [
|
||||
262.12347412109375,
|
||||
487.22296142578125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
356,
|
||||
362
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
357,
|
||||
363
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Masked_Load_Me_in_Loader (7).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 118,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
327.9817199707031
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
60
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
358,
|
||||
361
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAELoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_image_vae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors",
|
||||
"directory": "vae"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_image_vae.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 121,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
-454.4298095703125,
|
||||
247.9816436767578
|
||||
],
|
||||
"size": [
|
||||
425.27801513671875,
|
||||
180.6060791015625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 319
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
324
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Negative Prompt)",
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
" low quality, bad anatomy, extra digits, missing digits, extra limbs, missing limbs"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 120,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
-454.86480712890625,
|
||||
41.89194869995117
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 318
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
323
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Cyberpunk-style Einstein portrait: He wears a sleek black coat with glowing cyan circuit patterns, silver-rimmed cybernetic glasses (lenses display faint data streams), and his hair has subtle neon blue highlights. His expression is calm, with a faint smile. Behind him: a dark, rain-washed cybercity backdrop\u2014towering skyscrapers with flickering holographic ads, wet pavement reflecting neon pink/magenta lights. In front of him: giant, glowing white 3D text of \"LanPaint\", with electric blue energy pulses swirling around the equation. Cinematic lighting, hyper-detailed textures, rain droplets visible in the air."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 136,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
-93.8,
|
||||
199.6
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
110
|
||||
],
|
||||
"flags": {},
|
||||
"order": 50,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 356
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 358
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 357
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "latent",
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
359
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode",
|
||||
"cnr_id": "LanPaint"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 137,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
626.2,
|
||||
199.6
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
150
|
||||
],
|
||||
"flags": {},
|
||||
"order": 51,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "samples",
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 360
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 361
|
||||
},
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 362
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 363
|
||||
},
|
||||
{
|
||||
"localized_name": "blend_overlap",
|
||||
"name": "blend_overlap",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
364
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode",
|
||||
"cnr_id": "LanPaint"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
318,
|
||||
117,
|
||||
0,
|
||||
120,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
319,
|
||||
117,
|
||||
0,
|
||||
121,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
320,
|
||||
119,
|
||||
0,
|
||||
122,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
323,
|
||||
120,
|
||||
0,
|
||||
74,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
324,
|
||||
121,
|
||||
0,
|
||||
74,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
325,
|
||||
122,
|
||||
0,
|
||||
74,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
356,
|
||||
123,
|
||||
0,
|
||||
136,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
357,
|
||||
123,
|
||||
1,
|
||||
136,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
358,
|
||||
118,
|
||||
0,
|
||||
136,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
359,
|
||||
136,
|
||||
0,
|
||||
74,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
360,
|
||||
74,
|
||||
0,
|
||||
137,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
361,
|
||||
118,
|
||||
0,
|
||||
137,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
362,
|
||||
123,
|
||||
0,
|
||||
137,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
363,
|
||||
123,
|
||||
1,
|
||||
137,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
364,
|
||||
137,
|
||||
0,
|
||||
113,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.6722341729170042,
|
||||
"offset": [
|
||||
1421.2190992818914,
|
||||
-80.75631642004547
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.43.18",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
},
|
||||
"groupNodes": {}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
Before Width: | Height: | Size: 921 KiB |
@@ -1,806 +0,0 @@
|
||||
{
|
||||
"id": "11cce4ab-536b-4f42-a95c-0be437d04ace",
|
||||
"revision": 0,
|
||||
"last_node_id": 128,
|
||||
"last_link_id": 338,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 74,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
276.219970703125,
|
||||
179.55892944335938
|
||||
],
|
||||
"size": [
|
||||
388.97625732421875,
|
||||
572
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 325
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 323
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 324
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 332
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
334
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522",
|
||||
"Node name for S&R": "LanPaint_KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
20,
|
||||
4,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
807.1268310546875,
|
||||
868.395263671875
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 338
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 117,
|
||||
"type": "CLIPLoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
177.9814910888672
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
110
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
318,
|
||||
319
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "CLIPLoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"directory": "text_encoders"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"qwen_image",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 118,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
327.9817199707031
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
60
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
329,
|
||||
335
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "VAELoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_image_vae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors",
|
||||
"directory": "vae"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_image_vae.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 121,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
-454.4298095703125,
|
||||
247.9816436767578
|
||||
],
|
||||
"size": [
|
||||
425.27801513671875,
|
||||
180.6060791015625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 319
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
324
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Negative Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
" low quality, bad anatomy, extra digits, missing digits, extra limbs, missing limbs"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 122,
|
||||
"type": "ModelSamplingAuraFlow",
|
||||
"pos": [
|
||||
-34.14249038696289,
|
||||
-43.64523696899414
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 320
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
325
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "ModelSamplingAuraFlow",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
3.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 119,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
-824.4296875,
|
||||
37.98154830932617
|
||||
],
|
||||
"size": [
|
||||
330,
|
||||
90
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
320
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_image_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_fp8_e4m3fn.safetensors",
|
||||
"directory": "diffusion_models"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_image_fp8_e4m3fn.safetensors",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 124,
|
||||
"type": "VAEEncode",
|
||||
"pos": [
|
||||
-530.8583984375,
|
||||
708.7066650390625
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "pixels",
|
||||
"type": "IMAGE",
|
||||
"link": 326
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 329
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
327
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEEncode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 125,
|
||||
"type": "SetLatentNoiseMask",
|
||||
"pos": [
|
||||
-234.4196014404297,
|
||||
705.1629638671875
|
||||
],
|
||||
"size": [
|
||||
264.5999755859375,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 327
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 328
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
332
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "SetLatentNoiseMask"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 127,
|
||||
"type": "LanPaint_MaskBlend",
|
||||
"pos": [
|
||||
405.3840637207031,
|
||||
938.8120727539062
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"link": 336
|
||||
},
|
||||
{
|
||||
"name": "image2",
|
||||
"type": "IMAGE",
|
||||
"link": 333
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 337
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
338
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "4d3d5d17f0105b673df92da5b084cce567c9c712",
|
||||
"Node name for S&R": "LanPaint_MaskBlend"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 126,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
115.07575225830078,
|
||||
878.4630737304688
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 334
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 335
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
333
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 123,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-543.5358276367188,
|
||||
851.759765625
|
||||
],
|
||||
"size": [
|
||||
262.12347412109375,
|
||||
487.22296142578125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
326,
|
||||
336
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
328,
|
||||
337
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Masked_Load_Me_in_Loader (7).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 120,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
-454.86480712890625,
|
||||
41.89194869995117
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 318
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
323
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.48",
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Cyberpunk-style Einstein portrait: He wears a sleek black coat with glowing cyan circuit patterns, silver-rimmed cybernetic glasses (lenses display faint data streams), and his hair has subtle neon blue highlights. His expression is calm, with a faint smile. Behind him: a dark, rain-washed cybercity backdrop—towering skyscrapers with flickering holographic ads, wet pavement reflecting neon pink/magenta lights. In front of him: giant, glowing white 3D text of \"LanPaint\", with electric blue energy pulses swirling around the equation. Cinematic lighting, hyper-detailed textures, rain droplets visible in the air."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 128,
|
||||
"type": "MarkdownNote",
|
||||
"pos": [
|
||||
715.929931640625,
|
||||
371.1071472167969
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
190
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"title": "KSampler settings",
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Decrease **LanPaint_NumSteps** for faster generation. \n"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
318,
|
||||
117,
|
||||
0,
|
||||
120,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
319,
|
||||
117,
|
||||
0,
|
||||
121,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
320,
|
||||
119,
|
||||
0,
|
||||
122,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
323,
|
||||
120,
|
||||
0,
|
||||
74,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
324,
|
||||
121,
|
||||
0,
|
||||
74,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
325,
|
||||
122,
|
||||
0,
|
||||
74,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
326,
|
||||
123,
|
||||
0,
|
||||
124,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
327,
|
||||
124,
|
||||
0,
|
||||
125,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
328,
|
||||
123,
|
||||
1,
|
||||
125,
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
329,
|
||||
118,
|
||||
0,
|
||||
124,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
332,
|
||||
125,
|
||||
0,
|
||||
74,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
333,
|
||||
126,
|
||||
0,
|
||||
127,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
334,
|
||||
74,
|
||||
0,
|
||||
126,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
335,
|
||||
118,
|
||||
0,
|
||||
126,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
336,
|
||||
123,
|
||||
0,
|
||||
127,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
337,
|
||||
123,
|
||||
1,
|
||||
127,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
338,
|
||||
127,
|
||||
0,
|
||||
113,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.7162766973052638,
|
||||
"offset": [
|
||||
1084.0595529886727,
|
||||
5.084234529384386
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.25.10",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
},
|
||||
"groupNodes": {}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
After Width: | Height: | Size: 305 KiB |
@@ -0,0 +1,759 @@
|
||||
{
|
||||
"id": "26fb90cb-eb4a-422e-97d0-8b84dd6c3302",
|
||||
"revision": 0,
|
||||
"last_node_id": 79,
|
||||
"last_link_id": 211,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
333.06903076171875,
|
||||
249.68698120117188
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 81
|
||||
},
|
||||
{
|
||||
"localized_name": "text",
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "text"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
199
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1girl, blue shirt, masterpiece, high score, great score, absurdres"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
335.06903076171875,
|
||||
462.68701171875
|
||||
],
|
||||
"size": [
|
||||
425.27801513671875,
|
||||
180.6060791015625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "clip",
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 82
|
||||
},
|
||||
{
|
||||
"localized_name": "text",
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "text"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "CONDITIONING",
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
200
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lowres, bad anatomy, bad hands, text, error, missing finger, extra digits, fewer digits, cropped, worst quality, low quality, low score, bad score, average score, signature, watermark, username, blurry, nude, NSFW"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
623.0,
|
||||
288.7
|
||||
],
|
||||
"size": [
|
||||
195.3150390625,
|
||||
66
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 204
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 203
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"shape": 7,
|
||||
"type": "MASK",
|
||||
"link": 205
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "latent",
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
206
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
992.965419921875,
|
||||
268.686944885254
|
||||
],
|
||||
"size": [
|
||||
413.6495666503906,
|
||||
572
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "model",
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 198
|
||||
},
|
||||
{
|
||||
"localized_name": "positive",
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 199
|
||||
},
|
||||
{
|
||||
"localized_name": "negative",
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 200
|
||||
},
|
||||
{
|
||||
"localized_name": "latent_image",
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 206
|
||||
},
|
||||
{
|
||||
"localized_name": "seed",
|
||||
"name": "seed",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "seed"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "steps",
|
||||
"name": "steps",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "steps"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "cfg",
|
||||
"name": "cfg",
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "cfg"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "sampler_name",
|
||||
"name": "sampler_name",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "sampler_name"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "scheduler",
|
||||
"name": "scheduler",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "scheduler"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "denoise",
|
||||
"name": "denoise",
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "denoise"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "LanPaint_NumSteps",
|
||||
"name": "LanPaint_NumSteps",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "LanPaint_NumSteps"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "LanPaint_PromptMode",
|
||||
"name": "LanPaint_PromptMode",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "LanPaint_PromptMode"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "LanPaint_Info",
|
||||
"name": "LanPaint_Info",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "LanPaint_Info"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "Inpainting_mode",
|
||||
"name": "Inpainting_mode",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "Inpainting_mode"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "LATENT",
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
207
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_KSampler",
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
20,
|
||||
5,
|
||||
"euler",
|
||||
"karras",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting",
|
||||
"lanpaint_star_button"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-62.627220153808594,
|
||||
407.01416015625
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "ckpt_name",
|
||||
"name": "ckpt_name",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "ckpt_name"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "MODEL",
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
198
|
||||
]
|
||||
},
|
||||
{
|
||||
"localized_name": "CLIP",
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
81,
|
||||
82
|
||||
]
|
||||
},
|
||||
{
|
||||
"localized_name": "VAE",
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
203,
|
||||
208
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"animagineXL40_v4Opt.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
45.70227813720703,
|
||||
1147.2928466796875
|
||||
],
|
||||
"size": [
|
||||
262.12347412109375,
|
||||
487.22296142578125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "image"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "choose file to upload",
|
||||
"name": "upload",
|
||||
"type": "IMAGEUPLOAD",
|
||||
"widget": {
|
||||
"name": "upload"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "IMAGE",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
204,
|
||||
209
|
||||
]
|
||||
},
|
||||
{
|
||||
"localized_name": "MASK",
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
205,
|
||||
210
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Masked_Load_Me_in_Loader (19).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
2279.375686842185,
|
||||
747.0061447670435
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "images",
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 211
|
||||
},
|
||||
{
|
||||
"localized_name": "filename_prefix",
|
||||
"name": "filename_prefix",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "filename_prefix"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "images",
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
1343.0,
|
||||
288.7
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
118
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "samples",
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 207
|
||||
},
|
||||
{
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 208
|
||||
},
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"shape": 7,
|
||||
"type": "IMAGE",
|
||||
"link": 209
|
||||
},
|
||||
{
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"shape": 7,
|
||||
"type": "MASK",
|
||||
"link": 210
|
||||
},
|
||||
{
|
||||
"localized_name": "blend_overlap",
|
||||
"name": "blend_overlap",
|
||||
"shape": 7,
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
211
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
81,
|
||||
29,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
82,
|
||||
29,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
198,
|
||||
29,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
199,
|
||||
6,
|
||||
0,
|
||||
73,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
200,
|
||||
7,
|
||||
0,
|
||||
73,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
203,
|
||||
29,
|
||||
2,
|
||||
78,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
204,
|
||||
20,
|
||||
0,
|
||||
78,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
205,
|
||||
20,
|
||||
1,
|
||||
78,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
206,
|
||||
78,
|
||||
0,
|
||||
73,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
207,
|
||||
73,
|
||||
0,
|
||||
79,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
208,
|
||||
29,
|
||||
2,
|
||||
79,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
209,
|
||||
20,
|
||||
0,
|
||||
79,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
210,
|
||||
20,
|
||||
1,
|
||||
79,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
211,
|
||||
79,
|
||||
0,
|
||||
75,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Mask image for inpainting.",
|
||||
"bounding": [
|
||||
36.04227828979492,
|
||||
989.7313232421875,
|
||||
278.89093017578125,
|
||||
669.3414916992188
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Convert Latents for LanPaint",
|
||||
"bounding": [
|
||||
286.0640563964844,
|
||||
714.343505859375,
|
||||
489.16796875,
|
||||
197.81044006347656
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Load Model and Set Prompts",
|
||||
"bounding": [
|
||||
-78.9311294555664,
|
||||
176.08712768554688,
|
||||
1006.1721801757812,
|
||||
514.258544921875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Inpaint with the LanPaint KSampler",
|
||||
"bounding": [
|
||||
960.8922729492188,
|
||||
179.17588806152344,
|
||||
474.8909606933594,
|
||||
630.4742431640625
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"title": "LanPaint",
|
||||
"bounding": [
|
||||
-262.59381103515625,
|
||||
140.46656799316406,
|
||||
1737.328857421875,
|
||||
797.4443359375
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.8954302432552493,
|
||||
"offset": [
|
||||
-1381.79269847794,
|
||||
-521.6998945185028
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.25.10",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -1,735 +0,0 @@
|
||||
{
|
||||
"id": "26fb90cb-eb4a-422e-97d0-8b84dd6c3302",
|
||||
"revision": 0,
|
||||
"last_node_id": 75,
|
||||
"last_link_id": 192,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
333.06903076171875,
|
||||
249.68698120117188
|
||||
],
|
||||
"size": [
|
||||
422.84503173828125,
|
||||
164.31304931640625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 81
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
184
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1girl, blue shirt, masterpiece, high score, great score, absurdres"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
335.06903076171875,
|
||||
462.68701171875
|
||||
],
|
||||
"size": [
|
||||
425.27801513671875,
|
||||
180.6060791015625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 82
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
185
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lowres, bad anatomy, bad hands, text, error, missing finger, extra digits, fewer digits, cropped, worst quality, low quality, low score, bad score, average score, signature, watermark, username, blurry, nude, NSFW"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-62.627220153808594,
|
||||
407.01416015625
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
183
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
81,
|
||||
82
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
84,
|
||||
157
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"animagineXL40_v4Opt.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
1091.036376953125,
|
||||
1158.526611328125
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 103
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1211.46484375,
|
||||
1065.318359375
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 187
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 84
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
103,
|
||||
189
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
45.70227813720703,
|
||||
1147.2928466796875
|
||||
],
|
||||
"size": [
|
||||
262.12347412109375,
|
||||
487.22296142578125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
156,
|
||||
190
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"slot_index": 1,
|
||||
"links": [
|
||||
155,
|
||||
191
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"clipspace/clipspace-mask-2620525.399999976.png [input]",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
1845.7059326171875,
|
||||
1044.06201171875
|
||||
],
|
||||
"size": [
|
||||
311.2532653808594,
|
||||
484.7096252441406
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 188
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
996.4982299804688,
|
||||
299.599365234375
|
||||
],
|
||||
"size": [
|
||||
413.6495666503906,
|
||||
572
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 183
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 184
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 185
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 186
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
187
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "56bd6c04e89124cd06682b304245d6ddf8b20522",
|
||||
"Node name for S&R": "LanPaint_KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"fixed",
|
||||
30,
|
||||
5,
|
||||
"euler",
|
||||
"karras",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 74,
|
||||
"type": "LanPaint_MaskBlend",
|
||||
"pos": [
|
||||
1512.2730712890625,
|
||||
1176.3270263671875
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"link": 190
|
||||
},
|
||||
{
|
||||
"name": "image2",
|
||||
"type": "IMAGE",
|
||||
"link": 189
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 191
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
188
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "4d3d5d17f0105b673df92da5b084cce567c9c712",
|
||||
"Node name for S&R": "LanPaint_MaskBlend"
|
||||
},
|
||||
"widgets_values": [
|
||||
9
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
"type": "SetLatentNoiseMask",
|
||||
"pos": [
|
||||
480.30780029296875,
|
||||
821.3880004882812
|
||||
],
|
||||
"size": [
|
||||
264.5999755859375,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 192
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 155
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
186
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "SetLatentNoiseMask"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"type": "VAEEncode",
|
||||
"pos": [
|
||||
206.43878173828125,
|
||||
818.9985961914062
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "pixels",
|
||||
"type": "IMAGE",
|
||||
"link": 156
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 157
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
192
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23",
|
||||
"Node name for S&R": "VAEEncode"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
81,
|
||||
29,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
82,
|
||||
29,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
84,
|
||||
29,
|
||||
2,
|
||||
8,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
103,
|
||||
8,
|
||||
0,
|
||||
48,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
155,
|
||||
20,
|
||||
1,
|
||||
66,
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
156,
|
||||
20,
|
||||
0,
|
||||
65,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
157,
|
||||
29,
|
||||
2,
|
||||
65,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
183,
|
||||
29,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
184,
|
||||
6,
|
||||
0,
|
||||
73,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
185,
|
||||
7,
|
||||
0,
|
||||
73,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
186,
|
||||
66,
|
||||
0,
|
||||
73,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
187,
|
||||
73,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
188,
|
||||
74,
|
||||
0,
|
||||
75,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
189,
|
||||
8,
|
||||
0,
|
||||
74,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
190,
|
||||
20,
|
||||
0,
|
||||
74,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
191,
|
||||
20,
|
||||
1,
|
||||
74,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
192,
|
||||
65,
|
||||
0,
|
||||
66,
|
||||
0,
|
||||
"LATENT"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Mask image for inpainting.",
|
||||
"bounding": [
|
||||
36.04227828979492,
|
||||
989.7313232421875,
|
||||
278.89093017578125,
|
||||
669.3414916992188
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Convert Latents for LanPaint",
|
||||
"bounding": [
|
||||
286.0640563964844,
|
||||
714.343505859375,
|
||||
489.16796875,
|
||||
197.81044006347656
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Load Model and Set Prompts",
|
||||
"bounding": [
|
||||
-78.9311294555664,
|
||||
176.08712768554688,
|
||||
1006.1721801757812,
|
||||
514.258544921875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Inpaint with the LanPaint KSampler",
|
||||
"bounding": [
|
||||
960.8922729492188,
|
||||
179.17588806152344,
|
||||
474.8909606933594,
|
||||
630.4742431640625
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "LanPaint OutPut",
|
||||
"bounding": [
|
||||
1085.6029052734375,
|
||||
994.0775756835938,
|
||||
345.4561767578125,
|
||||
669.4969482421875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"title": "LanPaint",
|
||||
"bounding": [
|
||||
-262.59381103515625,
|
||||
140.46656799316406,
|
||||
1737.328857421875,
|
||||
797.4443359375
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"title": "Paste back the original to preserve it exactly, if you want",
|
||||
"bounding": [
|
||||
1491.586181640625,
|
||||
984.3547973632812,
|
||||
749.66455078125,
|
||||
637.360595703125
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.6209213230591556,
|
||||
"offset": [
|
||||
850.0803535605011,
|
||||
86.6432096141053
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.25.10",
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.18",
|
||||
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
After Width: | Height: | Size: 673 KiB |
@@ -0,0 +1,998 @@
|
||||
{
|
||||
"id": "9ae6082b-c7f4-433c-9971-7a8f65a3ea65",
|
||||
"revision": 0,
|
||||
"last_node_id": 78,
|
||||
"last_link_id": 112,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 40,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
131.0846434356025,
|
||||
576.6065838350738
|
||||
],
|
||||
"size": [
|
||||
269.77864583333337,
|
||||
67.82552083333334
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
104,
|
||||
108
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAELoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "ae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors",
|
||||
"directory": "vae"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"ae.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"type": "EmptySD3LatentImage",
|
||||
"pos": [
|
||||
130.26337981400314,
|
||||
705.2539588236402
|
||||
],
|
||||
"size": [
|
||||
259.77864583333337,
|
||||
124.4921875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptySD3LatentImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.64",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
1024,
|
||||
1024,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"type": "LoraLoaderModelOnly",
|
||||
"pos": [
|
||||
459.99957646933433,
|
||||
109.99990579628181
|
||||
],
|
||||
"size": [
|
||||
369.7916666666667,
|
||||
96.15885416666667
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 54
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
60
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoraLoaderModelOnly",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.75",
|
||||
"models": [
|
||||
{
|
||||
"name": "pixel_art_style_z_image_turbo.safetensors",
|
||||
"url": "https://huggingface.co/tarn59/pixel_art_style_lora_z_image_turbo/resolve/main/pixel_art_style_z_image_turbo.safetensors",
|
||||
"directory": "loras"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"pixel_art_style_z_image_turbo.safetensors",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"type": "MarkdownNote",
|
||||
"pos": [
|
||||
-390.00009654097465,
|
||||
239.99994184235885
|
||||
],
|
||||
"size": [
|
||||
489.77864583333337,
|
||||
617.3828125
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"title": "Model link",
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"## Report workflow issue\n\nIf you found any issues when running this workflow, [report template issue here](https://github.com/Comfy-Org/workflow_templates/issues)\n\n\n## Model links\n\n**text_encoders**\n\n- [qwen_3_4b.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors)\n\n**loras**\n\n- [pixel_art_style_z_image_turbo.safetensors](https://huggingface.co/tarn59/pixel_art_style_lora_z_image_turbo/resolve/main/pixel_art_style_z_image_turbo.safetensors)\n\n**diffusion_models**\n\n- [z_image_turbo_bf16.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors)\n\n**vae**\n\n- [ae.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors)\n\n\nModel Storage Location\n\n```\n\ud83d\udcc2 ComfyUI/\n\u251c\u2500\u2500 \ud83d\udcc2 models/\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 text_encoders/\n\u2502 \u2502 \u2514\u2500\u2500 qwen_3_4b.safetensors\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 loras/\n\u2502 \u2502 \u2514\u2500\u2500 pixel_art_style_z_image_turbo.safetensors\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 diffusion_models/\n\u2502 \u2502 \u2514\u2500\u2500 z_image_turbo_bf16.safetensors\n\u2502 \u2514\u2500\u2500 \ud83d\udcc2 vae/\n\u2502 \u2514\u2500\u2500 ae.safetensors\n```\n"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
2759.9685719809763,
|
||||
991.0132526957497
|
||||
],
|
||||
"size": [
|
||||
1086.7317708333335,
|
||||
1170.0130208333335
|
||||
],
|
||||
"flags": {},
|
||||
"order": 25,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 111
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
1072.3258089884341,
|
||||
1251.3163769049763
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
88
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Reshape image to suit VAE "
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
130.8803835176007,
|
||||
238.85087590367888
|
||||
],
|
||||
"size": [
|
||||
269.77864583333337,
|
||||
96.15885416666667
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
54
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "z_image_turbo_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors",
|
||||
"directory": "diffusion_models"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"z_image_bf16.safetensors",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
6.077161599140862,
|
||||
923.8378757225194
|
||||
],
|
||||
"size": [
|
||||
273.88020833333337,
|
||||
546.8098958333334
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
103,
|
||||
109
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
105,
|
||||
110
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.59",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
},
|
||||
"image": "Masked_Load_Me_in_Loader (13).png [input]"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Masked_Load_Me_in_Loader (13).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"type": "CLIPLoader",
|
||||
"pos": [
|
||||
130.26337981400314,
|
||||
405.2542176588747
|
||||
],
|
||||
"size": [
|
||||
269.77864583333337,
|
||||
124.4921875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
44,
|
||||
98
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_3_4b.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors",
|
||||
"directory": "text_encoders"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_3_4b.safetensors",
|
||||
"lumina2",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
448.7872367553159,
|
||||
690.9253024127064
|
||||
],
|
||||
"size": [
|
||||
390,
|
||||
125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 98
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
102
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Negative Prompt)",
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
""
|
||||
],
|
||||
"color": "#323",
|
||||
"bgcolor": "#535"
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1828.9318713728562,
|
||||
114.5680075231259
|
||||
],
|
||||
"size": [
|
||||
187.5,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 112
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.76"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"type": "ModelSamplingAuraFlow",
|
||||
"pos": [
|
||||
920.7037987240392,
|
||||
248.48064332359456
|
||||
],
|
||||
"size": [
|
||||
309.89583333333337,
|
||||
67.82552083333334
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 60
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
100
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ModelSamplingAuraFlow",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.64",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
1869.2934574860883,
|
||||
468.13621723594605
|
||||
],
|
||||
"size": [
|
||||
372.64694675564056,
|
||||
406.22742984745275
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_KSampler",
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "6109df6591a4cf2bc9d3b113d03f7297fa9248e9"
|
||||
},
|
||||
"widgets_values": [
|
||||
1011254864048366,
|
||||
"randomize",
|
||||
20,
|
||||
4,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Prompt First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
450.2633140828448,
|
||||
275.25446846701436
|
||||
],
|
||||
"size": [
|
||||
409.77864583333337,
|
||||
374.77864583333337
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 44
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
101
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"A fashion photography work full of surreal romanticism, using a low-angle upward shooting composition, with a clear light blue sky as the background, and the visual focus concentrated on the fantasy blue vegetation and the cute gaint panda walking through it.\n\nThe vegetation in the picture is processed into varying shades of blue, from light ice blue to deep cobalt blue. The textures of the leaves and branches are delicate and realistic. The warm brown tree trunks form a sharp contrast with the cool blue leaves, resembling a dreamy forest from another world. A cute gaint panda walks slowly on the sand. The warm tones of the dress echo with the surrounding cool blue vegetation. The noon sun casts clear shadows on the sand, enhancing the sense of space and reality in the picture.\n\nThe entire scene, with its clean and transparent colors and fantasy settings, not only exudes the vastness of the natural wilderness but also presents a quiet and poetic high-fashion sense due to the surreal vegetation."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 76,
|
||||
"type": "LanPaint_KSamplerAdvanced",
|
||||
"pos": [
|
||||
1234.9129727307572,
|
||||
465.17562131822933
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
580
|
||||
],
|
||||
"flags": {},
|
||||
"order": 21,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 100
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 101
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 102
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 106
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
107
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_KSamplerAdvanced"
|
||||
},
|
||||
"widgets_values": [
|
||||
"enable",
|
||||
0,
|
||||
"fixed",
|
||||
20,
|
||||
4,
|
||||
"euler",
|
||||
"simple",
|
||||
0,
|
||||
10000,
|
||||
"disable",
|
||||
5,
|
||||
5.0,
|
||||
0.2,
|
||||
1,
|
||||
15,
|
||||
"Image First",
|
||||
1,
|
||||
"LanPaint KSampler Adv. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
600.0,
|
||||
500.0
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
150
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 103
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 104
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 105
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
106
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
1900.0,
|
||||
500.0
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
200
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 107
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 108
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 109
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 110
|
||||
},
|
||||
{
|
||||
"name": "blend_overlap",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
111,
|
||||
112
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [
|
||||
9
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
44,
|
||||
39,
|
||||
0,
|
||||
45,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
54,
|
||||
46,
|
||||
0,
|
||||
48,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
60,
|
||||
48,
|
||||
0,
|
||||
47,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
98,
|
||||
39,
|
||||
0,
|
||||
75,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
100,
|
||||
47,
|
||||
0,
|
||||
76,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
101,
|
||||
45,
|
||||
0,
|
||||
76,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
102,
|
||||
75,
|
||||
0,
|
||||
76,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
103,
|
||||
57,
|
||||
0,
|
||||
77,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
104,
|
||||
40,
|
||||
0,
|
||||
77,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
105,
|
||||
57,
|
||||
1,
|
||||
77,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
106,
|
||||
77,
|
||||
0,
|
||||
76,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
107,
|
||||
76,
|
||||
0,
|
||||
78,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
108,
|
||||
40,
|
||||
0,
|
||||
78,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
109,
|
||||
57,
|
||||
0,
|
||||
78,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
110,
|
||||
57,
|
||||
1,
|
||||
78,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
111,
|
||||
78,
|
||||
0,
|
||||
61,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
112,
|
||||
78,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Step2 - Image size",
|
||||
"bounding": [
|
||||
120,
|
||||
640,
|
||||
290,
|
||||
200
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Step3 - Prompt",
|
||||
"bounding": [
|
||||
430,
|
||||
210,
|
||||
450,
|
||||
540
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Step1 - Load models",
|
||||
"bounding": [
|
||||
120,
|
||||
210,
|
||||
290,
|
||||
413.6
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "Ctrl-B to enable LoRA input",
|
||||
"bounding": [
|
||||
430,
|
||||
40,
|
||||
440,
|
||||
160
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.2673486306413776,
|
||||
"offset": [
|
||||
853.4341376683553,
|
||||
289.09247282952845
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.37.11",
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true,
|
||||
"workflowRendererVersion": "LG"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
After Width: | Height: | Size: 237 KiB |
@@ -0,0 +1,925 @@
|
||||
{
|
||||
"id": "9ae6082b-c7f4-433c-9971-7a8f65a3ea65",
|
||||
"revision": 0,
|
||||
"last_node_id": 75,
|
||||
"last_link_id": 105,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 39,
|
||||
"type": "CLIPLoader",
|
||||
"pos": [
|
||||
158.31605577680375,
|
||||
488.30506119064967
|
||||
],
|
||||
"size": [
|
||||
323.734375,
|
||||
179.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
44
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "qwen_3_4b.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors",
|
||||
"directory": "text_encoders"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"qwen_3_4b.safetensors",
|
||||
"lumina2",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
159.30157212272297,
|
||||
693.9279006020886
|
||||
],
|
||||
"size": [
|
||||
323.734375,
|
||||
111.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
97,
|
||||
101
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAELoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "ae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors",
|
||||
"directory": "vae"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"ae.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"type": "ConditioningZeroOut",
|
||||
"pos": [
|
||||
794.3164307166835,
|
||||
836.3052371197768
|
||||
],
|
||||
"size": [
|
||||
236.984375,
|
||||
77.421875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 36
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
64
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ConditioningZeroOut",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"type": "EmptySD3LatentImage",
|
||||
"pos": [
|
||||
158.31605577680375,
|
||||
848.3047505883683
|
||||
],
|
||||
"size": [
|
||||
311.734375,
|
||||
179.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptySD3LatentImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.64",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
1024,
|
||||
1024,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"type": "ModelSamplingAuraFlow",
|
||||
"pos": [
|
||||
1106.844558468847,
|
||||
300.1767719883135
|
||||
],
|
||||
"size": [
|
||||
371.875,
|
||||
111.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 60
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
62
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ModelSamplingAuraFlow",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.64",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
542.3159768994137,
|
||||
332.3053621604172
|
||||
],
|
||||
"size": [
|
||||
491.734375,
|
||||
479.734375
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 44
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
36,
|
||||
63
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"Latina female with thick wavy hair, white shirt, harbor boats and pastel houses behind. Breezy seaside light, warm tones, cinematic close-up."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"type": "LoraLoaderModelOnly",
|
||||
"pos": [
|
||||
553.9994917632011,
|
||||
133.99988695553816
|
||||
],
|
||||
"size": [
|
||||
443.75,
|
||||
145.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 54
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
60
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoraLoaderModelOnly",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.75",
|
||||
"models": [
|
||||
{
|
||||
"name": "pixel_art_style_z_image_turbo.safetensors",
|
||||
"url": "https://huggingface.co/tarn59/pixel_art_style_lora_z_image_turbo/resolve/main/pixel_art_style_z_image_turbo.safetensors",
|
||||
"directory": "loras"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"pixel_art_style_z_image_turbo.safetensors",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"type": "MarkdownNote",
|
||||
"pos": [
|
||||
-466.00011584916956,
|
||||
289.9999302108306
|
||||
],
|
||||
"size": [
|
||||
587.734375,
|
||||
770.859375
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"title": "Model link",
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"## Report workflow issue\n\nIf you found any issues when running this workflow, [report template issue here](https://github.com/Comfy-Org/workflow_templates/issues)\n\n\n## Model links\n\n**text_encoders**\n\n- [qwen_3_4b.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors)\n\n**loras**\n\n- [pixel_art_style_z_image_turbo.safetensors](https://huggingface.co/tarn59/pixel_art_style_lora_z_image_turbo/resolve/main/pixel_art_style_z_image_turbo.safetensors)\n\n**diffusion_models**\n\n- [z_image_turbo_bf16.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors)\n\n**vae**\n\n- [ae.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors)\n\n\nModel Storage Location\n\n```\n\ud83d\udcc2 ComfyUI/\n\u251c\u2500\u2500 \ud83d\udcc2 models/\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 text_encoders/\n\u2502 \u2502 \u2514\u2500\u2500 qwen_3_4b.safetensors\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 loras/\n\u2502 \u2502 \u2514\u2500\u2500 pixel_art_style_z_image_turbo.safetensors\n\u2502 \u251c\u2500\u2500 \ud83d\udcc2 diffusion_models/\n\u2502 \u2502 \u2514\u2500\u2500 z_image_turbo_bf16.safetensors\n\u2502 \u2514\u2500\u2500 \ud83d\udcc2 vae/\n\u2502 \u2514\u2500\u2500 ae.safetensors\n```\n"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"type": "LanPaint_KSampler",
|
||||
"pos": [
|
||||
1280.6434468860161,
|
||||
480.42471689895115
|
||||
],
|
||||
"size": [
|
||||
479.765625,
|
||||
887.59375
|
||||
],
|
||||
"flags": {},
|
||||
"order": 20,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 62
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 63
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 64
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 99
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
100
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_KSampler",
|
||||
"cnr_id": "LanPaint",
|
||||
"ver": "6109df6591a4cf2bc9d3b113d03f7297fa9248e9"
|
||||
},
|
||||
"widgets_values": [
|
||||
880311146947153,
|
||||
"randomize",
|
||||
9,
|
||||
1,
|
||||
"euler",
|
||||
"simple",
|
||||
1,
|
||||
5,
|
||||
"Image First",
|
||||
"LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star \u2b50\ufe0f!",
|
||||
"\ud83d\uddbc\ufe0f Image Inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
9.292593918969033,
|
||||
1110.6054508670231
|
||||
],
|
||||
"size": [
|
||||
328.65625,
|
||||
686.171875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
96,
|
||||
102
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
96,
|
||||
102
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.59",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.1",
|
||||
"input_ue_unconnectable": {}
|
||||
},
|
||||
"image": "clipspace/clipspace-painted-masked-1764827669471.png [input]"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Example_21_Masked.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
3313.9622863771715,
|
||||
1191.2159032348995
|
||||
],
|
||||
"size": [
|
||||
1304.078125,
|
||||
1434.015625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 104
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.23"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
2226.506787053337,
|
||||
107.57278250350078
|
||||
],
|
||||
"size": [
|
||||
225,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 23,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 105
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.76"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
159.05646022112083,
|
||||
288.62105108441466
|
||||
],
|
||||
"size": [
|
||||
323.734375,
|
||||
145.390625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
54
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.73",
|
||||
"models": [
|
||||
{
|
||||
"name": "z_image_turbo_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors",
|
||||
"directory": "diffusion_models"
|
||||
}
|
||||
],
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65
|
||||
},
|
||||
"widgets_values": [
|
||||
"z_image_turbo_bf16.safetensors",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
1288.7909707861209,
|
||||
1503.5796522859714
|
||||
],
|
||||
"size": [
|
||||
225,
|
||||
109.765625
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Reshape image to suit VAE "
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 74,
|
||||
"type": "LanPaint_ImageEncode",
|
||||
"pos": [
|
||||
910.6,
|
||||
500.4
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
150
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 96
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 97
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 98
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "latent",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
99
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageEncode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "LanPaint_ImageDecode",
|
||||
"pos": [
|
||||
1630.6,
|
||||
500.4
|
||||
],
|
||||
"size": [
|
||||
300,
|
||||
200
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 100
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 101
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 102
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 103
|
||||
},
|
||||
{
|
||||
"name": "blend_overlap",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "blend_overlap"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
104,
|
||||
105
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets_values": [
|
||||
9
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LanPaint_ImageDecode",
|
||||
"cnr_id": "LanPaint"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
36,
|
||||
45,
|
||||
0,
|
||||
42,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
44,
|
||||
39,
|
||||
0,
|
||||
45,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
54,
|
||||
46,
|
||||
0,
|
||||
48,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
60,
|
||||
48,
|
||||
0,
|
||||
47,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
62,
|
||||
47,
|
||||
0,
|
||||
56,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
63,
|
||||
45,
|
||||
0,
|
||||
56,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
64,
|
||||
42,
|
||||
0,
|
||||
56,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
96,
|
||||
57,
|
||||
0,
|
||||
74,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
97,
|
||||
40,
|
||||
0,
|
||||
74,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
98,
|
||||
57,
|
||||
1,
|
||||
74,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
99,
|
||||
74,
|
||||
0,
|
||||
56,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
100,
|
||||
56,
|
||||
0,
|
||||
75,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
101,
|
||||
40,
|
||||
0,
|
||||
75,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
102,
|
||||
57,
|
||||
0,
|
||||
75,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
103,
|
||||
57,
|
||||
1,
|
||||
75,
|
||||
3,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
104,
|
||||
75,
|
||||
0,
|
||||
61,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
105,
|
||||
75,
|
||||
0,
|
||||
73,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Step2 - Image size",
|
||||
"bounding": [
|
||||
146,
|
||||
770,
|
||||
348,
|
||||
240
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Step3 - Prompt",
|
||||
"bounding": [
|
||||
518,
|
||||
254,
|
||||
540,
|
||||
648
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Step1 - Load models",
|
||||
"bounding": [
|
||||
146,
|
||||
254,
|
||||
348,
|
||||
496.32
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "Ctrl-B to enable LoRA input",
|
||||
"bounding": [
|
||||
518,
|
||||
50,
|
||||
528,
|
||||
192
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.3800835362432756,
|
||||
"offset": [
|
||||
-902.897494105397,
|
||||
-396.4805064253962
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.33.14",
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true,
|
||||
"workflowRendererVersion": "Vue"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 302 KiB |
|
Before Width: | Height: | Size: 683 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 4.3 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 797 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 79 KiB |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "LanPaint"
|
||||
version = "1.4.9"
|
||||
version = "2.1.0"
|
||||
description = "Achieve seamless inpainting results without needing a specialized inpainting model."
|
||||
authors = [
|
||||
{name = "LanPaint", email = "czhengac@connect.ust.hk"}
|
||||
@@ -75,5 +75,8 @@ select = [
|
||||
# See all rules here: https://docs.astral.sh/ruff/rules/#pyflakes-f
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"src/LanPaint/nodes.py" = ["F403", "F405"]
|
||||
|
||||
[tool.ruff.lint.flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
"""
|
||||
Early Stop Logic Contributed by `https://github.com/godnight10061`.
|
||||
"""
|
||||
|
||||
import inspect
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import torch
|
||||
|
||||
from .types import LangevinState
|
||||
|
||||
|
||||
def _clamp01(val: float) -> float:
|
||||
if val <= 0.0:
|
||||
return 0.0
|
||||
if val >= 1.0:
|
||||
return 1.0
|
||||
return val
|
||||
|
||||
|
||||
def _abt_scale(abt_val: float) -> float:
|
||||
"""
|
||||
Smooth, parameter-free scale based on outer-step noise level.
|
||||
|
||||
- 0 at abt=0/1 (disable at extreme noise / extreme tail)
|
||||
- 1 at abt=0.5 (mid-schedule)
|
||||
"""
|
||||
abt_val = _clamp01(abt_val)
|
||||
return _clamp01(4.0 * abt_val * (1.0 - abt_val))
|
||||
|
||||
|
||||
def _boundary_weight(latent_mask: torch.Tensor, inpaint_weight: torch.Tensor) -> Optional[torch.Tensor]:
|
||||
"""
|
||||
Return a 4-neighbor boundary weight: unknown pixels adjacent to known pixels.
|
||||
|
||||
This replaces the previous dilation-based "ring" (kernel/padding) and has no tunable hyperparameters.
|
||||
"""
|
||||
if latent_mask.dim() != 4:
|
||||
return None
|
||||
|
||||
known = latent_mask > 0.5
|
||||
neighbor_known = torch.zeros_like(known)
|
||||
neighbor_known[:, :, 1:, :] |= known[:, :, :-1, :]
|
||||
neighbor_known[:, :, :-1, :] |= known[:, :, 1:, :]
|
||||
neighbor_known[:, :, :, 1:] |= known[:, :, :, :-1]
|
||||
neighbor_known[:, :, :, :-1] |= known[:, :, :, 1:]
|
||||
|
||||
boundary = (~known) & neighbor_known
|
||||
return boundary.to(dtype=torch.float32) * inpaint_weight
|
||||
|
||||
|
||||
def _weighted_mse(t1: torch.Tensor, t2: torch.Tensor, weight: torch.Tensor) -> float:
|
||||
diff_sq = (t1.to(dtype=torch.float32) - t2.to(dtype=torch.float32)) ** 2
|
||||
denom = torch.sum(weight) + 1e-12
|
||||
return float((torch.sum(diff_sq * weight) / denom).item())
|
||||
|
||||
|
||||
class LanPaintEarlyStopper:
|
||||
"""
|
||||
Per-step early-stop logic for LanPaint inner (Langevin) iterations.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_options(
|
||||
cls,
|
||||
*,
|
||||
model_options: Optional[dict],
|
||||
latent_mask: torch.Tensor,
|
||||
abt: torch.Tensor,
|
||||
default_threshold: float,
|
||||
default_patience: int,
|
||||
default_distance_fn: Optional[Callable[..., Any]],
|
||||
) -> Optional["LanPaintEarlyStopper"]:
|
||||
semantic_stop = model_options.get("lanpaint_semantic_stop") if isinstance(model_options, dict) else None
|
||||
|
||||
threshold = float(default_threshold)
|
||||
patience = int(default_patience)
|
||||
distance_fn = default_distance_fn
|
||||
# distance_fn contract: return None (use default metric) or a scalar (Python number / 0-d (1-element) torch.Tensor)
|
||||
|
||||
if isinstance(semantic_stop, dict):
|
||||
threshold = float(semantic_stop.get("threshold", threshold))
|
||||
patience = int(semantic_stop.get("patience", patience))
|
||||
distance_fn = semantic_stop.get("distance_fn", distance_fn)
|
||||
|
||||
# Backward compatibility: map legacy 'min_steps' to a patience floor so it is not an independent knob.
|
||||
if patience > 0:
|
||||
min_steps = semantic_stop.get("min_steps")
|
||||
if min_steps is not None:
|
||||
try:
|
||||
min_steps_int = int(min_steps)
|
||||
except (TypeError, ValueError):
|
||||
min_steps_int = 0
|
||||
if min_steps_int > 1:
|
||||
patience = max(patience, min_steps_int - 1)
|
||||
|
||||
enabled_early_stop = (threshold > 0.0) and (patience > 0)
|
||||
# Require N+1 consecutive stable checks:
|
||||
# - the first stable step sets patience_counter to 1
|
||||
# - `patience=1` therefore stops after 2 stable steps
|
||||
patience_eff = max(1, patience) + 1
|
||||
threshold_eff = threshold
|
||||
inpaint_weight = ring_weight = trace = abt_val = None
|
||||
|
||||
if enabled_early_stop:
|
||||
try:
|
||||
abt_val = float(torch.mean(abt).item())
|
||||
except (TypeError, ValueError):
|
||||
abt_val = 0.0
|
||||
|
||||
threshold_eff = threshold * _abt_scale(abt_val)
|
||||
if threshold_eff <= 0.0:
|
||||
enabled_early_stop = False
|
||||
else:
|
||||
inpaint_weight = (1 - latent_mask).to(dtype=torch.float32)
|
||||
if float(torch.sum(inpaint_weight).item()) < 1e-6:
|
||||
enabled_early_stop = False
|
||||
else:
|
||||
ring_weight = _boundary_weight(latent_mask, inpaint_weight)
|
||||
if isinstance(model_options, dict):
|
||||
trace = model_options.get("lanpaint_semantic_trace")
|
||||
|
||||
if not enabled_early_stop:
|
||||
return None
|
||||
|
||||
# Pre-fetch trace keys to avoid repeated dict lookups
|
||||
bench_case_id = bench_outer_step = bench_timestep = None
|
||||
if isinstance(trace, list) and isinstance(model_options, dict):
|
||||
bench_case_id = model_options.get("bench_case_id")
|
||||
bench_outer_step = model_options.get("bench_outer_step")
|
||||
bench_timestep = model_options.get("bench_timestep")
|
||||
|
||||
return cls(
|
||||
enabled=enabled_early_stop,
|
||||
threshold=threshold,
|
||||
threshold_eff=threshold_eff,
|
||||
patience_eff=patience_eff,
|
||||
inpaint_weight=inpaint_weight,
|
||||
ring_weight=ring_weight,
|
||||
distance_fn=distance_fn,
|
||||
trace=trace,
|
||||
bench_case_id=bench_case_id,
|
||||
bench_outer_step=bench_outer_step,
|
||||
bench_timestep=bench_timestep,
|
||||
abt_val=abt_val,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
enabled: bool,
|
||||
threshold: float,
|
||||
threshold_eff: float,
|
||||
patience_eff: int,
|
||||
inpaint_weight: Optional[torch.Tensor],
|
||||
ring_weight: Optional[torch.Tensor],
|
||||
distance_fn: Optional[Callable[..., Any]] = None,
|
||||
trace: Optional[list] = None,
|
||||
bench_case_id: Any = None,
|
||||
bench_outer_step: Any = None,
|
||||
bench_timestep: Any = None,
|
||||
abt_val: Optional[float] = None,
|
||||
) -> None:
|
||||
self.enabled = bool(enabled)
|
||||
self.threshold = float(threshold)
|
||||
self.threshold_eff = float(threshold_eff)
|
||||
self.patience_eff = int(patience_eff)
|
||||
|
||||
self.inpaint_weight = inpaint_weight
|
||||
self.ring_weight = ring_weight
|
||||
|
||||
self.trace = trace
|
||||
self.bench_case_id = bench_case_id
|
||||
self.bench_outer_step = bench_outer_step
|
||||
self.bench_timestep = bench_timestep
|
||||
self.abt_val = abt_val
|
||||
|
||||
self.patience_counter = 0
|
||||
self.x0_anchor = None
|
||||
|
||||
self._dist_wrapper = self._wrap_distance_fn(distance_fn) if self.enabled else None
|
||||
|
||||
@property
|
||||
def has_custom_distance_fn(self) -> bool:
|
||||
return self._dist_wrapper is not None
|
||||
|
||||
@staticmethod
|
||||
def _wrap_distance_fn(distance_fn: Optional[Callable[..., Any]]):
|
||||
"""
|
||||
Wrap a user-provided `distance_fn` into a normalized callable: fn(prev, cur, ctx) -> dist|None.
|
||||
|
||||
Supported signatures:
|
||||
- 3+ positional (or *args): `distance_fn(prev, cur, ctx)`
|
||||
- explicit / **kwargs ctx: `distance_fn(prev, cur, ctx=ctx)`
|
||||
- default 2-arg: `distance_fn(cur, prev)`
|
||||
|
||||
Return contract: None (use default metric) or a scalar (Python number / 0-d (1-element) torch.Tensor).
|
||||
"""
|
||||
if not callable(distance_fn):
|
||||
return None
|
||||
|
||||
try:
|
||||
sig = inspect.signature(distance_fn)
|
||||
params = list(sig.parameters.values())
|
||||
|
||||
has_ctx_param = "ctx" in sig.parameters
|
||||
has_var_kw = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params)
|
||||
has_var_pos = any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in params)
|
||||
|
||||
pos_params = [
|
||||
p
|
||||
for p in params
|
||||
if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)
|
||||
]
|
||||
|
||||
if len(pos_params) >= 3 or has_var_pos:
|
||||
# 3-arg positional: fn(prev, cur, ctx)
|
||||
return lambda p, c, ctx: distance_fn(p, c, ctx)
|
||||
if has_ctx_param or has_var_kw:
|
||||
# keyword ctx: fn(prev, cur, ctx=ctx)
|
||||
return lambda p, c, ctx: distance_fn(p, c, ctx=ctx)
|
||||
|
||||
# Default 2-arg: fn(cur, prev)
|
||||
return lambda p, c, ctx: distance_fn(c, p)
|
||||
except (ValueError, TypeError):
|
||||
# Fallback for built-ins or complex callables.
|
||||
def fallback_wrapper(p, c, ctx):
|
||||
try:
|
||||
return distance_fn(p, c, ctx)
|
||||
except TypeError as e:
|
||||
tb = e.__traceback__
|
||||
if tb is not None and tb.tb_frame.f_code is not fallback_wrapper.__code__:
|
||||
raise
|
||||
return distance_fn(c, p)
|
||||
|
||||
return fallback_wrapper
|
||||
|
||||
def step(
|
||||
self,
|
||||
*,
|
||||
i: int,
|
||||
n_steps: int,
|
||||
x_t_before: torch.Tensor,
|
||||
x_t_after: torch.Tensor,
|
||||
x_t_prev_for_custom: Optional[torch.Tensor],
|
||||
prev_args: Any,
|
||||
args: Any,
|
||||
ctx: dict,
|
||||
) -> bool:
|
||||
if not self.enabled:
|
||||
return False
|
||||
|
||||
# 'inpaint_weight' is guaranteed to be set when enabled is True in the caller.
|
||||
inpaint = self.inpaint_weight
|
||||
if inpaint is None:
|
||||
return False
|
||||
|
||||
dist = None
|
||||
custom_dist = False
|
||||
dist_inpaint = dist_ring = dist_drift = x0_prev = x0_cur = None
|
||||
|
||||
if self._dist_wrapper is not None:
|
||||
dist = self._dist_wrapper(x_t_prev_for_custom, x_t_after, ctx)
|
||||
if dist is not None:
|
||||
if isinstance(dist, torch.Tensor):
|
||||
if dist.numel() != 1:
|
||||
raise TypeError("distance_fn must return None or a scalar / 0-d (1-element) tensor")
|
||||
dist = float(dist.item())
|
||||
else:
|
||||
dist = float(dist)
|
||||
custom_dist = dist is not None
|
||||
|
||||
if dist is None:
|
||||
def _get_x0(arg: Any) -> Optional[torch.Tensor]:
|
||||
if isinstance(arg, LangevinState):
|
||||
return arg.x0
|
||||
if isinstance(arg, tuple) and len(arg) >= 3:
|
||||
return arg[2]
|
||||
return None
|
||||
|
||||
x0_prev = _get_x0(prev_args)
|
||||
x0_cur = _get_x0(args)
|
||||
|
||||
if x0_prev is not None and x0_cur is not None:
|
||||
dist_inpaint = _weighted_mse(x0_cur, x0_prev, inpaint)
|
||||
dist_ring = _weighted_mse(x0_cur, x0_prev, self.ring_weight) if self.ring_weight is not None else None
|
||||
dist = dist_inpaint if dist_ring is None else max(dist_inpaint, dist_ring)
|
||||
else:
|
||||
dist_inpaint = _weighted_mse(x_t_after, x_t_before, inpaint)
|
||||
dist = dist_inpaint
|
||||
|
||||
threshold_used = self.threshold if custom_dist else self.threshold_eff
|
||||
|
||||
# Drift guard (only for default metric with x0_cur).
|
||||
if x0_cur is not None and not custom_dist:
|
||||
if dist <= threshold_used:
|
||||
if self.x0_anchor is None:
|
||||
self.x0_anchor = x0_cur.detach()
|
||||
else:
|
||||
drift_inpaint = _weighted_mse(x0_cur, self.x0_anchor, inpaint)
|
||||
drift_ring = _weighted_mse(x0_cur, self.x0_anchor, self.ring_weight) if self.ring_weight is not None else None
|
||||
dist_drift = drift_inpaint if drift_ring is None else max(drift_inpaint, drift_ring)
|
||||
dist = max(dist, dist_drift)
|
||||
else:
|
||||
self.x0_anchor = None
|
||||
|
||||
if dist <= threshold_used:
|
||||
self.patience_counter += 1
|
||||
else:
|
||||
self.patience_counter = 0
|
||||
self.x0_anchor = None
|
||||
|
||||
should_stop = self.patience_counter >= self.patience_eff
|
||||
|
||||
if isinstance(self.trace, list):
|
||||
self.trace.append(
|
||||
{
|
||||
"case_id": self.bench_case_id,
|
||||
"outer_step": self.bench_outer_step,
|
||||
"bench_timestep": self.bench_timestep,
|
||||
"inner_step": i + 1,
|
||||
"dist": dist,
|
||||
"dist_inpaint": None if dist_inpaint is None else float(dist_inpaint),
|
||||
"dist_ring": None if dist_ring is None else float(dist_ring),
|
||||
"dist_drift": None if dist_drift is None else float(dist_drift),
|
||||
"threshold": float(threshold_used),
|
||||
"threshold_eff": float(self.threshold_eff),
|
||||
"patience_counter": int(self.patience_counter),
|
||||
"patience_eff": int(self.patience_eff),
|
||||
"abt": None if self.abt_val is None else float(self.abt_val),
|
||||
"custom_dist": bool(custom_dist),
|
||||
"stopped": bool(should_stop),
|
||||
}
|
||||
)
|
||||
|
||||
return bool(should_stop)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import torch
|
||||
from .utils import *
|
||||
# from .utils import StochasticHarmonicOscillator # second-order scheme, not used
|
||||
from functools import partial
|
||||
from .earlystop import LanPaintEarlyStopper
|
||||
from .types import LangevinState
|
||||
|
||||
class LanPaint():
|
||||
def __init__(self, Model, NSteps, Friction, Lambda, Beta, StepSize, IS_FLUX = False, IS_FLOW = False):
|
||||
def __init__(self, Model, NSteps, Friction, Lambda, Beta, StepSize, IS_FLUX = False, IS_FLOW = False, EarlyStopThreshold = 0.0, EarlyStopPatience = 1, EarlyStopHook = None, MinStepFrac = 0.0):
|
||||
self.n_steps = NSteps
|
||||
self.chara_lamb = Lambda
|
||||
self.IS_FLUX = IS_FLUX
|
||||
@@ -12,39 +14,84 @@ class LanPaint():
|
||||
self.inner_model = Model
|
||||
self.friction = Friction
|
||||
self.chara_beta = Beta
|
||||
self.min_step_frac = MinStepFrac
|
||||
self.img_dim_size = None
|
||||
self.early_stop_threshold = EarlyStopThreshold
|
||||
self.early_stop_patience = EarlyStopPatience
|
||||
self.early_stop_hook = EarlyStopHook
|
||||
|
||||
def add_none_dims(self, array):
|
||||
# Create a tuple with ':' for the first dimension and 'None' repeated num_nones times
|
||||
index = (slice(None),) + (None,) * (self.img_dim_size-1)
|
||||
return array[index]
|
||||
# Broadcast to the latent's dimensionality. Identical to the tuple-index
|
||||
# form for scalar/[B] inputs; per-row (already broadcast) tensors pass
|
||||
# through unchanged.
|
||||
while array.ndim < self.img_dim_size:
|
||||
array = array.unsqueeze(array.ndim)
|
||||
return array
|
||||
def remove_none_dims(self, array):
|
||||
# Create a tuple with ':' for the first dimension and 'None' repeated num_nones times
|
||||
index = (slice(None),) + (0,) * (self.img_dim_size-1)
|
||||
return array[index]
|
||||
def __call__(self, x, latent_image, noise, sigma, latent_mask, current_times, model_options, seed, n_steps=None):
|
||||
def unpack_model_output(self, output):
|
||||
# Some guider/model wrappers return one denoised latent, others return
|
||||
# both the normal and BIG-guidance denoised latents.
|
||||
if isinstance(output, (tuple, list)):
|
||||
if len(output) >= 2:
|
||||
return output[0], output[1]
|
||||
if len(output) == 1:
|
||||
return output[0], output[0]
|
||||
raise ValueError("Model output is empty")
|
||||
return output, output
|
||||
def __call__(self, x, latent_image, noise, sigma, latent_mask, current_times, model_options, seed, n_steps=None, current_times_audio=None, audio_indicator=None, audio_correction=None):
|
||||
self.img_dim_size = len(x.shape)
|
||||
self.latent_image = latent_image
|
||||
self.noise = noise
|
||||
self.audio_indicator = audio_indicator
|
||||
self.current_times_audio = current_times_audio
|
||||
self.audio_correction = audio_correction
|
||||
if torch.mean(torch.abs(self.noise)) < 1e-8:
|
||||
self.noise = torch.randn_like(self.noise)
|
||||
if n_steps is None:
|
||||
n_steps = self.n_steps
|
||||
return self.LanPaint(x, sigma, latent_mask, current_times, n_steps, model_options, seed, self.IS_FLUX, self.IS_FLOW)
|
||||
def LanPaint(self, x, sigma, latent_mask, current_times, n_steps, model_options, seed, IS_FLUX, IS_FLOW):
|
||||
input_x = x
|
||||
VE_Sigma, abt, Flow_t = current_times
|
||||
|
||||
|
||||
step_size = self.step_size * (1 - abt)
|
||||
# MiniMax H3 AV packs: the audio rows of the flat pack run on their own
|
||||
# shifted sigma schedule (sigma_audio = time_shift_sigma(sigma_video,
|
||||
# shift_v, shift_a)). Blend the per-stream times so every downstream
|
||||
# consumer (x_t conversions, score, dynamics coefficients, replace
|
||||
# step) uses the audio schedule on the audio rows and the video
|
||||
# schedule elsewhere. Flow_t stays the video timestep -- the DiT
|
||||
# derives the audio schedule from it internally.
|
||||
replace_sigma = sigma
|
||||
if self.audio_indicator is not None and self.current_times_audio is not None:
|
||||
VE_a, abt_a, Flow_a = self.current_times_audio
|
||||
ai = self.audio_indicator
|
||||
VE_Sigma = VE_Sigma * (1 - ai) + VE_a * ai
|
||||
abt = abt * (1 - ai) + abt_a * ai
|
||||
replace_sigma = sigma * (1 - ai) + Flow_a * ai
|
||||
current_times = (VE_Sigma, abt, Flow_t)
|
||||
|
||||
# Above MinStepFrac the step size scales with the remaining noise
|
||||
# fraction (1 - abt); below it the step size is pinned at
|
||||
# StepSize*MinStepFrac and the inner-step count ramps down instead
|
||||
# (see KSamplerX0Inpaint.__call__). 0.0 disables the pin (the step
|
||||
# size keeps shrinking to zero as before).
|
||||
step_size = self.step_size * (1 - abt).clamp(min=self.min_step_frac)
|
||||
step_size = self.add_none_dims(step_size)
|
||||
# self.inner_model.inner_model.scale_latent_inpaint returns variance exploding x_t values
|
||||
# This is the replace step
|
||||
def scale_latent_inpaint(x, sigma, noise, latent_image):
|
||||
return self.inner_model.inner_model.model_sampling.noise_scaling(sigma.reshape([sigma.shape[0]] + [1] * (len(noise.shape) - 1)), noise, latent_image)
|
||||
|
||||
x = x * (1 - latent_mask) + scale_latent_inpaint(x=x, sigma=sigma, noise=self.noise, latent_image=self.latent_image)* latent_mask
|
||||
|
||||
s = self.add_none_dims(sigma)
|
||||
if s.numel() == 1:
|
||||
return self.inner_model.inner_model.model_sampling.noise_scaling(s, noise, latent_image)
|
||||
# per-row (audio) sigma: model_sampling.noise_scaling requires a
|
||||
# scalar sigma, so emulate its flow form elementwise
|
||||
ns = getattr(self.inner_model.inner_model.model_sampling, "noise_scale", 1.0)
|
||||
return s * (ns * noise) + (1.0 - s) * latent_image
|
||||
|
||||
x = x * (1 - latent_mask) + scale_latent_inpaint(x=x, sigma=replace_sigma, noise=self.noise, latent_image=self.latent_image)* latent_mask
|
||||
|
||||
if IS_FLUX or IS_FLOW:
|
||||
x_t = x * ( self.add_none_dims(abt)**0.5 + (1-self.add_none_dims(abt))**0.5 )
|
||||
@@ -54,32 +101,86 @@ class LanPaint():
|
||||
############ LanPaint Iterations Start ###############
|
||||
# after noise_scaling, noise = latent_image + noise * sigma, which is x_t in the variance exploding diffusion model notation for the known region.
|
||||
args = None
|
||||
stopper = LanPaintEarlyStopper.from_options(
|
||||
model_options=model_options if isinstance(model_options, dict) else None,
|
||||
latent_mask=latent_mask,
|
||||
abt=abt,
|
||||
default_threshold=self.early_stop_threshold,
|
||||
default_patience=self.early_stop_patience,
|
||||
default_distance_fn=self.early_stop_hook,
|
||||
)
|
||||
|
||||
for i in range(n_steps):
|
||||
score_func = partial( self.score_model, y = self.latent_image, mask = latent_mask, abt = self.add_none_dims(abt), sigma = self.add_none_dims(VE_Sigma), tflow = self.add_none_dims(Flow_t), model_options = model_options, seed = seed )
|
||||
|
||||
prev_args = args
|
||||
x_t_prev = x_t.detach() if (stopper is not None and stopper.has_custom_distance_fn) else None
|
||||
x_t_before = x_t if (stopper is not None and stopper.enabled) else None
|
||||
|
||||
x_t, args = self.langevin_dynamics(x_t, score_func , latent_mask, step_size , current_times, sigma_x = self.add_none_dims(self.sigma_x(abt)), sigma_y = self.add_none_dims(self.sigma_y(abt)), args = args)
|
||||
|
||||
if stopper is not None:
|
||||
ctx = {
|
||||
"step": i,
|
||||
"steps_done": i + 1,
|
||||
"n_steps": n_steps,
|
||||
"mask": latent_mask,
|
||||
"latent_image": self.latent_image,
|
||||
"current_times": current_times,
|
||||
"seed": seed,
|
||||
}
|
||||
if stopper.step(
|
||||
i=i,
|
||||
n_steps=n_steps,
|
||||
x_t_before=x_t_before,
|
||||
x_t_after=x_t,
|
||||
x_t_prev_for_custom=x_t_prev,
|
||||
prev_args=prev_args,
|
||||
args=args,
|
||||
ctx=ctx,
|
||||
):
|
||||
break
|
||||
|
||||
if IS_FLUX or IS_FLOW:
|
||||
x = x_t / ( self.add_none_dims(abt)**0.5 + (1-self.add_none_dims(abt))**0.5 )
|
||||
else:
|
||||
x = x_t * ( 1+self.add_none_dims(VE_Sigma)**2 )**0.5 # switch to variance perserving x_t values
|
||||
############ LanPaint Iterations End ###############
|
||||
# out is x_0
|
||||
out, _ = self.inner_model(x, sigma, model_options=model_options, seed=seed)
|
||||
|
||||
out, _ = self.unpack_model_output(
|
||||
self.inner_model(x, sigma, model_options=model_options, seed=seed)
|
||||
)
|
||||
out = out * (1-latent_mask) + self.latent_image * latent_mask
|
||||
|
||||
input_x.copy_(x)
|
||||
return out
|
||||
|
||||
def score_model(self, x_t, y, mask, abt, sigma, tflow, model_options, seed):
|
||||
|
||||
lamb = self.chara_lamb
|
||||
if self.IS_FLUX or self.IS_FLOW:
|
||||
# compute t for flow model, with a small epsilon compensating for numerical error.
|
||||
x = x_t / ( abt**0.5 + (1-abt)**0.5 ) # switch to Gaussian flow matching
|
||||
x_0, x_0_BIG = self.inner_model(x, self.remove_none_dims(tflow), model_options=model_options, seed=seed)
|
||||
x_0, x_0_BIG = self.unpack_model_output(
|
||||
self.inner_model(x, self.remove_none_dims(tflow), model_options=model_options, seed=seed)
|
||||
)
|
||||
else:
|
||||
x = x_t * ( 1+sigma**2 )**0.5 # switch to variance exploding
|
||||
x_0, x_0_BIG = self.inner_model(x, self.remove_none_dims(sigma), model_options=model_options, seed=seed)
|
||||
x_0, x_0_BIG = self.unpack_model_output(
|
||||
self.inner_model(x, self.remove_none_dims(sigma), model_options=model_options, seed=seed)
|
||||
)
|
||||
|
||||
if getattr(self, "audio_correction", None) is not None:
|
||||
# The flat-grid model output for the audio rows is the slope-scaled
|
||||
# velocity estimate, which overshoots the true denoised audio by
|
||||
# sigma_v*slope/sigma_a. Pull the Langevin target back to the true
|
||||
# audio denoised: x0_true = x + c*(x0_flat - x), c = 1 on video rows
|
||||
# (video is untouched).
|
||||
x_0 = x + self.audio_correction * (x_0 - x)
|
||||
x_0_BIG = x + self.audio_correction * (x_0_BIG - x)
|
||||
|
||||
score_x = -(x_t - x_0)
|
||||
score_y = - (1 + lamb) * ( x_t - y ) + lamb * (x_t - x_0_BIG)
|
||||
score_y = - (1 + lamb) * ( x_t - y ) + lamb * (x_t - x_0_BIG)
|
||||
return score_x * (1 - mask) + score_y * mask
|
||||
def sigma_x(self, abt):
|
||||
# the time scale for the x_t update
|
||||
@@ -89,6 +190,13 @@ class LanPaint():
|
||||
return beta
|
||||
|
||||
def langevin_dynamics(self, x_t, score, mask, step_size, current_times, sigma_x=1, sigma_y=0, args=None):
|
||||
if args is not None and not isinstance(args, LangevinState):
|
||||
if isinstance(args, tuple):
|
||||
if len(args) == 2:
|
||||
# Backwards compat: older state was (v, C) without x0.
|
||||
args = LangevinState(args[0], args[1], None)
|
||||
elif len(args) >= 3:
|
||||
args = LangevinState(args[0], args[1], args[2])
|
||||
# prepare the step size and time parameters
|
||||
with torch.autocast(device_type=x_t.device.type, dtype=torch.float32):
|
||||
step_sizes = self.prepare_step_size(current_times, step_size, sigma_x, sigma_y)
|
||||
@@ -104,40 +212,85 @@ class LanPaint():
|
||||
A = A_x * (1-mask) + A_y * mask
|
||||
D = D_x * (1-mask) + D_y * mask
|
||||
dt = dtx * (1-mask) + dty * mask
|
||||
Gamma = Gamma_x * (1-mask) + Gamma_y * mask
|
||||
|
||||
# Gamma = Gamma_x * (1-mask) + Gamma_y * mask # only used by the disabled second-order scheme
|
||||
|
||||
def Coef_C(x_t):
|
||||
x0 = self.x0_evalutation(x_t, score, sigma, args)
|
||||
x0 = x_t + score(x_t)
|
||||
C = (abt**0.5 * x0 - x_t )/ (1-abt) + A * x_t
|
||||
return C
|
||||
def advance_time(x_t, v, dt, Gamma, A, C, D):
|
||||
return C, x0
|
||||
# Second-order damped-oscillator update (position + velocity via
|
||||
# StochasticHarmonicOscillator) -- kept for reference, not used.
|
||||
# def advance_time(x_t, v, dt, Gamma, A, C, D):
|
||||
# dtype = x_t.dtype
|
||||
# with torch.autocast(device_type=x_t.device.type, dtype=torch.float32):
|
||||
# osc = StochasticHarmonicOscillator(Gamma, A, C, D )
|
||||
# x_t, v = osc.dynamics(x_t, v, dt )
|
||||
# x_t = x_t.to(dtype)
|
||||
# v = v.to(dtype)
|
||||
# return x_t, v
|
||||
|
||||
def advance_time_overdamped(x_t, dt, A, C, D):
|
||||
"""
|
||||
Overdamped (Gamma -> infinity) limit:
|
||||
dx = -A x dt + C dt + D dW_t
|
||||
with C treated as constant over this substep.
|
||||
"""
|
||||
dtype = x_t.dtype
|
||||
with torch.autocast(device_type=x_t.device.type, dtype=torch.float32):
|
||||
osc = StochasticHarmonicOscillator(Gamma, A, C, D )
|
||||
x_t, v = osc.dynamics(x_t, v, dt )
|
||||
x_t = x_t.to(dtype)
|
||||
v = v.to(dtype)
|
||||
return x_t, v
|
||||
if args is None:
|
||||
#v = torch.zeros_like(x_t)
|
||||
v = None
|
||||
C = Coef_C(x_t)
|
||||
#print(torch.squeeze(dtx), torch.squeeze(dty))
|
||||
x_t, v = advance_time(x_t, v, dt, Gamma, A, C, D)
|
||||
else:
|
||||
v, C = args
|
||||
A_dt = A * dt
|
||||
exp_neg = torch.exp(-A_dt)
|
||||
|
||||
x_t, v = advance_time(x_t, v, dt/2, Gamma, A, C, D)
|
||||
eps = 1e-8
|
||||
abs_A = torch.abs(A)
|
||||
# k = (1 - exp(-A dt)) / A -> dt when A -> 0
|
||||
k = torch.where(abs_A < eps, dt, (-torch.expm1(-A_dt)) / A)
|
||||
# k2 = (1 - exp(-2 A dt)) / (2 A) -> dt when A -> 0
|
||||
k2 = torch.where(abs_A < eps, dt, (-torch.expm1(-2 * A_dt)) / (2 * A))
|
||||
|
||||
C_new = Coef_C(x_t)
|
||||
v = v + Gamma**0.5 * ( C_new - C) *dt
|
||||
mean = exp_neg * x_t + k * C
|
||||
var = (D ** 2) * k2
|
||||
noise = torch.randn_like(x_t) * torch.sqrt(torch.clamp(var, min=0.0))
|
||||
x_t = mean + noise
|
||||
return x_t.to(dtype)
|
||||
|
||||
x_t, v = advance_time(x_t, v, dt/2, Gamma, A, C, D)
|
||||
# Second-order damped-oscillator scheme (position + velocity) -- kept
|
||||
# for reference, not used.
|
||||
# def run_damped(x_t, args):
|
||||
# if args is None:
|
||||
# v = None
|
||||
# C, x0 = Coef_C(x_t)
|
||||
# x_t, v = advance_time(x_t, v, dt, Gamma, A, C, D)
|
||||
# else:
|
||||
# v = args.v
|
||||
# C = args.C
|
||||
# x_t, v = advance_time(x_t, v, dt/2, Gamma, A, C, D)
|
||||
# C_new, x0 = Coef_C(x_t)
|
||||
# v = v + Gamma**0.5 * ( C_new - C) *dt
|
||||
# x_t, v = advance_time(x_t, v, dt/2, Gamma, A, C, D)
|
||||
# C = C_new
|
||||
# # args is (v, C, x0) for the next inner step.
|
||||
# return x_t, LangevinState(v, C, x0)
|
||||
|
||||
C = C_new
|
||||
|
||||
return x_t, (v, C)
|
||||
def run_overdamped(x_t, args):
|
||||
if args is None:
|
||||
C, x0 = Coef_C(x_t)
|
||||
x_t = advance_time_overdamped(x_t, dt, A, C, D)
|
||||
else:
|
||||
C = args.C
|
||||
x_t = advance_time_overdamped(x_t, dt / 2, A, C, D)
|
||||
C_new, x0 = Coef_C(x_t)
|
||||
x_t = x_t + (C_new - C) * dt
|
||||
x_t = advance_time_overdamped(x_t, dt / 2, A, C, D)
|
||||
C = C_new
|
||||
# args is (v, C, x0); v is None in the overdamped fallback.
|
||||
return x_t, LangevinState(None, C, x0)
|
||||
|
||||
# Only the first-order (overdamped) scheme is used; the second-order
|
||||
# damped-oscillator scheme is kept commented out above.
|
||||
x_t, state = run_overdamped(x_t, args)
|
||||
|
||||
# args is (v, C, x0); v is always None in the overdamped scheme.
|
||||
return x_t, state
|
||||
|
||||
def prepare_step_size(self, current_times, step_size, sigma_x, sigma_y):
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -148,7 +301,7 @@ class LanPaint():
|
||||
# Compute time step (dtx, dty) for x and y branches.
|
||||
dtx = 2 * step_size * sigma_x
|
||||
dty = 2 * step_size * sigma_y
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Define friction parameter Gamma_hat for each branch.
|
||||
# Using dtx**0 provides a tensor of the proper device/dtype.
|
||||
@@ -173,9 +326,3 @@ class LanPaint():
|
||||
D_x = (2 * abt**0 )**0.5
|
||||
D_y = (2 * abt**0 )**0.5
|
||||
return sigma, abt, dtx/2, dty/2, Gamma_x, Gamma_y, A_x, A_y, D_x, D_y
|
||||
|
||||
|
||||
|
||||
def x0_evalutation(self, x_t, score, sigma, args):
|
||||
x0 = x_t + score(x_t)
|
||||
return x0
|
||||
@@ -0,0 +1,10 @@
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
class LangevinState(NamedTuple):
|
||||
v: Optional[torch.Tensor]
|
||||
C: Optional[torch.Tensor]
|
||||
x0: Optional[torch.Tensor]
|
||||
|
||||
@@ -28,11 +28,11 @@ def expm1mxmhx2_x3(x):
|
||||
def exp_1mcosh_GD(gamma_t, delta):
|
||||
"""
|
||||
Compute e^(-Γt) * (1 - cosh(Γt√Δ))/ ( (Γt)**2 Δ )
|
||||
|
||||
|
||||
Parameters:
|
||||
gamma_t: Γ*t term (could be a scalar or tensor)
|
||||
delta: Δ term (could be a scalar or tensor)
|
||||
|
||||
|
||||
Returns:
|
||||
Result of the computation with numerical stability handling
|
||||
"""
|
||||
@@ -68,7 +68,6 @@ def exp_sinh_GsqrtD(gamma_t, delta):
|
||||
sqrt_abs_delta = torch.sqrt(torch.abs(delta))
|
||||
gamma_t_sqrt_delta = gamma_t * sqrt_abs_delta
|
||||
numerator_pos = (torch.exp(gamma_t * (sqrt_abs_delta - 1)) - torch.exp(gamma_t * (-sqrt_abs_delta - 1))) / 2
|
||||
denominator_pos = gamma_t_sqrt_delta
|
||||
result_pos = numerator_pos / gamma_t_sqrt_delta
|
||||
result_pos = torch.where(torch.isfinite(result_pos), result_pos, torch.zeros_like(result_pos))
|
||||
|
||||
@@ -117,15 +116,15 @@ def zeta1(gamma_t, delta):
|
||||
exp_cosh_term = exp_cosh(half_gamma_t, delta)
|
||||
exp_sinh_term = exp_sinh_sqrtD(half_gamma_t, delta)
|
||||
|
||||
|
||||
|
||||
# Main computation
|
||||
numerator = 1 - (exp_cosh_term + exp_sinh_term)
|
||||
denominator = gamma_t * (1 - delta) / 4
|
||||
result = 1 - numerator / denominator
|
||||
|
||||
|
||||
# Handle numerical instability
|
||||
result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
|
||||
|
||||
|
||||
# Taylor expansion for small x (similar to your epxm1Dx approach)
|
||||
mask = torch.abs(denominator) < 5e-3
|
||||
term1 = epxm1_x(-gamma_t)
|
||||
@@ -133,17 +132,17 @@ def zeta1(gamma_t, delta):
|
||||
term3 = expm1mxmhx2_x3(-gamma_t)
|
||||
taylor = term1 + (1/2.+ term1-3*term2)*denominator + (-1/6. + term1/2 - 4 * term2 + 10 * term3) * denominator**2
|
||||
result = torch.where(mask, taylor, result)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
def exp_cosh_minus_terms(gamma_t, delta):
|
||||
"""
|
||||
Compute E^(-tΓ) * (Cosh[tΓ] - 1 - (Cosh[tΓ√Δ] - 1)/Δ) / (tΓ(1 - Δ))
|
||||
|
||||
|
||||
Parameters:
|
||||
gamma_t: Γ*t term (could be a scalar or tensor)
|
||||
delta: Δ term (could be a scalar or tensor)
|
||||
|
||||
|
||||
Returns:
|
||||
Result of the computation with numerical stability handling
|
||||
"""
|
||||
@@ -151,17 +150,17 @@ def exp_cosh_minus_terms(gamma_t, delta):
|
||||
# Compute individual terms
|
||||
exp_cosh_term = exp_cosh(gamma_t, gamma_t**0) - exp_term # E^(-tΓ) (Cosh[tΓ] - 1) term
|
||||
exp_cosh_delta_term = - gamma_t**2 * exp_1mcosh_GD(gamma_t, delta) # E^(-tΓ) (Cosh[tΓ√Δ] - 1)/Δ term
|
||||
|
||||
|
||||
#exp_1mcosh_GD e^(-Γt) * (1 - cosh(Γt√Δ))/ ( (Γt)**2 Δ )
|
||||
# Main computation
|
||||
numerator = exp_cosh_term - exp_cosh_delta_term
|
||||
denominator = gamma_t * (1 - delta)
|
||||
|
||||
|
||||
result = numerator / denominator
|
||||
|
||||
|
||||
# Handle numerical instability
|
||||
result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
|
||||
|
||||
|
||||
# Taylor expansion for small gamma_t and delta near 1
|
||||
mask = (torch.abs(denominator) < 1e-1)
|
||||
exp_1mcosh_GD_term = exp_1mcosh_GD(gamma_t, delta**0)
|
||||
@@ -170,7 +169,7 @@ def exp_cosh_minus_terms(gamma_t, delta):
|
||||
- denominator / 4 * ( 0.5 * exp_cosh(gamma_t, delta**0) - 4 * exp_1mcosh_GD_term - 5 /2 * exp_sinh_GsqrtD(gamma_t, delta**0) )
|
||||
)
|
||||
result = torch.where(mask, taylor, result)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -185,7 +184,7 @@ def sig11(gamma_t, delta):
|
||||
def Zcoefs(gamma_t, delta):
|
||||
Zeta1 = zeta1(gamma_t, delta)
|
||||
Zeta2 = zeta2(gamma_t, delta)
|
||||
|
||||
|
||||
sq_total = 1 - Zeta1 + gamma_t * (delta - 1) * (Zeta1 - 1)**2 / 8
|
||||
amplitude = torch.sqrt(sq_total)
|
||||
Zcoef1 = ( gamma_t**0.5 * Zeta2 / 2 **0.5 ) / amplitude
|
||||
@@ -208,7 +207,7 @@ class StochasticHarmonicOscillator:
|
||||
dq(t) = -Γ A y(t) dt + Γ C dt + Γ D dw(t) - Γ q(t) dt
|
||||
|
||||
Also define v(t) = q(t) / √Γ, which is numerically more stable.
|
||||
|
||||
|
||||
Where:
|
||||
y(t) - Position variable
|
||||
q(t) - Velocity variable
|
||||
@@ -239,7 +238,7 @@ class StochasticHarmonicOscillator:
|
||||
Returns:
|
||||
tuple: (y(t), v(t))
|
||||
"""
|
||||
|
||||
|
||||
dummyzero = y0.new_zeros(1) # convert scalar to tensor with same device and dtype as y0
|
||||
Delta = self.Delta + dummyzero
|
||||
Gamma_hat = self.Gamma * t + dummyzero
|
||||
@@ -254,12 +253,12 @@ class StochasticHarmonicOscillator:
|
||||
if v0 is None:
|
||||
v0 = torch.randn_like(y0) * D / 2 ** 0.5
|
||||
#v0 = (C - A * y0)/Gamma**0.5
|
||||
|
||||
|
||||
# Calculate mean position and velocity
|
||||
term1 = (1 - zeta_1) * (C * t - A * t * y0) + zeta_2 * (Gamma ** 0.5) * v0 * t
|
||||
y_mean = term1 + y0
|
||||
v_mean = (1 - EE)*(C - A * y0) / (Gamma ** 0.5) + (EE - A * t * (1 - zeta_1)) * v0
|
||||
|
||||
|
||||
cov_yy = D**2 * t * self.sig22(Gamma_hat, Delta)
|
||||
cov_vv = D**2 * self.sig11(Gamma_hat, Delta) / 2
|
||||
cov_yv = (zeta2(Gamma_hat, Delta) * Gamma_hat * D ) **2 / 2 / (Gamma ** 0.5)
|
||||
@@ -274,7 +273,7 @@ class StochasticHarmonicOscillator:
|
||||
cov_matrix[..., 1, 1] = cov_vv
|
||||
|
||||
|
||||
|
||||
|
||||
# Compute the Cholesky decomposition to get scale_tril
|
||||
#scale_tril = torch.linalg.cholesky(cov_matrix)
|
||||
scale_tril = torch.zeros(*batch_shape, 2, 2, device=y0.device, dtype=y0.dtype)
|
||||
@@ -298,4 +297,4 @@ class StochasticHarmonicOscillator:
|
||||
scale_tril=scale_tril
|
||||
).sample()
|
||||
|
||||
return new_yv[...,0], new_yv[...,1]
|
||||
return new_yv[...,0], new_yv[...,1]
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
"""Per-frame video mask interpolation for the LanPaint video mask editor.
|
||||
|
||||
Keyframes are grayscale PNGs painted in the frontend at a capped resolution.
|
||||
This module loads them, morphs between them via signed-distance-field (SDF)
|
||||
level-set interpolation (at keyframe resolution), and upscales once to the
|
||||
frame size.
|
||||
|
||||
The interpolation contract is shared with the frontend preview
|
||||
(web/lanpaint_video_mask_editor.js): what the user sees is exactly what
|
||||
``interpolate_masks`` produces, pixel for pixel.
|
||||
|
||||
Each keyframe mask is binarized at 0.5 and turned into a signed distance
|
||||
field (positive inside, negative outside, 0 on the boundary). For a frame t
|
||||
between keyframes k_i and k_{i+1} with w = (t - k_i) / (k_{i+1} - k_i):
|
||||
|
||||
d(p) = (1 - w) * sdf_i(p) + w * sdf_{i+1}(p)
|
||||
mask(p) = sigmoid(d(p) / softness) softness = 1.0 pixel
|
||||
|
||||
The zero level set of the blended field slides linearly between the two
|
||||
shapes (translation, growth, shrink, merge/split), keeping edges sharp
|
||||
instead of cross-fading them. Frames that are exact keyframes return the
|
||||
original painted mask unchanged. Frames outside the keyframe window (before
|
||||
the first or after the last keyframe) have NO mask (all zeros): the mask
|
||||
exists only at keyframes and between them. Mask convention: 1 = regenerate,
|
||||
0 = keep.
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def load_keyframe_png(path: str, size: Tuple[int, int] | None = None) -> np.ndarray:
|
||||
"""Load a mask PNG as float32 [H, W] in [0, 1].
|
||||
|
||||
PNGs saved by the mask editor carry the mask in the alpha channel; a plain
|
||||
grayscale PNG (e.g. produced by other tools) is read via luminance.
|
||||
|
||||
``size`` is (width, height); the mask is resized with bilinear filtering
|
||||
(soft mask edges stay soft).
|
||||
"""
|
||||
with Image.open(path) as im:
|
||||
if "A" in im.getbands():
|
||||
im = im.getchannel("A")
|
||||
elif im.mode != "L":
|
||||
im = im.convert("L")
|
||||
if size is not None and im.size != tuple(size):
|
||||
im = im.resize(tuple(size), Image.BILINEAR)
|
||||
arr = np.asarray(im, dtype=np.float32) / 255.0
|
||||
return arr
|
||||
|
||||
|
||||
def resize_masks(masks: np.ndarray, size: Tuple[int, int]) -> np.ndarray:
|
||||
"""Resize a [T, H, W] mask sequence to ``size`` = (width, height)."""
|
||||
if tuple(masks.shape[1:][::-1]) == tuple(size):
|
||||
return masks
|
||||
frames = [Image.fromarray((m * 255).astype(np.uint8)) for m in masks]
|
||||
frames = [f.resize(tuple(size), Image.BILINEAR) for f in frames]
|
||||
out = np.stack([np.asarray(f, dtype=np.float32) / 255.0 for f in frames])
|
||||
return out
|
||||
|
||||
|
||||
#: sentinel for the EDT: larger than any possible squared distance
|
||||
_EDT_LARGE = None # set per-call from the mask shape
|
||||
|
||||
#: edge softness of the morph sigmoid, in pixels
|
||||
_MORPH_SOFTNESS = 1.0
|
||||
|
||||
try: # exact EDT at C speed; the pure-Python fallback is kept for CI/stubs
|
||||
from scipy.ndimage import distance_transform_edt as _scipy_edt
|
||||
except ImportError:
|
||||
_scipy_edt = None
|
||||
|
||||
|
||||
def _edt_1d_sq(f: np.ndarray) -> np.ndarray:
|
||||
"""Exact 1D Euclidean distance transform (squared distances).
|
||||
|
||||
Felzenszwalb-Huttenlocher lower-envelope of parabolas; O(n). ``f`` holds
|
||||
0.0 at foreground positions and a large sentinel elsewhere; the result is
|
||||
the squared distance to the nearest foreground element.
|
||||
"""
|
||||
n = f.shape[0]
|
||||
v = np.zeros(n, dtype=np.int64)
|
||||
z = np.zeros(n + 1, dtype=np.float64)
|
||||
z[0] = -np.inf
|
||||
z[1] = np.inf
|
||||
k = 0
|
||||
for q in range(1, n):
|
||||
q2 = float(q * q)
|
||||
while True:
|
||||
vk = v[k]
|
||||
s = ((f[q] + q2) - (f[vk] + float(vk * vk))) / (2.0 * (q - vk))
|
||||
if s > z[k]:
|
||||
break
|
||||
k -= 1
|
||||
k += 1
|
||||
v[k] = q
|
||||
z[k] = s
|
||||
z[k + 1] = np.inf
|
||||
k = 0
|
||||
d = np.empty(n, dtype=np.float64)
|
||||
for q in range(n):
|
||||
while z[k + 1] < q:
|
||||
k += 1
|
||||
vk = v[k]
|
||||
d[q] = f[vk] + float(q - vk) * float(q - vk)
|
||||
return d
|
||||
|
||||
|
||||
def _edt_2d(mask_binary: np.ndarray) -> np.ndarray:
|
||||
"""Exact 2D Euclidean distance transform of a bool mask (True = foreground).
|
||||
|
||||
Returns float64 [H, W] distances to the nearest foreground pixel. Uses
|
||||
scipy's C implementation when available (same exact distances, ~100x
|
||||
faster than the pure-Python Felzenszwalb-Huttenlocher fallback, which is
|
||||
kept for environments without scipy).
|
||||
"""
|
||||
if _scipy_edt is not None:
|
||||
# scipy measures distance to the nearest ZERO of the input, so the
|
||||
# mask is inverted to get the distance to the nearest foreground
|
||||
return _scipy_edt(~mask_binary)
|
||||
h, w = mask_binary.shape
|
||||
large = float(h * h + w * w) + 1.0 # > any possible squared distance
|
||||
f = np.where(mask_binary, 0.0, large).astype(np.float64)
|
||||
for y in range(h):
|
||||
f[y, :] = _edt_1d_sq(f[y, :])
|
||||
for x in range(w):
|
||||
f[:, x] = np.sqrt(np.maximum(_edt_1d_sq(f[:, x]), 0.0))
|
||||
return f
|
||||
|
||||
|
||||
def _signed_distance(mask_binary: np.ndarray) -> np.ndarray:
|
||||
"""Signed distance field of a bool mask; positive inside, negative outside.
|
||||
|
||||
Empty masks get a uniform -max(H, W)/2 field (so a shape morphs *in* from
|
||||
nothing); full masks get +max(H, W)/2 (so a shape morphs *out* to fill
|
||||
the frame).
|
||||
"""
|
||||
h, w = mask_binary.shape
|
||||
inside = mask_binary.all()
|
||||
outside = not mask_binary.any()
|
||||
if outside:
|
||||
return np.full((h, w), -max(h, w) / 2.0, dtype=np.float64)
|
||||
if inside:
|
||||
return np.full((h, w), max(h, w) / 2.0, dtype=np.float64)
|
||||
d_fg = _edt_2d(mask_binary) # distance to foreground (0 inside, >0 outside)
|
||||
d_bg = _edt_2d(~mask_binary) # distance to background (0 outside, >0 inside)
|
||||
return d_bg - d_fg
|
||||
|
||||
|
||||
def _sigmoid_stable(x: np.ndarray, softness: float) -> np.ndarray:
|
||||
"""Sigmoid with clipped input so exp cannot overflow."""
|
||||
return 1.0 / (1.0 + np.exp(-np.clip(x / softness, -50.0, 50.0)))
|
||||
|
||||
|
||||
def _shift(field: np.ndarray, dy: int, dx: int) -> np.ndarray:
|
||||
"""Shift a field by whole pixels; vacated pixels become 0."""
|
||||
h, w = field.shape
|
||||
out = np.zeros_like(field)
|
||||
src_y0, src_y1 = max(0, -dy), min(h, h - dy)
|
||||
dst_y0, dst_y1 = src_y0 + dy, src_y1 + dy
|
||||
src_x0, src_x1 = max(0, -dx), min(w, w - dx)
|
||||
dst_x0, dst_x1 = src_x0 + dx, src_x1 + dx
|
||||
if src_y1 > src_y0 and src_x1 > src_x0:
|
||||
out[dst_y0:dst_y1, dst_x0:dst_x1] = field[src_y0:src_y1, src_x0:src_x1]
|
||||
return out
|
||||
|
||||
|
||||
def _centroid(mask_binary: np.ndarray):
|
||||
"""Centroid (y, x) of a bool mask, or None when it is empty."""
|
||||
ys, xs = np.where(mask_binary)
|
||||
if len(xs) == 0:
|
||||
return None
|
||||
return (float(ys.mean()), float(xs.mean()))
|
||||
|
||||
|
||||
def interpolate_masks(
|
||||
keyframes: Dict[int, np.ndarray], count: int
|
||||
) -> np.ndarray:
|
||||
"""Morph a {frame_idx: [H, W] mask} dict to ``count`` frames (SDF level-set
|
||||
interpolation).
|
||||
|
||||
Returns float32 [count, H, W] in [0, 1]. All keyframe masks must share
|
||||
one shape. ``count`` <= 0 raises ValueError. Exact keyframe frames return
|
||||
the original painted mask; frames outside the keyframe window (before the
|
||||
first / after the last keyframe) are all zeros (no mask).
|
||||
"""
|
||||
if count <= 0:
|
||||
raise ValueError("count must be positive")
|
||||
if not keyframes:
|
||||
raise ValueError("at least one keyframe is required")
|
||||
|
||||
indices = sorted(keyframes)
|
||||
keys = {
|
||||
i: np.asarray(keyframes[i], dtype=np.float32) for i in indices
|
||||
}
|
||||
h, w = next(iter(keys.values())).shape
|
||||
out = np.zeros((count, h, w), dtype=np.float32)
|
||||
|
||||
# keyframes at or beyond `count` are out of range; if none is in range,
|
||||
# no frame is a keyframe or between keyframes -> the mask stays empty
|
||||
in_range = [i for i in indices if i < count]
|
||||
if not in_range:
|
||||
return out
|
||||
|
||||
# exact keyframe frames keep the original painted masks
|
||||
for i in in_range:
|
||||
out[i] = keys[i]
|
||||
|
||||
if len(indices) == 1:
|
||||
return out
|
||||
|
||||
sdfs = {i: _signed_distance(keys[i] >= 0.5) for i in indices}
|
||||
centroids = {i: _centroid(keys[i] >= 0.5) for i in indices}
|
||||
for lo, hi in zip(indices, indices[1:]):
|
||||
n_inner = hi - lo - 1
|
||||
if n_inner <= 0:
|
||||
continue
|
||||
# translation compensation: sample each field in the interpolated
|
||||
# frame so a translating shape slides instead of collapsing (plain
|
||||
# SDF blending vanishes when the translation exceeds the shape's
|
||||
# inscribed radius). Skip when either shape is empty (growth/shrink
|
||||
# morphs need no compensation).
|
||||
c_lo, c_hi = centroids[lo], centroids[hi]
|
||||
if c_lo is None or c_hi is None:
|
||||
dx, dy = 0.0, 0.0
|
||||
else:
|
||||
dx, dy = c_hi[1] - c_lo[1], c_hi[0] - c_lo[0]
|
||||
sdf_lo, sdf_hi = sdfs[lo], sdfs[hi]
|
||||
h, w = sdf_lo.shape
|
||||
out[lo + 1 : hi] = 0.0 # fill whole intermediate block per frame
|
||||
for t in range(lo + 1, hi):
|
||||
wf = (t - lo) / (hi - lo)
|
||||
# d(p) = (1-w)*sdf_lo(p - w*D) + w*sdf_hi(p + (1-w)*D)
|
||||
sx1 = int(np.floor(wf * dx + 0.5))
|
||||
sy1 = int(np.floor(wf * dy + 0.5))
|
||||
sx2 = int(np.floor((1.0 - wf) * dx + 0.5))
|
||||
sy2 = int(np.floor((1.0 - wf) * dy + 0.5))
|
||||
d = (1.0 - wf) * _shift(sdf_lo, sy1, sx1) + wf * _shift(sdf_hi, -sy2, -sx2)
|
||||
out[t] = _sigmoid_stable(d, _MORPH_SOFTNESS).astype(np.float32)
|
||||
return out
|
||||
|
||||
|
||||
def parse_keyframes_widget(value: str) -> Dict[int, str]:
|
||||
"""Parse the node's ``keyframes`` widget JSON into {frame_idx: filename}.
|
||||
|
||||
Malformed input yields an empty dict (treated as "no keyframes"). Keys are
|
||||
coerced to ints.
|
||||
"""
|
||||
if not value:
|
||||
return {}
|
||||
try:
|
||||
data = json.loads(value)
|
||||
except (TypeError, ValueError):
|
||||
return {}
|
||||
if not isinstance(data, dict):
|
||||
return {}
|
||||
out = {}
|
||||
for k, v in data.items():
|
||||
try:
|
||||
if isinstance(v, str):
|
||||
out[int(k)] = v
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return out
|
||||
@@ -0,0 +1,291 @@
|
||||
"""Read and write LanPaint mask metadata in MP4 files via PyAV.
|
||||
|
||||
The metadata tag ``lanpaint-mask`` holds a UTF-8 JSON payload:
|
||||
{
|
||||
"version": 1,
|
||||
"video": "<source video filename>",
|
||||
"fps": <number>,
|
||||
"keyframes": {"<frame_idx>": "<base64 PNG (no data: prefix)>", ...},
|
||||
"audio_intervals": [{"start": <float>, "end": <float>}, ...]
|
||||
}
|
||||
|
||||
Keyframes are base64-encoded PNG masks (RGBA, alpha = mask).
|
||||
"No mask" = tag absent (None); "empty mask" = tag present with ``{}`` keyframes.
|
||||
|
||||
PyAV is import-guarded: the module-level ``av`` is ``None`` when PyAV is
|
||||
unavailable, and the read/write functions raise ``RuntimeError`` in that case.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
try:
|
||||
import av
|
||||
|
||||
_HAS_AV = True
|
||||
except ImportError:
|
||||
av = None # type: ignore[assignment]
|
||||
_HAS_AV = False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Payload helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_METADATA_KEY = "lanpaint-mask"
|
||||
_PAYLOAD_VERSION = 1
|
||||
|
||||
|
||||
def encode_payload(payload: dict) -> str:
|
||||
"""Encode a payload dict as a compact JSON string (UTF-8)."""
|
||||
return json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
|
||||
def decode_payload(raw: Optional[str]) -> Optional[dict]:
|
||||
"""Decode a metadata value to a payload dict, or None on any error.
|
||||
|
||||
``raw`` may be ``None`` (tag absent), a JSON string, or something
|
||||
unexpected. Malformed / missing input returns ``None`` gracefully.
|
||||
"""
|
||||
if raw is None:
|
||||
return None
|
||||
if not isinstance(raw, str):
|
||||
return None
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
return data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Core read / write
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def read_mask_metadata(path: str) -> Optional[dict]:
|
||||
"""Read the ``lanpaint-mask`` metadata tag from an MP4 file.
|
||||
|
||||
Returns the decoded payload dict, or ``None`` when the tag is absent or
|
||||
PyAV is unavailable.
|
||||
"""
|
||||
if not _HAS_AV:
|
||||
raise RuntimeError(
|
||||
"PyAV (av) is required to read mask metadata but is not installed"
|
||||
)
|
||||
with av.open(path, "r") as container:
|
||||
raw = container.metadata.get(_METADATA_KEY, None)
|
||||
return decode_payload(raw)
|
||||
|
||||
|
||||
def write_mask_metadata(
|
||||
input_path: str,
|
||||
output_path: str,
|
||||
payload: dict,
|
||||
) -> None:
|
||||
"""Remux *input_path* to *output_path*, attaching a ``lanpaint-mask``
|
||||
metadata tag.
|
||||
|
||||
The video track is stream-copied (no re-encode) so the video content is
|
||||
preserved byte-for-byte. Audio and other tracks are also preserved.
|
||||
|
||||
The source file at *input_path* is **never** modified.
|
||||
|
||||
Raises ``RuntimeError`` when PyAV is unavailable.
|
||||
"""
|
||||
if not _HAS_AV:
|
||||
raise RuntimeError(
|
||||
"PyAV (av) is required to write mask metadata but is not installed"
|
||||
)
|
||||
|
||||
json_str = encode_payload(payload)
|
||||
|
||||
with av.open(input_path, "r") as in_container:
|
||||
# ``movflags=use_metadata_tags`` is MANDATORY — without it ffmpeg
|
||||
# silently drops custom metadata keys from the output.
|
||||
with av.open(
|
||||
output_path,
|
||||
"w",
|
||||
format="mp4",
|
||||
options={"movflags": "use_metadata_tags"},
|
||||
) as out_container:
|
||||
# Copy any existing metadata (except our own key) from the source.
|
||||
for key, value in in_container.metadata.items():
|
||||
if key != _METADATA_KEY:
|
||||
out_container.metadata[key] = value
|
||||
|
||||
# Write the LanPaint payload.
|
||||
out_container.metadata[_METADATA_KEY] = json_str
|
||||
|
||||
# Stream-copy every stream from the source.
|
||||
stream_map: Dict[int, Any] = {}
|
||||
for in_stream in in_container.streams:
|
||||
out_stream = out_container.add_stream_from_template(in_stream)
|
||||
stream_map[in_stream.index] = out_stream
|
||||
|
||||
# Demux → mux every packet.
|
||||
for packet in in_container.demux():
|
||||
if packet.dts is None:
|
||||
continue
|
||||
out_stream = stream_map[packet.stream.index]
|
||||
packet.stream = out_stream
|
||||
out_container.mux(packet)
|
||||
|
||||
# The caller is responsible for the output file on disk.
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers for server routes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _unique_output_path(input_dir: str, base_name: str) -> str:
|
||||
"""Return a non-clobbering output filename in *input_dir*.
|
||||
|
||||
Appends ``_masked``, then ``_masked_2``, ``_masked_3``, ... until a name
|
||||
that does not already exist is found.
|
||||
|
||||
Returns the bare filename (not the full path).
|
||||
"""
|
||||
stem, ext = os.path.splitext(base_name)
|
||||
candidate = f"{stem}_masked{ext}"
|
||||
if not os.path.exists(os.path.join(input_dir, candidate)):
|
||||
return candidate
|
||||
n = 2
|
||||
while True:
|
||||
candidate = f"{stem}_masked_{n}{ext}"
|
||||
if not os.path.exists(os.path.join(input_dir, candidate)):
|
||||
return candidate
|
||||
n += 1
|
||||
|
||||
|
||||
def export_mask_video_from_request(
|
||||
input_dir: str,
|
||||
filename: str,
|
||||
keyframes: dict,
|
||||
audio_intervals: list,
|
||||
fps: float,
|
||||
) -> str:
|
||||
"""Remux a source video with mask metadata, returning the new filename.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_dir:
|
||||
The ComfyUI input directory (``folder_paths.get_input_directory()``).
|
||||
filename:
|
||||
The source video filename (relative to *input_dir*).
|
||||
keyframes:
|
||||
Dict of ``{frame_idx: base64_png_string}``.
|
||||
audio_intervals:
|
||||
List of ``{"start": float, "end": float}`` dicts.
|
||||
fps:
|
||||
The video frame rate.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The bare filename of the exported MP4 (in *input_dir*).
|
||||
"""
|
||||
payload: Dict[str, Any] = {
|
||||
"version": _PAYLOAD_VERSION,
|
||||
"video": filename,
|
||||
"fps": fps,
|
||||
"keyframes": keyframes,
|
||||
"audio_intervals": audio_intervals,
|
||||
}
|
||||
|
||||
src = os.path.join(input_dir, filename)
|
||||
if not os.path.isfile(src):
|
||||
raise FileNotFoundError(f"source video not found: {src}")
|
||||
|
||||
out_name = _unique_output_path(input_dir, filename)
|
||||
out_path = os.path.join(input_dir, out_name)
|
||||
write_mask_metadata(src, out_path, payload)
|
||||
return out_name
|
||||
|
||||
|
||||
def register_routes(server) -> None:
|
||||
"""Register the LanPaint video-mask metadata routes on a ComfyUI
|
||||
PromptServer instance.
|
||||
|
||||
Call this from ``__init__.py`` when running inside ComfyUI (the ``server``
|
||||
module is only importable in that environment). Safe to call multiple
|
||||
times — routes are registered once.
|
||||
"""
|
||||
import aiohttp
|
||||
import folder_paths
|
||||
|
||||
@server.routes.get("/lanpaint/video_mask_meta")
|
||||
async def video_mask_meta(request: aiohttp.web.Request) -> aiohttp.web.Response:
|
||||
filename = request.query.get("filename", "")
|
||||
if not filename:
|
||||
return aiohttp.web.json_response({"found": False})
|
||||
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
path = os.path.join(input_dir, filename)
|
||||
if not os.path.isfile(path):
|
||||
return aiohttp.web.json_response({"found": False})
|
||||
|
||||
try:
|
||||
payload = read_mask_metadata(path)
|
||||
except Exception:
|
||||
payload = None
|
||||
|
||||
if payload is None:
|
||||
return aiohttp.web.json_response({"found": False})
|
||||
return aiohttp.web.json_response({"found": True, "payload": payload})
|
||||
|
||||
@server.routes.post("/lanpaint/export_mask_video")
|
||||
async def export_mask_video(request: aiohttp.web.Request) -> aiohttp.web.Response:
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
return aiohttp.web.json_response(
|
||||
{"error": "invalid JSON body"}, status=400
|
||||
)
|
||||
|
||||
filename = body.get("filename", "")
|
||||
if not filename:
|
||||
return aiohttp.web.json_response(
|
||||
{"error": "missing filename"}, status=400
|
||||
)
|
||||
|
||||
keyframes = body.get("keyframes", {})
|
||||
if not isinstance(keyframes, dict):
|
||||
return aiohttp.web.json_response(
|
||||
{"error": "keyframes must be a dict"}, status=400
|
||||
)
|
||||
|
||||
audio_intervals = body.get("audio_intervals", [])
|
||||
if not isinstance(audio_intervals, list):
|
||||
return aiohttp.web.json_response(
|
||||
{"error": "audio_intervals must be a list"}, status=400
|
||||
)
|
||||
|
||||
fps = body.get("fps", 30.0)
|
||||
try:
|
||||
fps = float(fps)
|
||||
except (TypeError, ValueError):
|
||||
return aiohttp.web.json_response(
|
||||
{"error": "fps must be a number"}, status=400
|
||||
)
|
||||
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
try:
|
||||
out_name = export_mask_video_from_request(
|
||||
input_dir, filename, keyframes, audio_intervals, fps
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
return aiohttp.web.json_response({"error": str(e)}, status=404)
|
||||
except RuntimeError as e:
|
||||
return aiohttp.web.json_response({"error": str(e)}, status=500)
|
||||
except Exception as e:
|
||||
return aiohttp.web.json_response(
|
||||
{"error": f"export failed: {e}"}, status=500
|
||||
)
|
||||
|
||||
return aiohttp.web.json_response(
|
||||
{"filename": out_name, "path": os.path.join(input_dir, out_name)}
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Runs the frontend mask-morph math (web/lanpaint_mask_math.js) on the same
|
||||
* keyframes the Python backend uses and prints the per-frame masks, so a
|
||||
* pytest can pin the two implementations together.
|
||||
*
|
||||
* Usage: node parity_mask_math.mjs <input.json>
|
||||
* input: {w, h, count, keyframes: {"<idx>": [alpha 0-255, ...]}}
|
||||
* output (stdout): {w, h, count, data: [float ...]} per-frame flat arrays
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const data = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
||||
|
||||
// the web dir has no package.json, so the module is loaded as ESM via a data: URL
|
||||
const src = readFileSync(new URL("../web/lanpaint_mask_math.js", import.meta.url), "utf8");
|
||||
const math = await import("data:text/javascript;base64," + Buffer.from(src).toString("base64"));
|
||||
|
||||
const { computeSDFEntry, MaskMorph } = math;
|
||||
const { w, h, count, keyframes } = data;
|
||||
const morph = new MaskMorph(w, h);
|
||||
const indices = Object.keys(keyframes).map(Number).sort((a, b) => a - b);
|
||||
|
||||
const out = new Float32Array(count * w * h);
|
||||
const sdfs = new Map();
|
||||
for (const idx of indices) {
|
||||
const alpha = new Uint8ClampedArray(keyframes[idx]);
|
||||
out.set(alpha.map((a) => a / 255), idx * w * h); // exact keyframes keep the paint
|
||||
sdfs.set(idx, computeSDFEntry(alpha, w, h));
|
||||
}
|
||||
for (let p = 0; p < indices.length - 1; p++) {
|
||||
const lo = indices[p];
|
||||
const hi = indices[p + 1];
|
||||
const sdfLo = sdfs.get(lo);
|
||||
const sdfHi = sdfs.get(hi);
|
||||
for (let t = lo + 1; t < hi; t++) {
|
||||
const wf = (t - lo) / (hi - lo);
|
||||
out.set(morph.frame(sdfLo, sdfHi, wf), t * w * h);
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify({ w, h, count, data: Array.from(out) }));
|
||||
@@ -1,4 +1,5 @@
|
||||
[pytest]
|
||||
testpaths = . # Run tests in the current directory
|
||||
python_files = test_*.py # Run tests in files that start with "test_"
|
||||
norecursedirs = .. # Don't run tests in the parent directory
|
||||
# Keep settings value-only; pytest does not treat inline `# ...` as comments.
|
||||
testpaths = .
|
||||
python_files = test_*.py
|
||||
norecursedirs = ..
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
"""Basic import tests for LanPaint.
|
||||
|
||||
"""Tests for `LanPaint` package."""
|
||||
The ComfyUI runtime dependencies (e.g. `comfy`) are intentionally optional for unit tests.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from src.LanPaint.nodes import Example
|
||||
|
||||
@pytest.fixture
|
||||
def example_node():
|
||||
"""Fixture to create an Example node instance."""
|
||||
return Example()
|
||||
def test_package_imports_without_comfy() -> None:
|
||||
import LanPaint
|
||||
|
||||
def test_example_node_initialization(example_node):
|
||||
"""Test that the node can be instantiated."""
|
||||
assert isinstance(example_node, Example)
|
||||
|
||||
def test_return_types():
|
||||
"""Test the node's metadata."""
|
||||
assert Example.RETURN_TYPES == ("IMAGE",)
|
||||
assert Example.FUNCTION == "test"
|
||||
assert Example.CATEGORY == "Example"
|
||||
assert isinstance(LanPaint.NODE_CLASS_MAPPINGS, dict)
|
||||
assert isinstance(LanPaint.NODE_DISPLAY_NAME_MAPPINGS, dict)
|
||||
assert "LanPaint_KSampler" in LanPaint.NODE_CLASS_MAPPINGS
|
||||
assert LanPaint.WEB_DIRECTORY == "./web"
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
import importlib
|
||||
import sys
|
||||
import types
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from src.LanPaint.lanpaint import LanPaint as LanPaintEngine
|
||||
|
||||
|
||||
def _repeat_to_batch_size(tensor: torch.Tensor, batch_size: int) -> torch.Tensor:
|
||||
if tensor.shape[0] == batch_size:
|
||||
return tensor
|
||||
if tensor.shape[0] == 1:
|
||||
return tensor.repeat((batch_size,) + (1,) * (tensor.ndim - 1))
|
||||
repeats = (batch_size + tensor.shape[0] - 1) // tensor.shape[0]
|
||||
return tensor.repeat((repeats,) + (1,) * (tensor.ndim - 1))[:batch_size]
|
||||
|
||||
|
||||
def _import_nodes(monkeypatch):
|
||||
comfy_mod = types.ModuleType("comfy")
|
||||
comfy_mod.__path__ = []
|
||||
|
||||
comfy_utils_mod = types.ModuleType("comfy.utils")
|
||||
comfy_utils_mod.repeat_to_batch_size = _repeat_to_batch_size
|
||||
|
||||
comfy_samplers_mod = types.ModuleType("comfy.samplers")
|
||||
comfy_samplers_mod.KSAMPLER = type("KSAMPLER", (), {})
|
||||
|
||||
comfy_model_base_mod = types.ModuleType("comfy.model_base")
|
||||
comfy_model_base_mod.ModelType = types.SimpleNamespace(FLUX="FLUX", FLOW="FLOW")
|
||||
comfy_model_base_mod.WAN22 = type("WAN22", (), {})
|
||||
|
||||
comfyui_version_mod = types.ModuleType("comfyui_version")
|
||||
comfyui_version_mod.__version__ = "0.6.0"
|
||||
|
||||
comfy_mod.utils = comfy_utils_mod
|
||||
comfy_mod.samplers = comfy_samplers_mod
|
||||
comfy_mod.model_base = comfy_model_base_mod
|
||||
|
||||
monkeypatch.setitem(sys.modules, "comfy", comfy_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.utils", comfy_utils_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.samplers", comfy_samplers_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.model_base", comfy_model_base_mod)
|
||||
monkeypatch.setitem(sys.modules, "nodes", types.ModuleType("nodes"))
|
||||
monkeypatch.setitem(sys.modules, "latent_preview", types.ModuleType("latent_preview"))
|
||||
monkeypatch.setitem(sys.modules, "comfyui_version", comfyui_version_mod)
|
||||
|
||||
sys.modules.pop("src.LanPaint.nodes", None)
|
||||
return importlib.import_module("src.LanPaint.nodes")
|
||||
|
||||
|
||||
class _FakeDiffusionModel:
|
||||
sigma_shift_video = 12.0
|
||||
sigma_shift_audio = 3.0
|
||||
|
||||
|
||||
class _FakeModelPatcher:
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
|
||||
def _two_stream_shapes():
|
||||
return [(1, 24, 37, 30, 54), (1, 32, 2, 207)]
|
||||
|
||||
|
||||
def _minimax_model():
|
||||
model = type("FakeMiniMaxH3", (), {})()
|
||||
model.diffusion_model = _FakeDiffusionModel()
|
||||
return model
|
||||
|
||||
|
||||
# --- detection: only MiniMax H3 AV packs get the audio schedule -----------------
|
||||
|
||||
def test_detect_returns_none_for_single_stream_latent(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
patcher = _FakeModelPatcher(_minimax_model())
|
||||
assert nodes._detect_minimax_h3_audio(patcher, {}, [(1, 24, 37, 30, 54)]) is None
|
||||
assert nodes._detect_minimax_h3_audio(patcher, {}, None) is None
|
||||
|
||||
|
||||
def test_detect_returns_none_for_model_without_audio_schedule(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
patcher = _FakeModelPatcher(object()) # no diffusion_model / no shift attrs
|
||||
assert nodes._detect_minimax_h3_audio(patcher, {}, _two_stream_shapes()) is None
|
||||
|
||||
|
||||
def test_detect_returns_layout_for_minimax_av_pack(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
patcher = _FakeModelPatcher(_minimax_model())
|
||||
layout = nodes._detect_minimax_h3_audio(patcher, {}, _two_stream_shapes())
|
||||
assert layout == (_two_stream_shapes(), 12.0, 3.0)
|
||||
|
||||
|
||||
def test_detect_honors_sigma_shift_node_overrides(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
patcher = _FakeModelPatcher(_minimax_model())
|
||||
options = {"transformer_options": {"minimax_h3_sigma_shift_video": 10.0, "minimax_h3_sigma_shift_audio": 2.5}}
|
||||
layout = nodes._detect_minimax_h3_audio(patcher, options, _two_stream_shapes())
|
||||
assert layout == (_two_stream_shapes(), 10.0, 2.5)
|
||||
|
||||
|
||||
def test_guarded_imports_fall_back_in_stub_env(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
assert nodes.time_shift_sigma is None # comfy.ldm.minimax not importable here
|
||||
|
||||
|
||||
# --- per-stream schedule blending in the paint loop -----------------------------
|
||||
|
||||
class _DummySampling:
|
||||
"""Emulates the flow model_sampling: CONST.noise_scaling is
|
||||
sigma * (s * noise) + (1 - sigma) * latent and reshape_sigma requires a
|
||||
scalar sigma."""
|
||||
|
||||
noise_scale = 1.0
|
||||
|
||||
def noise_scaling(self, sigma, noise, latent_image, max_denoise=False): # type: ignore[no-untyped-def]
|
||||
assert sigma.numel() == 1, "noise_scaling requires a scalar sigma"
|
||||
return sigma * (self.noise_scale * noise) + (1.0 - sigma) * latent_image
|
||||
|
||||
|
||||
class _DummyModel:
|
||||
def __init__(self) -> None:
|
||||
self.inner_model = self
|
||||
self.model_sampling = _DummySampling()
|
||||
self.last_input = None
|
||||
|
||||
def __call__(self, x, sigma, model_options=None, seed=None): # type: ignore[no-untyped-def]
|
||||
self.last_input = x
|
||||
return x, x
|
||||
|
||||
|
||||
def _engine(n_steps: int = 1): # type: ignore[no-untyped-def]
|
||||
return LanPaintEngine(
|
||||
_DummyModel(),
|
||||
NSteps=n_steps,
|
||||
Friction=15.0,
|
||||
Lambda=1.0,
|
||||
Beta=1.0,
|
||||
StepSize=0.2,
|
||||
)
|
||||
|
||||
|
||||
def _flat_pack_inputs(): # type: ignore[no-untyped-def]
|
||||
x = torch.zeros(1, 1, 8) # flat pack: 5 video rows + 3 audio rows
|
||||
latent_image = torch.zeros_like(x)
|
||||
noise = torch.ones_like(x)
|
||||
sigma = torch.tensor([0.5])
|
||||
latent_mask = torch.zeros_like(x) # 1 = keep -> regenerate everywhere by default
|
||||
current_times = (torch.tensor([1.0]), torch.tensor([0.5]), torch.tensor([0.5])) # video: VE, abt, flow
|
||||
current_times_audio = (torch.tensor([0.25]), torch.tensor([0.9]), torch.tensor([0.2])) # audio: VE, abt, flow
|
||||
audio_indicator = torch.zeros(1, 1, 8)
|
||||
audio_indicator[..., 5:] = 1.0
|
||||
return x, latent_image, noise, sigma, latent_mask, current_times, current_times_audio, audio_indicator
|
||||
|
||||
|
||||
def test_audio_rows_get_audio_schedule_parameters() -> None:
|
||||
engine = _engine()
|
||||
captured = {}
|
||||
|
||||
def fake_prepare_step_size(current_times, step_size, sigma_x, sigma_y): # type: ignore[no-untyped-def]
|
||||
captured["abt"] = current_times[1]
|
||||
captured["step_size"] = step_size
|
||||
abt = current_times[1]
|
||||
ones = torch.ones_like(abt)
|
||||
return (current_times[0], abt, ones, ones, ones, ones, torch.zeros_like(abt), torch.zeros_like(abt), torch.zeros_like(abt), torch.zeros_like(abt))
|
||||
|
||||
engine.prepare_step_size = fake_prepare_step_size # type: ignore[method-assign]
|
||||
|
||||
x, latent_image, noise, sigma, latent_mask, current_times, current_times_audio, audio_indicator = _flat_pack_inputs()
|
||||
engine(x, latent_image, noise, sigma, latent_mask, current_times,
|
||||
model_options=None, seed=0, n_steps=1,
|
||||
current_times_audio=current_times_audio, audio_indicator=audio_indicator)
|
||||
|
||||
abt = captured["abt"].flatten()
|
||||
assert abt[0] == 0.5 # video rows keep the video schedule
|
||||
assert abt[-1] == 0.9 # audio rows run on the audio schedule
|
||||
step = captured["step_size"].flatten()
|
||||
assert step[0] == pytest.approx(0.2 * (1 - 0.5)) # video step size
|
||||
assert step[-1] == pytest.approx(0.2 * (1 - 0.9)) # audio step size
|
||||
|
||||
|
||||
def test_without_audio_context_schedule_stays_uniform() -> None:
|
||||
engine = _engine()
|
||||
captured = {}
|
||||
|
||||
def fake_prepare_step_size(current_times, step_size, sigma_x, sigma_y): # type: ignore[no-untyped-def]
|
||||
captured["abt"] = current_times[1]
|
||||
abt = current_times[1]
|
||||
ones = torch.ones_like(abt)
|
||||
return (current_times[0], abt, ones, ones, ones, ones, torch.zeros_like(abt), torch.zeros_like(abt), torch.zeros_like(abt), torch.zeros_like(abt))
|
||||
|
||||
engine.prepare_step_size = fake_prepare_step_size # type: ignore[method-assign]
|
||||
|
||||
x, latent_image, noise, sigma, latent_mask, current_times, _, _ = _flat_pack_inputs()
|
||||
engine(x, latent_image, noise, sigma, latent_mask, current_times,
|
||||
model_options=None, seed=0, n_steps=1)
|
||||
|
||||
abt = captured["abt"].flatten()
|
||||
assert abt[0] == 0.5
|
||||
assert abt[-1] == 0.5 # uniform: the video schedule everywhere
|
||||
|
||||
|
||||
def test_replace_step_uses_audio_sigma_for_audio_rows() -> None:
|
||||
# n_steps=0 with keep-everywhere mask: the only transform applied is the
|
||||
# replace step, and the final model call sees its result (dummy returns x).
|
||||
engine = _engine(n_steps=0)
|
||||
x, latent_image, noise, sigma, _, current_times, current_times_audio, audio_indicator = _flat_pack_inputs()
|
||||
latent_mask = torch.ones_like(x) # keep everywhere -> replace step applies
|
||||
|
||||
engine(x, latent_image, noise, sigma, latent_mask, current_times,
|
||||
model_options=None, seed=0, n_steps=0,
|
||||
current_times_audio=current_times_audio, audio_indicator=audio_indicator)
|
||||
|
||||
inp = engine.inner_model.last_input.flatten()
|
||||
# replace step = sigma_eff * (s * noise) + (1 - sigma_eff) * latent, with
|
||||
# noise = 1 and latent = 0 -> the effective flow sigma itself
|
||||
assert inp[0] == pytest.approx(0.5) # video rows: sigma_v
|
||||
assert inp[-1] == pytest.approx(0.2) # audio rows: sigma_audio
|
||||
|
||||
|
||||
class _OffsetModel:
|
||||
"""Returns x + offset for both heads, so the flat-grid target deviation is known."""
|
||||
|
||||
def __init__(self, offset): # type: ignore[no-untyped-def]
|
||||
self.inner_model = self
|
||||
self.model_sampling = _DummySampling()
|
||||
self.offset = offset
|
||||
|
||||
def __call__(self, x, sigma, model_options=None, seed=None): # type: ignore[no-untyped-def]
|
||||
return x + self.offset, x + self.offset
|
||||
|
||||
|
||||
def _score_engine(offset: float = 2.0): # type: ignore[no-untyped-def]
|
||||
engine = LanPaintEngine(
|
||||
_OffsetModel(torch.tensor(offset)),
|
||||
NSteps=1, Friction=15.0, Lambda=1.0, Beta=1.0, StepSize=0.2,
|
||||
IS_FLOW=True,
|
||||
)
|
||||
engine.img_dim_size = 3
|
||||
return engine
|
||||
|
||||
|
||||
def test_score_model_corrects_audio_target_only() -> None:
|
||||
engine = _score_engine()
|
||||
ai = torch.zeros(1, 1, 8)
|
||||
ai[..., 5:] = 1.0
|
||||
engine.audio_indicator = ai
|
||||
engine.audio_correction = (1.0 - ai) + 0.625 * ai # c = sigma_a/(sigma_v*slope_a) at sigma_v=0.5
|
||||
|
||||
x_t = torch.zeros(1, 1, 8)
|
||||
y = torch.zeros(1, 1, 8)
|
||||
mask = torch.zeros(1, 1, 8) # regenerate everywhere -> score_x branch
|
||||
abt = torch.full((1, 1, 8), 0.5)
|
||||
sigma = torch.ones(1, 1, 8)
|
||||
tflow = engine.add_none_dims(torch.tensor([0.5]))
|
||||
|
||||
score = engine.score_model(x_t, y, mask, abt, sigma, tflow, model_options=None, seed=0)
|
||||
s = score.flatten()
|
||||
# x = 0, x0_flat = 2 -> corrected x0 = 2*corr -> score = 2*corr
|
||||
assert s[0] == pytest.approx(2.0) # video rows: corr = 1 -> untouched
|
||||
assert s[-1] == pytest.approx(1.25) # audio rows: 2 * 0.625
|
||||
|
||||
|
||||
def test_score_model_without_audio_context_uses_flat_target() -> None:
|
||||
engine = _score_engine()
|
||||
x_t = torch.zeros(1, 1, 8)
|
||||
y = torch.zeros(1, 1, 8)
|
||||
mask = torch.zeros(1, 1, 8)
|
||||
abt = torch.full((1, 1, 8), 0.5)
|
||||
sigma = torch.ones(1, 1, 8)
|
||||
tflow = engine.add_none_dims(torch.tensor([0.5]))
|
||||
|
||||
score = engine.score_model(x_t, y, mask, abt, sigma, tflow, model_options=None, seed=0)
|
||||
s = score.flatten()
|
||||
assert s[0] == pytest.approx(2.0)
|
||||
assert s[-1] == pytest.approx(2.0) # uniform flat target: no correction
|
||||
|
||||
|
||||
# --- add_none_dims equivalence ------------------------------------------------
|
||||
|
||||
def test_add_none_dims_equivalent_for_scalar_inputs() -> None:
|
||||
engine = _engine()
|
||||
for img_dim_size in (3, 4, 5):
|
||||
engine.img_dim_size = img_dim_size
|
||||
t = torch.zeros(1)
|
||||
old = t[(slice(None),) + (None,) * (img_dim_size - 1)]
|
||||
new = engine.add_none_dims(t.clone())
|
||||
assert tuple(new.shape) == tuple(old.shape)
|
||||
# the while-loop additionally handles 0-dim inputs, which the
|
||||
# tuple-index form rejects in modern torch
|
||||
assert tuple(engine.add_none_dims(torch.zeros(())).shape) == (1,) * img_dim_size
|
||||
|
||||
|
||||
def test_add_none_dims_passes_per_row_tensors_through() -> None:
|
||||
engine = _engine()
|
||||
engine.img_dim_size = 3
|
||||
t = torch.zeros(1, 1, 8)
|
||||
assert tuple(engine.add_none_dims(t).shape) == (1, 1, 8)
|
||||
|
||||
|
||||
# --- prepare_step_size with per-row parameters --------------------------------
|
||||
|
||||
def test_prepare_step_size_handles_per_row_parameters() -> None:
|
||||
engine = _engine()
|
||||
engine.img_dim_size = 3
|
||||
sigma = torch.ones(1, 1, 8)
|
||||
abt = torch.full((1, 1, 8), 0.5)
|
||||
abt[..., 5:] = 0.9
|
||||
step_size = torch.full((1, 1, 8), 0.1)
|
||||
step_size[..., 5:] = 0.02
|
||||
sigma_x = torch.ones(1, 1, 8)
|
||||
sigma_y = torch.ones(1, 1, 8)
|
||||
|
||||
out = engine.prepare_step_size((sigma, abt, sigma), step_size, sigma_x, sigma_y)
|
||||
for t in out:
|
||||
assert t.ndim == 3 # everything stays per-row broadcastable
|
||||
|
||||
_, abt_out, dtx, dty, Gamma_x, Gamma_y, A_x, A_y, D_x, D_y = out
|
||||
# the A * dt cancellation: A_x = 1/(1-abt), dt = dtx/2 = step_size * sigma_x
|
||||
# -> A_x * dt = step_size / (1-abt) * (1-abt) * ... bounded per row
|
||||
adt = (A_x * dtx).flatten()
|
||||
assert adt[0] == pytest.approx(0.2) # 1/(1-0.5) * 0.1
|
||||
assert adt[-1] == pytest.approx(0.2) # 1/(1-0.9) * 0.02 -- bounded invariant
|
||||
@@ -0,0 +1,104 @@
|
||||
import torch
|
||||
|
||||
from src.LanPaint.lanpaint import LanPaint as LanPaintEngine
|
||||
|
||||
|
||||
class _DummySampling:
|
||||
def noise_scaling(self, sigma, noise, latent_image): # type: ignore[no-untyped-def]
|
||||
return latent_image + noise * sigma
|
||||
|
||||
|
||||
class _DummyModel:
|
||||
def __init__(self) -> None:
|
||||
self.inner_model = self
|
||||
self.model_sampling = _DummySampling()
|
||||
|
||||
def __call__(self, x, sigma, model_options=None, seed=None): # type: ignore[no-untyped-def]
|
||||
return x, x
|
||||
|
||||
|
||||
def _inputs(): # type: ignore[no-untyped-def]
|
||||
x = torch.zeros((1, 4, 8, 8))
|
||||
latent_image = torch.zeros_like(x)
|
||||
noise = torch.ones_like(x)
|
||||
sigma = torch.tensor([1.0])
|
||||
|
||||
latent_mask = torch.zeros_like(x)
|
||||
current_times = (sigma, torch.tensor([0.5]), torch.tensor([0.0]))
|
||||
return x, latent_image, noise, sigma, latent_mask, current_times
|
||||
|
||||
|
||||
def test_default_semantic_stop_triggers_at_patience_without_custom_distance_fn() -> None:
|
||||
engine = LanPaintEngine(
|
||||
_DummyModel(),
|
||||
NSteps=10,
|
||||
Friction=15.0,
|
||||
Lambda=1.0,
|
||||
Beta=1.0,
|
||||
StepSize=0.2,
|
||||
)
|
||||
|
||||
calls = {"langevin": 0, "with_score": 0, "without_score": 0}
|
||||
|
||||
def fake_langevin(x_t, score, mask, step_size, current_times, sigma_x=1, sigma_y=0, args=None): # type: ignore[no-untyped-def]
|
||||
calls["langevin"] += 1
|
||||
if score is None:
|
||||
calls["without_score"] += 1
|
||||
else:
|
||||
calls["with_score"] += 1
|
||||
return x_t, args
|
||||
|
||||
engine.langevin_dynamics = fake_langevin # type: ignore[method-assign]
|
||||
|
||||
model_options = {
|
||||
"lanpaint_semantic_stop": {
|
||||
"threshold": 1e-6,
|
||||
"patience": 2,
|
||||
}
|
||||
}
|
||||
|
||||
x, latent_image, noise, sigma, latent_mask, current_times = _inputs()
|
||||
engine(x, latent_image, noise, sigma, latent_mask, current_times, model_options=model_options, seed=0, n_steps=10)
|
||||
|
||||
assert calls["langevin"] == 3
|
||||
assert calls["with_score"] == 3
|
||||
assert calls["without_score"] == 0
|
||||
|
||||
|
||||
def test_semantic_stop_is_disabled_when_no_inpaint_region() -> None:
|
||||
engine = LanPaintEngine(
|
||||
_DummyModel(),
|
||||
NSteps=10,
|
||||
Friction=15.0,
|
||||
Lambda=1.0,
|
||||
Beta=1.0,
|
||||
StepSize=0.2,
|
||||
)
|
||||
|
||||
calls = {"langevin": 0, "with_score": 0, "without_score": 0}
|
||||
|
||||
def fake_langevin(x_t, score, mask, step_size, current_times, sigma_x=1, sigma_y=0, args=None): # type: ignore[no-untyped-def]
|
||||
calls["langevin"] += 1
|
||||
if score is None:
|
||||
calls["without_score"] += 1
|
||||
else:
|
||||
calls["with_score"] += 1
|
||||
return x_t, args
|
||||
|
||||
engine.langevin_dynamics = fake_langevin # type: ignore[method-assign]
|
||||
|
||||
model_options = {
|
||||
"lanpaint_semantic_stop": {
|
||||
"threshold": 1e-6,
|
||||
"patience": 1,
|
||||
}
|
||||
}
|
||||
|
||||
x, latent_image, noise, sigma, latent_mask, _ = _inputs()
|
||||
current_times = (sigma, torch.tensor([0.5]), torch.tensor([0.0]))
|
||||
no_inpaint_mask = torch.ones_like(latent_mask)
|
||||
engine(x, latent_image, noise, sigma, no_inpaint_mask, current_times, model_options=model_options, seed=0, n_steps=10)
|
||||
|
||||
assert calls["langevin"] == 10
|
||||
assert calls["with_score"] == 10
|
||||
assert calls["without_score"] == 0
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Tests for the MinStepFrac tail ramp (inner-step count reduction).
|
||||
|
||||
The ramp makes sure only one knob changes at a time: while the remaining
|
||||
noise fraction (1 - abt) is above the threshold the step size scales inside
|
||||
LanPaint and the count is constant; below it the step size is pinned and
|
||||
the count ramps down linearly.
|
||||
"""
|
||||
|
||||
|
||||
def _import_nodes():
|
||||
import LanPaint.src.LanPaint.nodes as nodes # type: ignore[attr-defined]
|
||||
|
||||
return nodes
|
||||
|
||||
|
||||
def test_disabled_returns_full_count() -> None:
|
||||
nodes = _import_nodes()
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.1, 0.0) == 5 # min_frac 0 = off
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.01, 0.0) == 5
|
||||
|
||||
|
||||
def test_above_fraction_returns_full_count() -> None:
|
||||
nodes = _import_nodes()
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.2, 0.05) == 5
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.05, 0.05) == 5 # boundary kept
|
||||
|
||||
|
||||
def test_below_fraction_ramps_linear() -> None:
|
||||
nodes = _import_nodes()
|
||||
# frac / min_frac: 0.04/0.05 = 0.8 -> 5*0.8 = 4
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.04, 0.05) == 4
|
||||
# 0.025/0.05 = 0.5 -> 2.5 -> round -> 2
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.025, 0.05) == 2
|
||||
# 0.005/0.05 = 0.1 -> 0.5 -> round(0.5) = 0 (banker's), max(0, ...) anyway
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.005, 0.05) == 0
|
||||
assert nodes.min_step_frac_effective_steps(5, 0.0, 0.05) == 0
|
||||
|
||||
|
||||
def test_zero_steps_stays_zero() -> None:
|
||||
nodes = _import_nodes()
|
||||
assert nodes.min_step_frac_effective_steps(0, 0.01, 0.05) == 0
|
||||
@@ -0,0 +1,118 @@
|
||||
import importlib
|
||||
import sys
|
||||
import types
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
|
||||
def _repeat_to_batch_size(tensor: torch.Tensor, batch_size: int) -> torch.Tensor:
|
||||
if tensor.shape[0] == batch_size:
|
||||
return tensor
|
||||
if tensor.shape[0] == 1:
|
||||
return tensor.repeat((batch_size,) + (1,) * (tensor.ndim - 1))
|
||||
repeats = (batch_size + tensor.shape[0] - 1) // tensor.shape[0]
|
||||
return tensor.repeat((repeats,) + (1,) * (tensor.ndim - 1))[:batch_size]
|
||||
|
||||
|
||||
def _import_nodes(monkeypatch):
|
||||
comfy_mod = types.ModuleType("comfy")
|
||||
comfy_mod.__path__ = []
|
||||
|
||||
comfy_utils_mod = types.ModuleType("comfy.utils")
|
||||
comfy_utils_mod.repeat_to_batch_size = _repeat_to_batch_size
|
||||
|
||||
comfy_samplers_mod = types.ModuleType("comfy.samplers")
|
||||
comfy_samplers_mod.KSAMPLER = type("KSAMPLER", (), {})
|
||||
|
||||
comfy_model_base_mod = types.ModuleType("comfy.model_base")
|
||||
comfy_model_base_mod.ModelType = types.SimpleNamespace(FLUX="FLUX", FLOW="FLOW")
|
||||
comfy_model_base_mod.WAN22 = type("WAN22", (), {})
|
||||
|
||||
comfyui_version_mod = types.ModuleType("comfyui_version")
|
||||
comfyui_version_mod.__version__ = "0.6.0"
|
||||
|
||||
comfy_mod.utils = comfy_utils_mod
|
||||
comfy_mod.samplers = comfy_samplers_mod
|
||||
comfy_mod.model_base = comfy_model_base_mod
|
||||
|
||||
monkeypatch.setitem(sys.modules, "comfy", comfy_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.utils", comfy_utils_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.samplers", comfy_samplers_mod)
|
||||
monkeypatch.setitem(sys.modules, "comfy.model_base", comfy_model_base_mod)
|
||||
monkeypatch.setitem(sys.modules, "nodes", types.ModuleType("nodes"))
|
||||
monkeypatch.setitem(sys.modules, "latent_preview", types.ModuleType("latent_preview"))
|
||||
monkeypatch.setitem(sys.modules, "comfyui_version", comfyui_version_mod)
|
||||
|
||||
sys.modules.pop("src.LanPaint.nodes", None)
|
||||
return importlib.import_module("src.LanPaint.nodes")
|
||||
|
||||
|
||||
class StubAudioVAE:
|
||||
"""Emulates the sd.VAE wrapper: channels-last audio in/out, converts to
|
||||
[B, C, L] for the raw VAE internally (as sd.py:1040/1216 do)."""
|
||||
|
||||
audio_sample_rate = 32000
|
||||
|
||||
def __init__(self):
|
||||
self.last_input = None
|
||||
|
||||
def encode(self, waveform):
|
||||
self.last_input = waveform
|
||||
w = waveform.movedim(-1, 1) # [B, L, C] -> [B, C, L]
|
||||
t = max(1, w.shape[-1] // 800)
|
||||
return torch.zeros(w.shape[0], 32, 2, t)
|
||||
|
||||
def decode(self, z):
|
||||
return torch.zeros(z.shape[0], z.shape[-1] * 800, 2) # [B, L, C]
|
||||
|
||||
|
||||
def _audio(samples: int, channels: int = 2, sample_rate: int = 32000):
|
||||
return {"waveform": torch.zeros(1, channels, samples), "sample_rate": sample_rate}
|
||||
|
||||
|
||||
def test_encode_produces_correct_latent_and_no_mask(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
vae = StubAudioVAE()
|
||||
out = nodes.LanPaint_MiniMaxAudioEncode().encode(_audio(320000), vae)
|
||||
latent = out[0]
|
||||
assert latent["samples"].shape == (1, 32, 2, 400)
|
||||
# pure encoder: masks come from the video mask editor via SetLatentNoiseMask
|
||||
assert "noise_mask" not in latent
|
||||
|
||||
|
||||
def test_encode_resamples_and_upmixes_mono(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
if nodes.torchaudio is None:
|
||||
pytest.skip("torchaudio not available")
|
||||
vae = StubAudioVAE()
|
||||
mono_44k = _audio(320000, channels=1, sample_rate=44100)
|
||||
nodes.LanPaint_MiniMaxAudioEncode().encode(mono_44k, vae)
|
||||
# wrapper receives channels-last stereo at its own sample rate
|
||||
assert vae.last_input.shape[-1] == 2 # mono upmixed to stereo (channels last)
|
||||
assert 200000 < vae.last_input.shape[1] < 320000 # resampled 44.1k -> 32k shortens
|
||||
|
||||
|
||||
def test_decode_returns_waveform_and_sample_rate(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
vae = StubAudioVAE()
|
||||
audio = nodes.LanPaint_MiniMaxAudioDecode().decode({"samples": torch.zeros(1, 32, 2, 400)}, vae)
|
||||
assert audio[0]["waveform"].shape == (1, 2, 320000)
|
||||
assert audio[0]["sample_rate"] == 32000
|
||||
|
||||
|
||||
def test_decode_handles_nested_av_latent(monkeypatch) -> None:
|
||||
nodes = _import_nodes(monkeypatch)
|
||||
vae = StubAudioVAE()
|
||||
|
||||
class FakeNested:
|
||||
is_nested = True
|
||||
|
||||
def __init__(self, tensors):
|
||||
self.tensors = tensors
|
||||
|
||||
def unbind(self):
|
||||
return self.tensors
|
||||
|
||||
audio = nodes.LanPaint_MiniMaxAudioDecode().decode({"samples": FakeNested([torch.zeros(1, 24, 4, 8, 8), torch.zeros(1, 32, 2, 400)])}, vae)
|
||||
assert audio[0]["waveform"].shape == (1, 2, 320000)
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Tests for the retired LanPaint hyperparameters and the value sanitizer.
|
||||
|
||||
Beta/Friction/EarlyStop/InnerThreshold/InnerPatience/MinStepFrac were
|
||||
removed from the sampler node widgets; old prompts still pass them, so the
|
||||
nodes accept-and-ignore them via hidden inputs. Invalid widget values fall
|
||||
back to defaults instead of crashing the node.
|
||||
"""
|
||||
|
||||
RETIRED = [
|
||||
"LanPaint_Beta",
|
||||
"LanPaint_Friction",
|
||||
"LanPaint_EarlyStop",
|
||||
"LanPaint_InnerThreshold",
|
||||
"LanPaint_InnerPatience",
|
||||
"LanPaint_MinStepFrac",
|
||||
]
|
||||
|
||||
|
||||
def _import_nodes():
|
||||
import LanPaint.src.LanPaint.nodes as nodes # type: ignore[attr-defined]
|
||||
|
||||
return nodes
|
||||
|
||||
|
||||
def _required(node_cls):
|
||||
return node_cls.INPUT_TYPES().get("required", {})
|
||||
|
||||
|
||||
def _hidden(node_cls):
|
||||
return node_cls.INPUT_TYPES().get("hidden", {})
|
||||
|
||||
|
||||
def test_retired_params_removed_from_widgets() -> None:
|
||||
nodes = _import_nodes()
|
||||
for cls in (
|
||||
nodes.LanPaint_KSampler,
|
||||
nodes.LanPaint_KSamplerAdvanced,
|
||||
nodes.LanPaint_SamplerCustom,
|
||||
nodes.LanPaint_SamplerCustomAdvanced,
|
||||
):
|
||||
req = _required(cls)
|
||||
for name in RETIRED:
|
||||
assert name not in req, f"{cls.__name__} still exposes {name}"
|
||||
|
||||
|
||||
def test_retired_params_kept_as_hidden_inputs() -> None:
|
||||
nodes = _import_nodes()
|
||||
# The two advanced nodes exposed all six params; the basic KSampler
|
||||
# only ever had MinStepFrac. Old prompts must still validate.
|
||||
assert set(_hidden(nodes.LanPaint_KSamplerAdvanced)) >= set(RETIRED)
|
||||
assert set(_hidden(nodes.LanPaint_SamplerCustomAdvanced)) >= set(RETIRED)
|
||||
assert "LanPaint_MinStepFrac" in _hidden(nodes.LanPaint_KSampler)
|
||||
|
||||
|
||||
def test_kept_widgets_still_present() -> None:
|
||||
nodes = _import_nodes()
|
||||
req = _required(nodes.LanPaint_KSamplerAdvanced)
|
||||
for name in (
|
||||
"LanPaint_NumSteps",
|
||||
"LanPaint_Lambda",
|
||||
"LanPaint_StepSize",
|
||||
"LanPaint_PromptMode",
|
||||
"LanPaint_Info",
|
||||
"Inpainting_mode",
|
||||
):
|
||||
assert name in req
|
||||
|
||||
|
||||
def test_sanitize_param_combos() -> None:
|
||||
nodes = _import_nodes()
|
||||
sanitize = nodes._sanitize_param
|
||||
allowed = ("Image First", "Prompt First")
|
||||
assert sanitize("Image First", "Image First", allowed=allowed) == "Image First"
|
||||
assert sanitize("Prompt First", "Image First", allowed=allowed) == "Prompt First"
|
||||
assert sanitize(1.0, "Image First", allowed=allowed) == "Image First" # retired float
|
||||
assert sanitize("bogus", "Image First", allowed=allowed) == "Image First"
|
||||
assert sanitize(None, "Image First", allowed=allowed) == "Image First"
|
||||
|
||||
|
||||
def test_sanitize_param_numbers() -> None:
|
||||
nodes = _import_nodes()
|
||||
sanitize = nodes._sanitize_param
|
||||
assert sanitize(5, 5) == 5
|
||||
assert sanitize(3.7, 0.2) == 3.7
|
||||
assert sanitize("abc", 0.2) == 0.2
|
||||
assert sanitize(None, 0.2) == 0.2
|
||||
assert sanitize(True, 5) == 5 # bool is not a valid number
|
||||