Compare commits

...
61 Commits
Author SHA1 Message Date
scraed 1820d88594 fix link 2025-09-17 15:59:35 +08:00
scraed c265bcc989 Add Wan2.2 inpainting example and bump version
Added a new example for Wan2.2 inpainting with image and workflow links in the README. Updated project version to 1.3.2 in pyproject.toml.
2025-09-17 15:55:28 +08:00
scraed b8973c570b Support Wan 2.2 Txt 2 Img 2025-09-17 15:46:11 +08:00
scraed 1b50a90424 qwen edit resize fix 2025-09-07 13:07:17 +08:00
charrywhite dab41249da Update README.md 2025-08-30 11:15:11 +08:00
charrywhite b2b2b21bf1 Update README.md 2025-08-30 11:14:53 +08:00
scraed 099d4137a9 Clarify speed improvement suggestions
Reworded instructions for boosting speed in README.
2025-08-26 12:10:11 +08:00
scraed 2f7967a584 update version 2025-08-25 11:37:54 +08:00
scraed e28e115297 add example workflow jsons 2025-08-25 11:37:31 +08:00
scraed f2cc70fed3 Update inpainted example image
Replaces the InPainted_Drag_Me_to_ComfyUI.png file in Example_14 with a new version. This may reflect updated visual content or corrections to the example image.
2025-08-25 11:11:09 +08:00
scraed b8e140f7aa Comment out unused model_sigmas initialization
The initialization of model_sigmas in KSamplerX0Inpaint was commented out to remove warning. Also remove group nodes from example workflow
2025-08-22 10:34:51 +08:00
scraed e6f61e806c Bump version to 1.3.0 in pyproject.toml
Updated the project version from 1.2.0 to 1.3.0 to reflect new changes or improvements in the LanPaint package.
2025-08-22 00:26:46 +08:00
scraed ba506f2654 Add new Qwen edit example and update README
Added Example_14 images and LanPaintQwen_03.jpg to showcase the masked Qwen edit workflow. Updated README with new example references and instructions for using the ComfyUI Qwen Image Edit workflow.
2025-08-22 00:17:40 +08:00
scraed 986dee6bd9 Merge branch 'master' of https://github.com/scraed/LanPaint 2025-08-21 18:12:18 +08:00
scraed e514553120 fix performance issue 2025-08-21 18:12:11 +08:00
scraed 40d7f0854e Update README.md 2025-08-18 23:07:02 +08:00
charrywhite a5e3af84f9 Update README.md 2025-08-18 18:18:21 +08:00
charrywhite 598ca776eb Update README.md 2025-08-18 18:10:50 +08:00
charrywhite b36584b2a7 Update README.md 2025-08-18 18:07:49 +08:00
charrywhite 082c549b24 Update README.md 2025-08-18 18:05:21 +08:00
charrywhite 453b6c090e Update README.md 2025-08-18 17:45:27 +08:00
charrywhite 07229ecbcd Update README.md
reorganize readme
2025-08-18 17:36:02 +08:00
charrywhite 76cf0ff5fe Update README.md 2025-08-18 17:25:21 +08:00
scraed e1f84469f1 Merge branch 'master' of https://github.com/scraed/LanPaint 2025-08-18 17:20:03 +08:00
charrywhite 1fa77b0d1e Update README.md 2025-08-18 17:19:20 +08:00
scraed cd62e71467 Update LanPaintQwen_01.jpg example image
Replaces the existing LanPaintQwen_01.jpg in the examples directory with a new version.
2025-08-18 17:19:02 +08:00
charrywhite deaf2cec2a Update README.md 2025-08-18 17:18:01 +08:00
scraed 587a941607 Update README.md 2025-08-18 15:44:21 +08:00
scraed 7bdbab75e6 Update LanPaintQwen_01.jpg 2025-08-18 15:35:01 +08:00
scraed b77c25677f Fix swapped links for Qwen workflows in README
Corrected the example links for Qwen Inpaint and Outpaint workflows to point to their respective directories.
2025-08-18 15:30:29 +08:00
scraed bf5b607238 update readme 2025-08-18 15:29:21 +08:00
scraed ad77fb7836 Merge branch 'master' of https://github.com/scraed/LanPaint 2025-08-18 15:27:19 +08:00
scraed 3f8eb2552f Update README and add new Qwen inpainting examples
Enhanced README with details and links for Qwen Image workflows and added new example images for Qwen inpainting and outpainting in Example_12 and Example_13 directories. Also included a sample result image for Qwen inpainting.
2025-08-18 15:27:12 +08:00
scraed 238e49e31f Update README.md 2025-08-15 15:34:36 +08:00
scraed 7aeb6e535f Add Qwen image support and new inpainting examples
Updated the README to announce Qwen image support and provide usage instructions. Added new example images and workflow files for Qwen inpainting in the examples directory.
2025-08-08 14:09:53 +08:00
scraed eda0f19944 Generalize dimension handling in LanPaint and fix mask reshape
Refactored LanPaint to dynamically handle tensors with varying numbers of dimensions by introducing add_none_dims and remove_none_dims utility methods. Updated all relevant tensor broadcasting to use these methods, improving flexibility. Also fixed reshape_mask in nodes.py to use the last two dimensions for resizing, ensuring correct mask shape.
2025-08-08 13:42:24 +08:00
scraed e20c8f20ce update version 2025-06-21 14:13:09 +08:00
scraed 89b9010b35 Fix indentation error in sample method
Corrected an indentation issue in the sample method of LanPaint_SamplerCustomAdvanced to ensure proper execution of the end_at_step check.
2025-06-21 14:09:11 +08:00
scraed ee0c65656e simplify custom sampler 2025-06-21 13:18:25 +08:00
scraed 48b1dd4be6 Merge branch 'master' into pr/33 2025-06-21 13:10:05 +08:00
scraed 9d304cd5a3 update readme 2025-06-21 13:03:30 +08:00
scraed 61c19ac31d update algorithm with better outpaint 2025-06-21 12:56:36 +08:00
Bagier 1ca6e53090 adjust info names 2025-06-20 08:31:51 +07:00
Bagier 520932cee0 add invalid value check 2025-06-19 20:49:20 +07:00
Bagier 37612bb399 Logic fix 2025-06-19 20:12:50 +07:00
Bagier 0e6cb45081 re-add lanpaint info 2025-06-19 19:25:59 +07:00
Bagier 0a5e36d55e add advanced custom sampler 2025-06-19 19:15:58 +07:00
Bagier b319f772cb fix parameter update error 2025-06-19 19:00:41 +07:00
Bagier 340376ad18 Add custom sampler variation 2025-06-19 16:14:26 +07:00
scraed dbfc1585fc Merge branch 'master' of https://github.com/scraed/LanPaint 2025-06-16 20:03:31 +08:00
scraed c3ae2c644d update coefficients 2025-06-16 20:03:25 +08:00
scraed c7017373c9 Update pyproject.toml 2025-06-08 17:59:04 +08:00
scraed 850f707eb6 Remove redundant comment 2025-06-08 17:58:40 +08:00
scraed 62870f060a Update README.md 2025-06-06 13:05:50 +08:00
scraed a91cefacf0 Update README.md 2025-06-06 13:05:16 +08:00
scraed 4265f71a85 Update README.md 2025-06-05 18:15:21 +08:00
scraed 4189ba80c2 add error message 2025-06-05 17:10:07 +08:00
scraed 23ad6e47fd add new masked blend node 2025-06-05 16:21:46 +08:00
scraed 4d3d5d17f0 Update README.md 2025-06-05 01:22:09 +08:00
scraed b86f3b7112 update version 2025-06-05 01:14:57 +08:00
scraed 96b7f3eecd fix sigma batch error 2025-06-05 01:11:04 +08:00
42 changed files with 5843 additions and 101 deletions
+121 -59
View File
@@ -1,29 +1,57 @@
# LanPaint (Thinking mode Inpaint)
<div align="center">
Unlock precise inpainting without additional training. LanPaint lets the model "think" through multiple iterations before denoising, aiming for seamless and accurate results.
![Inpainting Result 13](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_13.jpg)
# LanPaint: Universal Inpainting Sampler with "Think Mode"
[![arXiv](https://img.shields.io/badge/Arxiv-2502.03491-b31b1b.svg?logo=arXiv)](https://arxiv.org/abs/2502.03491)
[![Python Benchmark](https://img.shields.io/badge/🐍-Python_Benchmark-3776AB?logo=python)](https://github.com/scraed/LanPaintBench)
[![ComfyUI Extension](https://img.shields.io/badge/ComfyUI-Extension-7B5DFF)](https://github.com/comfyanonymous/ComfyUI)
[![Hugging Face](https://img.shields.io/badge/Hugging%20Face-yellow?logo=huggingface&logoColor=white)](https://huggingface.co/charrywhite/LanPaint)
[![Blog](https://img.shields.io/badge/📝-Blog-9cf)](https://scraed.github.io/scraedBlog/)
[![GitHub stars](https://img.shields.io/github/stars/scraed/LanPaint)](https://github.com/scraed/LanPaint/stargazers)
</div>
This is the official implementation of ["Lanpaint: Training-Free Diffusion Inpainting with Exact and Fast Conditional Inference"](https://arxiv.org/abs/2502.03491).
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 Exact and Fast Conditional Inference"](https://arxiv.org/abs/2502.03491). The repository is for ComfyUI extension. Local Python benchmark code is published here: [LanPaintBench](https://github.com/scraed/LanPaintBench).
![Qwen Result 2](https://github.com/scraed/LanPaint/blob/master/examples/LanPaintQwen_03.jpg)
Check [Mased Qwen Edit Workflow](https://github.com/scraed/LanPaint/tree/master/examples/Example_14). You need to follow the ComfyUI version of [Qwen Image Edit workflow](https://docs.comfy.org/tutorials/image/qwen/qwen-image-edit) to download and install the model.
![Qwen Result 1](https://github.com/scraed/LanPaint/blob/master/examples/LanPaintQwen_01.jpg)
Also check [Qwen Inpaint Workflow](https://github.com/scraed/LanPaint/tree/master/examples/Example_13) and [Qwen Outpaint Workflow](https://github.com/scraed/LanPaint/tree/master/examples/Example_12). You need to follow the ComfyUI version of [Qwen Image workflow](https://docs.comfy.org/tutorials/image/qwen/qwen-image) to download and install the model.
## Table of Contents
- [Features](#features)
- [Quickstart](#quickstart)
- [How to Use Examples](#how-to-use-examples)
- [Examples](#examples)
- [Wan 2.2 T2I](#example-wan22-inpaintlanpaint-k-sampler-5-steps-of-thinking)
- [Qwen Image](#example-qwen-image-inpaintlanpaint-k-sampler-5-steps-of-thinking)
- [HiDream](#example-hidream-inpaint-lanpaint-k-sampler-5-steps-of-thinking)
- [SD 3.5](#example-sd-35-inpaintlanpaint-k-sampler-5-steps-of-thinking)
- [Flux](#example-flux-inpaintlanpaint-k-sampler-5-steps-of-thinking)
- [SDXL Examples](#example-sdxl-0-character-consistency-side-view-generation-lanpaint-k-sampler-5-steps-of-thinking)
- [Usage](#usage)
- [Basic Sampler](#basic-sampler)
- [Advanced Sampler](#lanpaint-ksampler-advanced)
- [Tuning Guide](#lanpaint-ksampler-advanced-tuning-guide)
- [Community Showcase](#community-showcase-)
- [Updates](#updates)
- [ToDo](#todo)
- [Citation](#citation)
## Features
- **Universal Compatibility** – Works instantly with almost any model (SD 1.5, XL, 3.5, Flux, HiDream, or custom LoRAs) and ControlNet.
- **Universal Compatibility** – Works instantly with almost any model (**SD 1.5, XL, 3.5, Flux, HiDream, Qwen-Image or custom LoRAs**) and ControlNet.
![Inpainting Result 13](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_13.jpg)
- **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.
## How It Works
LanPaint uses Langevin Dynamics as "thinking" steps, which digs deeper into the diffusion process and allows the model to generate more consistent results.
LanPaint introduces "BIG score" that creates a **two-way alignment** between masked and unmasked areas. It continuously evaluates:
- *"Does the new content make sense with the existing elements?"*
- *"Do the existing elements support the new creation?"*
Based on this evaluation, LanPaint iteratively updates the noise in both the masked and unmasked regions.
LanPaint also implements an accurate, robust, and fast Langevin dynamics solver.
**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).
## Quickstart
@@ -41,32 +69,50 @@ LanPaint also implements an accurate, robust, and fast Langevin dynamics solver.
Once installed, you'll find the LanPaint nodes under the "sampling" category in ComfyUI. Use them just like the default KSampler for high-quality inpainting!
## Updates
- 2025/06/04
- Add more sampler support.
- Add early stopping to advanced sampler.
- 2025/05/28
- Major update on the Langevin solver. It is now much faster and more stable.
- Greatly simplified the parameters for advanced sampler.
- Fix performance issue on Flux and SD 3.5
- 2025/04/16
- Added Primary HiDream support
- 2025/03/22
- Added Primary Flux support
- Added Tease Mode
- 2025/03/10
- LanPaint has received a major update! All examples now use the LanPaint K Sampler, offering a simplified interface with enhanced performance and stability.
## **How to Use Examples:**
1. Navigate to the **example** folder (i.e example_1), download all pictures.
2. Drag **InPainted_Drag_Me_to_ComfyUI.png** into ComfyUI to load the workflow.
3. Download the required model (i.e clicking **Model Used in This Example**).
4. Load the model in ComfyUI.
5. Upload **Masked_Load_Me_in_Loader.png** to the **"Load image"** node in the **"Mask image for inpainting"** group (second from left), or the **Prepare Image** node.
7. Queue the task, you will get inpainted results from LanPaint. Some example also gives you inpainted results from the following methods for comparison:
- **[VAE Encode for Inpainting](https://comfyanonymous.github.io/ComfyUI_examples/inpaint/)**
- **[Set Latent Noise Mask](https://comfyui-wiki.com/en/tutorial/basic/how-to-inpaint-an-image-in-comfyui)**
## Examples
All examples use a random seed 0 to generate batch of 4 images for fair comparison. (Warning: Generating 4 images may exceed your GPU memory; adjust batch size as needed.)
### Example HiDream: InPaint(LanPaint K Sampler, 5 steps of thinking)
### Example Wan2.2: InPaint(LanPaint K Sampler, 5 steps of thinking)
We are excited to announce that LanPaint now supports Wan2.2 text to image generation with Wan2.2 T2V model.
![Inpainting Result 45](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_45.jpg)
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_15)
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.
### Example Qwen Image: InPaint(LanPaint K Sampler, 5 steps of thinking)
![Inpainting Result 14](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_14.jpg)
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_11)
You need to follow the ComfyUI version of [Qwen Image workflow](https://docs.comfy.org/tutorials/image/qwen/qwen-image) to download and install the model.
The following examples utilize a random seed of 0 to generate a batch of 4 images for variance demonstration and fair comparison. (Note: Generating 4 images may exceed your GPU memory; please adjust the batch size as necessary.)
### Example HiDream: InPaint (LanPaint K Sampler, 5 steps of thinking)
![Inpainting Result 8](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_11.jpg)
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_8)
You need to follow the ComfyUI version of [HiDream workflow](https://docs.comfy.org/tutorials/image/hidream/hidream-i1) to download and install the model.
### Example HiDream: OutPaint(LanPaint K Sampler, 5 steps of thinking)
![Inpainting Result 8](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_13(1).jpg)
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_10)
You need to follow the ComfyUI version of [HiDream workflow](https://docs.comfy.org/tutorials/image/hidream/hidream-i1) to download and install the model. Thanks [Amazon90](https://github.com/Amazon90) for providing this example.
### Example SD 3.5: InPaint(LanPaint K Sampler, 5 steps of thinking)
![Inpainting Result 8](https://github.com/scraed/LanPaint/blob/master/examples/InpaintChara_12.jpg)
[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_9)
@@ -115,23 +161,6 @@ You need to follow the ComfyUI version of [SD 3.5 workflow](https://comfyui-wiki
Check more for use cases like inpaint on [fine tuned models](https://github.com/scraed/LanPaint/issues/12#issuecomment-2938662021) and [face swapping](https://github.com/scraed/LanPaint/issues/12#issuecomment-2938723501), thanks to [Amazon90](https://github.com/Amazon90).
## **How to Use These Examples:**
1. Navigate to the **example** folder (i.e example_1) by clicking **View Workflow & Masks**, download all pictures.
2. Drag **InPainted_Drag_Me_to_ComfyUI.png** into ComfyUI to load the workflow.
3. Download the required model from Civitai by clicking **Model Used in This Example**.
4. Load the model into the **"Load Checkpoint"** node.
5. Upload **Original_No_Mask.png** to the **"Load image"** node in the **"Original Image"** group (far left).
6. Upload **Masked_Load_Me_in_Loader.png** to the **"Load image"** node in the **"Mask image for inpainting"** group (second from left).
7. Queue the task, you will get inpainted results from three methods:
- **[VAE Encode for Inpainting](https://comfyanonymous.github.io/ComfyUI_examples/inpaint/)** (middle),
- **[Set Latent Noise Mask](https://comfyui-wiki.com/en/tutorial/basic/how-to-inpaint-an-image-in-comfyui)** (second from right),
- **LanPaint** (far right).
Compare and explore the results from each method!
![WorkFlow](https://github.com/scraed/LanPaint/blob/master/Example.JPG)
## Usage
**Workflow Setup**
@@ -166,20 +195,22 @@ Full parameter control:
| `LanPaint_StepSize` | 0.1-1.0 | The StepSize of each thinking step. Recommend 0.1-0.5. |
| `LanPaint_Beta` | 0.1-2.0 | The StepSize ratio between masked / unmasked region. Small value can compensate high lambda values. Recommend 1.0 |
| `LanPaint_Friction` | 0.0-100.0 | The friction of Langevin dynamics. Higher means more slow but stable, lower means fast but unstable. Recommend 10.0 - 20.0|
| `LanPaint_EarlyStop` | 0-10 | Stop LanPaint iteration before the final sampling step. Helps to remove artifacts in some cases. Recommend 1-5|
| `LanPaint_PromptMode` | Image First / Prompt First | Image First mode focuses on the image context, maybe ignore prompt. Prompt First mode focuses more on the prompt. |
For detailed descriptions of each parameter, simply hover your mouse over the corresponding input field to view tooltips with additional information.
### LanPaint Mask Blend
This node blends the original image with the inpainted image based on the mask. It is useful if you want the unmasked region to match the original image pixel perfectly.
## LanPaint KSampler (Advanced) Tuning Guide
For challenging inpainting tasks:
1️⃣ **Boost Quality**
Increase total number of sampling steps, **LanPaint_NumSteps** (thinking iterations) or **LanPaint_Lambda** if the inpainted result does not meet your expectations.
Increase **total number of sampling steps** (very important!), **LanPaint_NumSteps** (thinking iterations) or **LanPaint_Lambda** if the inpainted result does not meet your expectations.
2️⃣ **Boost Speed**
If you want better results but still need fewer steps, consider:
Decrease **LanPaint_NumSteps** to accelerate generation! If you want better results but still need fewer steps, consider:
- **Increasing LanPaint_StepSize** to speed up the thinking process.
- **Decreasing LanPaint_Friction** to make the Langevin dynamics converges more faster.
@@ -192,14 +223,45 @@ If you find the results have wired texture, try
⚠️ **Notes**:
- For effective tuning, **fix the seed** and adjust parameters incrementally while observing the results. This helps isolate the impact of each setting. Better to do it with a batche of images to avoid overfitting on a single image.
## Community Showcase [](#community-showcase-)
Discover how the community is using LanPaint! Here are some user-created tutorials:
- [Ai绘画进阶148-三大王炸!庆祝高允贞出道6周年!T8即将直播?当AI绘画学会深度思考?!万能修复神器LanPaint,万物皆可修!-T8 Comfyui教程](https://www.youtube.com/watch?v=Z4DSTv3UPJo)
- [Ai绘画进阶151-真相了!T8竟是个AI?!LanPaint进阶(二),人物一致性,多视角实验性测试,新参数讲解,工作流分享-T8 Comfyui教程](https://www.youtube.com/watch?v=landiRhvF3k)
- [重绘和三视图角色一致性解决新方案!LanPaint节点尝试](https://www.youtube.com/watch?v=X0WbXdm6FA0)
- [ComfyUI: HiDream with Perturbation Upscale, LanPaint Inpainting (Workflow Tutorial)](https://www.youtube.com/watch?v=2-mGe4QVIIw&t=2785s)
- [ComfyUI必备LanPaint插件超详细使用教程](https://plugin.aix.ink/archives/lanpaint)
Submit a PR to add your tutorial/video here, or open an [Issue](https://github.com/scraed/LanPaint/issues) with details!
## Updates
- 2025/08/08
- Add Qwen image support
- 2025/06/21
- Update the algorithm with enhanced stability and outpaint performance.
- Add outpaint example
- Supports Sampler Custom (Thanks to [MINENEMA](https://github.com/MINENEMA))
- 2025/06/04
- Add more sampler support.
- Add early stopping to advanced sampler.
- 2025/05/28
- Major update on the Langevin solver. It is now much faster and more stable.
- Greatly simplified the parameters for advanced sampler.
- Fix performance issue on Flux and SD 3.5
- 2025/04/16
- Added Primary HiDream support
- 2025/03/22
- Added Primary Flux support
- Added Tease Mode
- 2025/03/10
- LanPaint has received a major update! All examples now use the LanPaint K Sampler, offering a simplified interface with enhanced performance and stability.
- 2025/03/06:
- Bug Fix for str not callable error and unpack error. Big thanks to [jamesWalker55](https://github.com/jamesWalker55) and [EricBCoding](https://github.com/EricBCoding).
## ToDo
- Try Implement Detailer
- Provide inference code on without GUI.
## Contribute
- 2025/03/06: Bug Fix for str not callable error and unpack error. Big thanks to [jamesWalker55](https://github.com/jamesWalker55) and [EricBCoding](https://github.com/EricBCoding).
- ~~Provide inference code on without GUI.~~ Check our local Python benchmark code [LanPaintBench](https://github.com/scraed/LanPaintBench).
## Citation
Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 921 KiB

+806
View File
@@ -0,0 +1,806 @@
{
"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
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

+909
View File
@@ -0,0 +1,909 @@
{
"id": "11cce4ab-536b-4f42-a95c-0be437d04ace",
"revision": 0,
"last_node_id": 136,
"last_link_id": 351,
"nodes": [
{
"id": 74,
"type": "LanPaint_KSampler",
"pos": [
276.219970703125,
179.55892944335938
],
"size": [
388.97625732421875,
572
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 336
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 334
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 335
},
{
"name": "latent_image",
"type": "LATENT",
"link": 345
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"slot_index": 0,
"links": [
347
]
}
],
"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": 15,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 351
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.23"
},
"widgets_values": [
"ComfyUI"
]
},
{
"id": 123,
"type": "CLIPLoader",
"pos": [
-730.1405029296875,
183.70140075683594
],
"size": [
330,
110
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"slot_index": 0,
"links": [
329,
330
]
}
],
"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": 124,
"type": "VAELoader",
"pos": [
-730.1405029296875,
333.70147705078125
],
"size": [
330,
60
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"slot_index": 0,
"links": [
342,
348
]
}
],
"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": 125,
"type": "UNETLoader",
"pos": [
-730.1405029296875,
43.7014274597168
],
"size": [
330,
90
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"slot_index": 0,
"links": [
331
]
}
],
"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": 127,
"type": "CLIPTextEncode",
"pos": [
-360.1405029296875,
253.70147705078125
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 330
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"slot_index": 0,
"links": [
335
]
}
],
"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": 128,
"type": "ModelSamplingAuraFlow",
"pos": [
60.14683151245117,
-37.92536544799805
],
"size": [
300,
58
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 331
}
],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
336
]
}
],
"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": 126,
"type": "CLIPTextEncode",
"pos": [
-360.57550048828125,
47.6118278503418
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 329
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"slot_index": 0,
"links": [
334
]
}
],
"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-inspired portrait of a beautiful young woman with ethereal features: She has long, flowing silver hair with glowing purple neon streaks, wearing a form-fitting black leather jacket adorned with holographic circuit designs in electric blue, and augmented reality earrings that project faint digital fractals. Her eyes are piercing emerald green with subtle cybernetic enhancements showing data overlays, and her expression is mysterious yet alluring, with a subtle smirk. Behind her: a foggy, neon-lit futuristic alleyway—crumbling brick walls covered in vibrant graffiti and flickering LED signs, puddles on the ground reflecting turquoise and violet lights from overhead drones. Dramatic volumetric lighting, ultra-realistic details, mist particles in the air."
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 130,
"type": "ImagePadForOutpaint",
"pos": [
-437.0110168457031,
895.0103759765625
],
"size": [
210,
174
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 337
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
338,
349
]
},
{
"name": "MASK",
"type": "MASK",
"links": [
339
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.39",
"Node name for S&R": "ImagePadForOutpaint",
"widget_ue_connectable": {}
},
"widgets_values": [
200,
200,
200,
200,
20
]
},
{
"id": 131,
"type": "VAEEncode",
"pos": [
-455.7289123535156,
723.4658203125
],
"size": [
210,
46
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "pixels",
"type": "IMAGE",
"link": 338
},
{
"name": "vae",
"type": "VAE",
"link": 342
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"slot_index": 0,
"links": [
340
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.23",
"Node name for S&R": "VAEEncode"
},
"widgets_values": []
},
{
"id": 132,
"type": "ThresholdMask",
"pos": [
-67.46508026123047,
903.83642578125
],
"size": [
270,
58
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "mask",
"type": "MASK",
"link": 339
}
],
"outputs": [
{
"name": "MASK",
"type": "MASK",
"links": [
341,
350
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.38",
"Node name for S&R": "ThresholdMask"
},
"widgets_values": [
0.010000000000000002
]
},
{
"id": 133,
"type": "SetLatentNoiseMask",
"pos": [
-152.71241760253906,
712.6437377929688
],
"size": [
264.5999755859375,
46
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 340
},
{
"name": "mask",
"type": "MASK",
"link": 341
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"slot_index": 0,
"links": [
345
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.23",
"Node name for S&R": "SetLatentNoiseMask"
},
"widgets_values": []
},
{
"id": 129,
"type": "LoadImage",
"pos": [
-789.0392456054688,
827.515380859375
],
"size": [
295,
399
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
337
]
},
{
"name": "MASK",
"type": "MASK",
"links": []
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "LoadImage",
"widget_ue_connectable": {}
},
"widgets_values": [
"ComfyUI_07699_.png",
"image"
]
},
{
"id": 134,
"type": "VAEDecode",
"pos": [
256.8516540527344,
900.2234497070312
],
"size": [
210,
46
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 347
},
{
"name": "vae",
"type": "VAE",
"link": 348
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"slot_index": 0,
"links": [
346
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.23",
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 135,
"type": "LanPaint_MaskBlend",
"pos": [
547.1599731445312,
960.5724487304688
],
"size": [
210,
98
],
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "image1",
"type": "IMAGE",
"link": 349
},
{
"name": "image2",
"type": "IMAGE",
"link": 346
},
{
"name": "mask",
"type": "MASK",
"link": 350
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
351
]
}
],
"properties": {
"cnr_id": "LanPaint",
"ver": "4d3d5d17f0105b673df92da5b084cce567c9c712",
"Node name for S&R": "LanPaint_MaskBlend"
},
"widgets_values": [
9
]
},
{
"id": 136,
"type": "MarkdownNote",
"pos": [
728.9194946289062,
332.5254821777344
],
"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": [
[
329,
123,
0,
126,
0,
"CLIP"
],
[
330,
123,
0,
127,
0,
"CLIP"
],
[
331,
125,
0,
128,
0,
"MODEL"
],
[
334,
126,
0,
74,
1,
"CONDITIONING"
],
[
335,
127,
0,
74,
2,
"CONDITIONING"
],
[
336,
128,
0,
74,
0,
"MODEL"
],
[
337,
129,
0,
130,
0,
"IMAGE"
],
[
338,
130,
0,
131,
0,
"IMAGE"
],
[
339,
130,
1,
132,
0,
"MASK"
],
[
340,
131,
0,
133,
0,
"LATENT"
],
[
341,
132,
0,
133,
1,
"MASK"
],
[
342,
124,
0,
131,
1,
"VAE"
],
[
345,
133,
0,
74,
3,
"LATENT"
],
[
346,
134,
0,
135,
1,
"IMAGE"
],
[
347,
74,
0,
134,
0,
"LATENT"
],
[
348,
124,
0,
134,
1,
"VAE"
],
[
349,
130,
0,
135,
0,
"IMAGE"
],
[
350,
132,
0,
135,
2,
"MASK"
],
[
351,
135,
0,
113,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.6087272163919795,
"offset": [
1617.0504979084035,
167.0583828525509
]
},
"frontendVersion": "1.25.10",
"node_versions": {
"comfy-core": "0.3.18",
"LanPaint": "0f509469ed2cd60c6032f739e282aad5dfc06166"
},
"groupNodes": {}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

+735
View File
@@ -0,0 +1,735 @@
{
"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
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "LanPaint"
version = "1.0.1"
version = "1.3.2"
description = "Achieve seamless inpainting results without needing a specialized inpainting model."
authors = [
{name = "LanPaint", email = "czhengac@connect.ust.hk"}
+51 -23
View File
@@ -1,6 +1,7 @@
import torch
from .utils import *
from functools import partial
class LanPaint():
def __init__(self, Model, NSteps, Friction, Lambda, Beta, StepSize, IS_FLUX = False, IS_FLOW = False):
self.n_steps = NSteps
@@ -11,8 +12,17 @@ class LanPaint():
self.inner_model = Model
self.friction = Friction
self.chara_beta = Beta
self.img_dim_size = None
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]
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):
self.img_dim_size = len(x.shape)
self.latent_image = latent_image
self.noise = noise
if n_steps is None:
@@ -23,26 +33,26 @@ class LanPaint():
step_size = self.step_size * (1 - abt)
step_size = step_size[:, None, None, None]
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
x = x * (1 - latent_mask) + self.inner_model.inner_model.scale_latent_inpaint(x=x, sigma=sigma, noise=self.noise, latent_image=self.latent_image)* latent_mask
if IS_FLUX or IS_FLOW:
x_t = x * ( abt[:, None,None,None]**0.5 + (1-abt[:, None,None,None])**0.5 )
x_t = x * ( self.add_none_dims(abt)**0.5 + (1-self.add_none_dims(abt))**0.5 )
else:
x_t = x / ( 1+VE_Sigma[:, None,None,None]**2 )**0.5 # switch to variance perserving x_t values
x_t = x / ( 1+self.add_none_dims(VE_Sigma)**2 )**0.5 # switch to variance perserving x_t values
############ 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
for i in range(n_steps):
score_func = partial( self.score_model, y = self.latent_image, mask = latent_mask, abt = abt[:, None,None,None], sigma = VE_Sigma[:, None,None,None], tflow = Flow_t[:, None,None,None], model_options = model_options, seed = seed )
x_t, args = self.langevin_dynamics(x_t, score_func , latent_mask, step_size , current_times, sigma_x = self.sigma_x(abt)[:, None,None,None], sigma_y = self.sigma_y(abt)[:, None,None,None], args = args)
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 )
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 IS_FLUX or IS_FLOW:
x = x_t / ( abt[:, None,None,None]**0.5 + (1-abt[:, None,None,None])**0.5 )
x = x_t / ( self.add_none_dims(abt)**0.5 + (1-self.add_none_dims(abt))**0.5 )
else:
x = x_t * ( 1+VE_Sigma[:, None,None,None]**2 )**0.5 # switch to variance perserving x_t values
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)
@@ -52,14 +62,13 @@ class LanPaint():
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, tflow[:, 0,0,0], model_options=model_options, seed=seed)
x_0, x_0_BIG = 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, sigma[:, 0,0,0], model_options=model_options, seed=seed)
x_0, x_0_BIG = self.inner_model(x, self.remove_none_dims(sigma), model_options=model_options, seed=seed)
score_x = -(x_t - x_0)
score_y = - (1 + lamb) * ( x_t - y ) + lamb * (x_t - x_0_BIG)
@@ -82,33 +91,52 @@ class LanPaint():
# -------------------------------------------------------------------------
# Compute the Langevin dynamics update in variance perserving notation
# -------------------------------------------------------------------------
x0 = self.x0_evalutation(x_t, score, sigma, args)
C = abt**0.5 * x0 / (1-abt)
#x0 = self.x0_evalutation(x_t, score, sigma, args)
#C = abt**0.5 * x0 / (1-abt)
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
def Coef_C(x_t):
x0 = self.x0_evalutation(x_t, score, sigma, args)
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):
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, = args
v, C = args
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, v = advance_time(x_t, v, dt/2, Gamma, A, C, D)
C_new = 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
return x_t, (v,)
return x_t, (v, C)
def prepare_step_size(self, current_times, step_size, sigma_x, sigma_y):
# -------------------------------------------------------------------------
# Unpack current times parameters (sigma and abt)
sigma, abt, flow_t = current_times
sigma = sigma[:, None,None,None]
abt = abt[:, None,None,None]
sigma = self.add_none_dims(sigma)
abt = self.add_none_dims(abt)
# Compute time step (dtx, dty) for x and y branches.
dtx = 2 * step_size * sigma_x
dty = 2 * step_size * sigma_y
@@ -123,9 +151,9 @@ class LanPaint():
# adjust dt to match denoise-addnoise steps sizes
Gamma_hat_x /= 2.
Gamma_hat_y /= 2.
A_t_x = (1) / ( 1 - abt ) * dtx / 2
A_t_y = (1) / ( 1 - abt ) * dty / 2
A_t_y = (1+self.chara_lamb) / ( 1 - abt ) * dty / 2
A_x = A_t_x / (dtx/2)
A_y = A_t_y / (dty/2)
+234 -11
View File
@@ -11,15 +11,13 @@ from comfy.samplers import *
from comfy.model_base import ModelType
from .utils import *
from .lanpaint import LanPaint
# Monkey patch comfy.samplers module by importing with absolute package path
#exec(inspect.getsource(comfy.samplers).replace("from .", "from comfy."))
def reshape_mask(input_mask, output_shape):
dims = len(output_shape) - 2
scale_mode = "nearest-exact"
mask = torch.nn.functional.interpolate(input_mask, size=output_shape[2:], mode=scale_mode)
mask = torch.nn.functional.interpolate(input_mask, size=output_shape[-2:], mode=scale_mode)
if mask.shape[1] < output_shape[1]:
mask = mask.repeat((1, output_shape[1]) + (1,) * dims)[:,:output_shape[1]]
mask = repeat_to_batch_size(mask, output_shape[0])
@@ -77,8 +75,8 @@ class KSamplerX0Inpaint:
def __init__(self, model, sigmas):
self.inner_model = model
self.sigmas = sigmas
self.model_sigmas = torch.cat( (torch.tensor([0.], device = sigmas.device) , torch.tensor( self.inner_model.model_patcher.get_model_object("model_sampling").sigmas, device = sigmas.device) ) )
self.model_sigmas = torch.tensor( self.model_sigmas, dtype = self.sigmas.dtype )
#self.model_sigmas = torch.cat( (torch.tensor([0.], device = sigmas.device) , torch.tensor( self.inner_model.model_patcher.get_model_object("model_sampling").sigmas, device = sigmas.device) ) )
#self.model_sigmas = torch.tensor( self.model_sigmas, dtype = self.sigmas.dtype )
def __call__(self, x, sigma, denoise_mask, model_options={}, seed=None,**kwargs):
### For 1.5 and XL model
# x is x_t in the notation of variance exploding diffusion model, x_t = x_0 + sigma * noise
@@ -111,10 +109,10 @@ class KSamplerX0Inpaint:
latent_mask = 1 - denoise_mask
current_times = (VE_Sigma, abt, Flow_t)
current_step = torch.argmin( torch.abs( self.sigmas - sigma ) )
current_step = torch.argmin( torch.abs( self.sigmas - torch.mean(sigma) ) )
total_steps = len(self.sigmas)-1
if total_steps - current_step < self.LanPaint_early_stop:
if total_steps - current_step <= self.LanPaint_early_stop:
out = self.PaintMethod(x, self.latent_image, self.noise, sigma, latent_mask, current_times, model_options, seed, n_steps=0)
else:
out = self.PaintMethod(x, self.latent_image, self.noise, sigma, latent_mask, current_times, model_options, seed)
@@ -260,8 +258,8 @@ class LanPaint_KSampler():
def sample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=1.0, LanPaint_NumSteps=5, LanPaint_PromptMode = "Image First", LanPaint_Info=""):
model.LanPaint_StepSize = 0.15
model.LanPaint_Lambda = 8.0
model.LanPaint_Beta = 1.0
model.LanPaint_Lambda = 16.0
model.LanPaint_Beta = 1.
model.LanPaint_NumSteps = LanPaint_NumSteps
model.LanPaint_Friction = 15.
model.LanPaint_EarlyStop = 1
@@ -289,7 +287,7 @@ class LanPaint_KSamplerAdvanced:
"end_at_step": ("INT", {"default": 10000, "min": 0, "max": 10000}),
"return_with_leftover_noise": (["disable", "enable"], ),
"LanPaint_NumSteps": ("INT", {"default": 5, "min": 0, "max": 100, "tooltip": "The number of steps for the Langevin dynamics, representing the turns of thinking per step."}),
"LanPaint_Lambda": ("FLOAT", {"default": 8., "min": 0.1, "max": 50.0, "step": 0.1, "round": 0.1, "tooltip": "The bidirectional guidance scale. Higher values align with known regions more closely, but may result in instability."}),
"LanPaint_Lambda": ("FLOAT", {"default": 16., "min": 0.1, "max": 50.0, "step": 0.1, "round": 0.1, "tooltip": "The bidirectional guidance scale. Higher values align with known regions more closely, but may result in instability."}),
"LanPaint_StepSize": ("FLOAT", {"default": 0.15, "min": 0.0001, "max": 1., "step": 0.01, "round": 0.001, "tooltip": "The step size for the Langevin dynamics. Higher values result in faster convergence but may be unstable."}),
"LanPaint_Beta": ("FLOAT", {"default": 1., "min": 0.0001, "max": 5, "step": 0.1, "round": 0.1, "tooltip": "The step size ratio between masked / unmasked regions. Lower value can compensate high values of LanPaint_Lambda."}),
"LanPaint_Friction": ("FLOAT", {"default": 15, "min": 0., "max": 50.0, "step": 0.1, "round": 0.1, "tooltip": "The friction parameter for fast langevin, lower values result in faster convergence but may be unstable."}),
@@ -326,11 +324,233 @@ class LanPaint_KSamplerAdvanced:
return nodes.common_ksampler(model, noise_seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise, disable_noise=disable_noise, start_step=start_at_step, last_step=end_at_step, force_full_denoise=force_full_denoise)
class MaskBlend:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image1": ("IMAGE", {"tooltip": "Image before inpaint"}),
"image2": ("IMAGE", {"tooltip": "Image after inpaint"}),
"mask": ("MASK",),
"blend_overlap": ("INT", {"default": 1, "min": 1, "max": 51, "step": 2, "tooltip": "The number of pixels to blend between the two images."})
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "blend_images"
CATEGORY = "image/postprocessing"
def blend_images(self, image1: torch.Tensor, image2: torch.Tensor, mask: torch.Tensor, blend_overlap: int):
# smooth the binary 01 mask, keep 1 still 1, but smooth the transition from 1 to 0
# for each mask pixel, find out the nearest 1 pixel, and set the mask value to the distance between the two pixels
# check the size of mask and image1, image2, if not the same, assert error
if image1.shape[1] != image2.shape[1] or image1.shape[2] != image2.shape[2]:
raise ValueError("Make sure your image size is a multiple of 8. Otherwise the mask will not be aligned with the output image.")
mask = mask.float()
mask = torch.nn.functional.max_pool2d(mask, kernel_size=blend_overlap, stride=1, padding=blend_overlap//2)
# apply Gaussian blur with kernel size blend_overlap
kernel = self.gaussian_kernel(blend_overlap)
kernel = kernel.to(image1.device)
kernel = kernel[None, None, ...]
mask = torch.nn.functional.conv2d(mask[:,None,:,:], kernel, padding=blend_overlap//2)[:,0,:,:]
blended_image = image1 * (1 - mask[...,None]) + image2 * mask[...,None]
return (blended_image,)
def gaussian_kernel(self,kernel_size):
"""
Creates a 2D Gaussian kernel with the given size and standard deviation (sigma).
"""
sigma = (kernel_size - 1)/4
# Create a grid of (x, y) coordinates
x = torch.arange(kernel_size).float() - kernel_size // 2
y = torch.arange(kernel_size).float() - kernel_size // 2
x_grid, y_grid = torch.meshgrid(x, y, indexing='ij')
# Compute the Gaussian function
kernel = torch.exp(-(x_grid ** 2 + y_grid ** 2) / (2 * sigma ** 2))
kernel = kernel / kernel.sum() # Normalize the kernel
return kernel
class Noise_EmptyNoise:
def generate_noise(self, latent):
return torch.zeros_like(latent["samples"])
class Noise_RandomNoise:
def __init__(self, seed):
self.seed = seed
def generate_noise(self, latent):
torch.manual_seed(self.seed)
return torch.randn_like(latent["samples"])
# Custom sampler implementation mimmicking base comfy nodes_custom_sampler.py
class LanPaint_SamplerCustom:
@classmethod
def INPUT_TYPES(s):
return {"required":
{"model": ("MODEL",),
"add_noise": ("BOOLEAN", {"default": True}),
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01}),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"sampler": ("SAMPLER",),
"sigmas": ("SIGMAS",),
"latent_image": ("LATENT",),
"LanPaint_NumSteps": ("INT", {"default": 5, "min": 0, "max": 100, "tooltip": "Number of steps for Langevin dynamics, representing turns of thinking per step."}),
"LanPaint_PromptMode": (["Image First", "Prompt First"], {"tooltip": "Image First: prioritizes image quality; Prompt First: prioritizes prompt adherence."}),
"LanPaint_Info": ("STRING", {"default": "LanPaint Custom Sampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!", "multiline": True}),
}
}
RETURN_TYPES = ("LATENT", "LATENT")
RETURN_NAMES = ("output", "denoised_output")
FUNCTION = "sample"
CATEGORY = "sampling/custom_sampling"
def sample(self, model, sampler, sigmas, add_noise, noise_seed, cfg, positive, negative, latent_image, LanPaint_NumSteps, LanPaint_PromptMode, LanPaint_Info=""):
model.LanPaint_StepSize = 0.15
model.LanPaint_Lambda = 16.0
model.LanPaint_Beta = 1.
model.LanPaint_NumSteps = LanPaint_NumSteps
model.LanPaint_Friction = 15.
model.LanPaint_EarlyStop = 1
if LanPaint_PromptMode == "Image First":
model.LanPaint_cfg_BIG = cfg
else:
model.LanPaint_cfg_BIG = 0 * cfg - 0.5
with override_sample_function():
latent = latent_image.copy()
latent_image = latent["samples"]
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image)
latent["samples"] = latent_image
if not add_noise:
noise = Noise_EmptyNoise().generate_noise(latent)
else:
noise = Noise_RandomNoise(noise_seed).generate_noise(latent)
noise_mask = None
if "noise_mask" in latent:
noise_mask = latent["noise_mask"]
x0_output = {}
callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output)
disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
samples = comfy.sample.sample_custom(model, noise, cfg, sampler, sigmas, positive, negative, latent_image,noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise_seed)
out = latent.copy()
out["samples"] = samples
if "x0" in x0_output:
out_denoised = latent.copy()
out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu())
else:
out_denoised = out
return (out, out_denoised)
class LanPaint_SamplerCustomAdvanced:
@classmethod
def INPUT_TYPES(s):
return {"required":
{"noise": ("NOISE",),
"guider": ("GUIDER",),
"sampler": ("SAMPLER",),
"sigmas": ("SIGMAS",),
"latent_image": ("LATENT",),
"start_at_step": ("INT", {"default": 0, "min": 0, "max": 10000}),
"end_at_step": ("INT", {"default": 10000, "min": 0, "max": 10000}),
"return_with_leftover_noise": (["disable", "enable"], ),
"LanPaint_NumSteps": ("INT", {"default": 5, "min": 0, "max": 100, "tooltip": "Number of steps for Langevin dynamics, representing turns of thinking per step."}),
"LanPaint_Lambda": ("FLOAT", {"default": 16.0, "min": 0.1, "max": 50.0, "step": 0.1, "tooltip": "Bidirectional guidance scale. Higher values align with known regions but may cause instability."}),
"LanPaint_StepSize": ("FLOAT", {"default": 0.15, "min": 0.0001, "max": 1.0, "step": 0.01, "tooltip": "Step size for Langevin dynamics. Higher values speed convergence but may be unstable."}),
"LanPaint_Beta": ("FLOAT", {"default": 1.0, "min": 0.0001, "max": 5.0, "step": 0.1, "tooltip": "Step size ratio between masked/unmasked regions. Lower values balance high Lambda."}),
"LanPaint_Friction": ("FLOAT", {"default": 15.0, "min": 0.0, "max": 50.0, "step": 0.1, "tooltip": "Friction parameter for fast Langevin. Lower values speed convergence but may be unstable."}),
"LanPaint_PromptMode": (["Image First", "Prompt First"], {"tooltip": "Image First: prioritizes image quality; Prompt First: prioritizes prompt adherence."}),
"LanPaint_EarlyStop": ("INT", {"default": 1, "min": 0, "max": 10000, "tooltip": "Steps to stop LanPaint early, preventing irregular patterns."}),
"LanPaint_Info": ("STRING", {"default": "LanPaint Custom Sampler Adv. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!", "multiline": True}),
}
}
RETURN_TYPES = ("LATENT", "LATENT")
RETURN_NAMES = ("output", "denoised_output")
FUNCTION = "sample"
CATEGORY = "sampling/custom_sampling"
def sample(self, noise, guider, sampler, sigmas, latent_image, start_at_step, end_at_step, return_with_leftover_noise, LanPaint_NumSteps, LanPaint_Lambda, LanPaint_StepSize, LanPaint_Beta, LanPaint_Friction, LanPaint_PromptMode, LanPaint_EarlyStop, LanPaint_Info=""):
force_full_denoise = True
if end_at_step <= start_at_step:
raise ValueError('end_at_step must be larger than start_at_step')
if return_with_leftover_noise == "enable":
force_full_denoise = False
model = guider.model_patcher
model.LanPaint_StepSize = LanPaint_StepSize
model.LanPaint_Lambda = LanPaint_Lambda
model.LanPaint_Beta = LanPaint_Beta
model.LanPaint_NumSteps = LanPaint_NumSteps
model.LanPaint_Friction = LanPaint_Friction
model.LanPaint_EarlyStop = LanPaint_EarlyStop
if LanPaint_PromptMode == "Image First":
model.LanPaint_cfg_BIG = guider.cfg
else:
model.LanPaint_cfg_BIG = 0 * guider.cfg - 0.5
with override_sample_function():
latent = latent_image.copy()
latent_image_samples = latent["samples"]
latent_image_samples = comfy.sample.fix_empty_latent_channels(model, latent_image_samples)
latent["samples"] = latent_image_samples
noise_mask = None
if "noise_mask" in latent:
noise_mask = latent["noise_mask"]
# From base comfy samplers.py
if end_at_step is not None and end_at_step < (len(sigmas) - 1):
sigmas = sigmas[:end_at_step + 1]
if force_full_denoise:
sigmas[-1] = 0
if start_at_step is not None:
if start_at_step < (len(sigmas) - 1):
sigmas = sigmas[start_at_step:]
else:
if latent_image is not None:
return latent_image
else:
return torch.zeros_like(noise)
x0_output = {}
callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output)
disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
samples = guider.sample( noise.generate_noise(latent), latent_image_samples, sampler, sigmas, denoise_mask=noise_mask, callback=callback,disable_pbar=disable_pbar, seed=noise.seed
)
samples = samples.to(comfy.model_management.intermediate_device())
out = latent.copy()
out["samples"] = samples
if "x0" in x0_output:
out_denoised = latent.copy()
out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu())
else:
out_denoised = out
return (out, out_denoised)
# A dictionary that contains all nodes you want to export with their names
# NOTE: names should be globally unique
NODE_CLASS_MAPPINGS = {
"LanPaint_KSampler": LanPaint_KSampler,
"LanPaint_KSamplerAdvanced": LanPaint_KSamplerAdvanced,
"LanPaint_SamplerCustom" : LanPaint_SamplerCustom,
"LanPaint_SamplerCustomAdvanced" : LanPaint_SamplerCustomAdvanced,
"LanPaint_MaskBlend": MaskBlend,
# "LanPaint_UpSale_LatentNoiseMask": LanPaint_UpSale_LatentNoiseMask,
}
@@ -338,5 +558,8 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"LanPaint_KSampler": "LanPaint KSampler",
"LanPaint_KSamplerAdvanced": "LanPaint KSampler (Advanced)",
"LanPaint_SamplerCustom" : "LanPaint Sampler Custom",
"LanPaint_SamplerCustomAdvanced" : "LanPaint Sampler Custom (Advanced)",
"LanPaint_MaskBlend": "LanPaint Mask Blend",
# "LanPaint_UpSale_LatentNoiseMask": "LanPaint UpSale Latent Noise Mask"
}
+20 -7
View File
@@ -240,7 +240,6 @@ class StochasticHarmonicOscillator:
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
@@ -253,8 +252,8 @@ class StochasticHarmonicOscillator:
EE = 1 - Gamma_hat * zeta_2
if v0 is None:
#v0 = torch.randn_like(y0) * D / 2 ** 0.5
v0 = (C - A * y0)/Gamma**0.5
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
@@ -273,16 +272,30 @@ class StochasticHarmonicOscillator:
cov_matrix[..., 0, 1] = cov_yv
cov_matrix[..., 1, 0] = cov_yv # symmetric
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)
tol = 1e-8
cov_yy = torch.clamp( cov_yy, min = tol )
sd_yy = torch.sqrt( cov_yy )
inv_sd_yy = 1/(sd_yy)
scale_tril[..., 0, 0] = sd_yy
scale_tril[..., 0, 1] = 0.
scale_tril[..., 1, 0] = cov_yv * inv_sd_yy
scale_tril[..., 1, 1] = torch.clamp( cov_vv - cov_yv**2 / cov_yy, min = tol ) ** 0.5
# check if it matches torch.linalg.
#assert torch.allclose(torch.linalg.cholesky(cov_matrix), scale_tril, atol = 1e-4, rtol = 1e-4 )
# Sample correlated noise from multivariate normal
mean = torch.zeros(*batch_shape, 2, device=y0.device, dtype=y0.dtype)
mean[..., 0] = y_mean
mean[..., 1] = v_mean
new_yv = torch.distributions.MultivariateNormal(
loc=mean,
covariance_matrix=cov_matrix
scale_tril=scale_tril
).sample()
return new_yv[...,0], new_yv[...,1]
return new_yv[...,0], new_yv[...,1]