.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..b8024625c83d881652c028995c83712582af7820
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,9 @@
+include LICENSE
+include README.md
+
+recursive-exclude * __pycache__
+recursive-exclude * *.py[co]
+
+recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
+
+graft src/LanPaint/web
diff --git a/Nodes.JPG b/Nodes.JPG
new file mode 100644
index 0000000000000000000000000000000000000000..0457d4dd499d1b334f0faa214c6b02310289d269
--- /dev/null
+++ b/Nodes.JPG
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5371453dbf997f4684a0712e19558c7c1592fc1c7cd57321aba00975689136cb
+size 121562
diff --git a/README.md b/README.md
index 6ddff2f305ace52cf0365e74625592c1a32ac0be..4920fac381f6ad9c10aef4caa987248e682efe5b 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,272 @@
----
-license: gpl-3.0
----
+
+
+# LanPaint: Universal Inpainting Sampler with "Think Mode"
+[](https://arxiv.org/abs/2502.03491)
+[](https://github.com/scraed/LanPaintBench)
+[](https://github.com/comfyanonymous/ComfyUI)
+[](https://scraed.github.io/scraedBlog/)
+[](https://github.com/scraed/LanPaint/stargazers)
+
+
+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).
+
+
+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.
+
+
+
+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)
+ - [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, Qwen-Image 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.
+
+**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
+
+1. **Install ComfyUI**: Follow the official [ComfyUI installation guide](https://docs.comfy.org/get_started) to set up ComfyUI on your system. Or ensure your ComfyUI version > 0.3.11.
+2. **Install ComfyUI-Manager**: Add the [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager) for easy extension management.
+3. **Install LanPaint Nodes**:
+ - **Via ComfyUI-Manager**: Search for "[LanPaint](https://registry.comfy.org/publishers/scraed/nodes/LanPaint)" in the manager and install it directly.
+ - **Manually**: Click "Install via Git URL" in ComfyUI-Manager and input the GitHub repository link:
+ ```
+ https://github.com/scraed/LanPaint.git
+ ```
+ Alternatively, clone this repository into the `ComfyUI/custom_nodes` folder.
+4. **Restart ComfyUI**: Restart ComfyUI to load the LanPaint nodes.
+
+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!
+
+
+## **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
+
+### Example Qwen Image: InPaint(LanPaint K Sampler, 5 steps of thinking)
+We are excited to announce that LanPaint now supports Qwen Image, providing powerful inpainting capabilities for image editing.
+
+
+[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)
+
+[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)
+.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)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_9)
+
+You need to follow the ComfyUI version of [SD 3.5 workflow](https://comfyui-wiki.com/en/tutorial/advanced/stable-diffusion-3-5-comfyui-workflow) to download and install the model.
+
+### Example Flux: InPaint(LanPaint K Sampler, 5 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_7)
+[Model Used in This Example](https://huggingface.co/Comfy-Org/flux1-dev/blob/main/flux1-dev-fp8.safetensors)
+(Note: Prompt First mode is disabled on Flux. As it does not use CFG guidance.)
+
+### Example SDXL 0: Character Consistency (Side View Generation) (LanPaint K Sampler, 5 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_6)
+[Model Used in This Example](https://civitai.com/models/1188071?modelVersionId=1408658)
+
+(Tricks 1: You can emphasize the character by copy it's image multiple times with Photoshop. Here I have made one extra copy.)
+
+(Tricks 2: Use prompts like multiple views, multiple angles, clone, turnaround. Use LanPaint's Prompt first mode (does not support Flux))
+
+(Tricks 3: Remeber LanPaint can in-paint: Mask non-consistent regions and try again!)
+
+
+### Example SDXL 1: Basket to Basket Ball (LanPaint K Sampler, 2 steps of thinking).
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_1)
+[Model Used in This Example](https://civitai.com/models/1188071?modelVersionId=1408658)
+### Example SDXL 2: White Shirt to Blue Shirt (LanPaint K Sampler, 5 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_2)
+[Model Used in This Example](https://civitai.com/models/1188071?modelVersionId=1408658)
+### Example SDXL 3: Smile to Sad (LanPaint K Sampler, 5 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_3)
+[Model Used in This Example](https://civitai.com/models/133005/juggernaut-xl)
+### Example SDXL 4: Damage Restoration (LanPaint K Sampler, 5 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_4)
+[Model Used in This Example](https://civitai.com/models/133005/juggernaut-xl)
+### Example SDXL 5: Huge Damage Restoration (LanPaint K Sampler, 20 steps of thinking)
+
+[View Workflow & Masks](https://github.com/scraed/LanPaint/tree/master/examples/Example_5)
+[Model Used in This Example](https://civitai.com/models/133005/juggernaut-xl)
+
+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).
+
+
+## Usage
+
+**Workflow Setup**
+Same as default ComfyUI KSampler - simply replace with LanPaint KSampler nodes. The inpainting workflow is the same as the [SetLatentNoiseMask](https://comfyui-wiki.com/zh/comfyui-nodes/latent/inpaint/set-latent-noise-mask) inpainting workflow.
+
+**Note**
+- LanPaint requires binary masks (values of 0 or 1) without opacity or smoothing. To ensure compatibility, set the mask's **opacity and hardness to maximum** in your mask editor. During inpainting, any mask with smoothing or gradients will automatically be converted to a binary mask.
+- LanPaint relies heavily on your text prompts to guide inpainting - explicitly describe the content you want generated in the masked area. If results show artifacts or mismatched elements, counteract them with targeted negative prompts.
+
+## Basic Sampler
+
+
+- LanPaint KSampler: The most basic and easy to use sampler for inpainting.
+- LanPaint KSampler (Advanced): Full control of all parameters.
+
+### LanPaint KSampler
+Simplified interface with recommended defaults:
+
+- Steps: 20 - 50. More steps will give more "thinking" and better results.
+- LanPaint NumSteps: The turns of thinking before denoising. Recommend 5 for most of tasks ( which means 5 times slower than sampling without thinking). Use 10 for more challenging tasks.
+- LanPaint Prompt mode: Image First mode and Prompt First mode. Image First mode focuses on the image, inpaint based on image context (maybe ignore prompt), while Prompt First mode focuses more on the prompt. Use Prompt First mode for tasks like character consistency. (Technically, it Prompt First mode change CFG scale to negative value in the BIG score to emphasis prompt, which will costs image quality.)
+
+### LanPaint KSampler (Advanced)
+Full parameter control:
+**Key Parameters**
+
+| Parameter | Range | Description |
+|-----------|-------|-------------|
+| `Steps` | 0-100 | Total steps of diffusion sampling. Higher means better inpainting. Recommend 20-50. |
+| `LanPaint_NumSteps` | 0-20 | Reasoning iterations per denoising step ("thinking depth"). Easy task: 2-5. Hard task: 5-10 |
+| `LanPaint_Lambda` | 0.1-50 | Content alignment strength (higher = stricter). Recommend 4.0 - 10.0 |
+| `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** (very important!), **LanPaint_NumSteps** (thinking iterations) or **LanPaint_Lambda** if the inpainted result does not meet your expectations.
+
+2️⃣ **Boost Speed**
+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.
+
+3️⃣ **Fix Unstability**:
+If you find the results have wired texture, try
+- Reduce **LanPaint_Friction** to make the Langevin dynamics more stable.
+- Reduce **LanPaint_StepSize** to use smaller step size.
+- Reduce **LanPaint_Beta** if you are using a high lambda value.
+
+⚠️ **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.~~ Check our local Python benchmark code [LanPaintBench](https://github.com/scraed/LanPaintBench).
+
+## Citation
+
+```
+@misc{zheng2025lanpainttrainingfreediffusioninpainting,
+ title={Lanpaint: Training-Free Diffusion Inpainting with Exact and Fast Conditional Inference},
+ author={Candi Zheng and Yuan Lan and Yang Wang},
+ year={2025},
+ eprint={2502.03491},
+ archivePrefix={arXiv},
+ primaryClass={eess.IV},
+ url={https://arxiv.org/abs/2502.03491},
+}
+```
+
+
+
+
+
+
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e0dd183865ded4318deed81f22b669332d09628
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,16 @@
+"""Top-level package for LanPaint."""
+
+__all__ = [
+ "NODE_CLASS_MAPPINGS",
+ "NODE_DISPLAY_NAME_MAPPINGS",
+ "WEB_DIRECTORY",
+]
+
+__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
+
+WEB_DIRECTORY = "./web"
diff --git a/example_workflows/Masked Qwen Image Edit.json b/example_workflows/Masked Qwen Image Edit.json
new file mode 100644
index 0000000000000000000000000000000000000000..f400bdd241d9d48317efab218c5c1a55b9df7401
--- /dev/null
+++ b/example_workflows/Masked Qwen Image Edit.json
@@ -0,0 +1,1367 @@
+{
+ "id": "91f6bbe2-ed41-4fd6-bac7-71d5b5864ecb",
+ "revision": 0,
+ "last_node_id": 141,
+ "last_link_id": 293,
+ "nodes": [
+ {
+ "id": 99,
+ "type": "MarkdownNote",
+ "pos": [
+ -830,
+ -10
+ ],
+ "size": [
+ 540,
+ 550
+ ],
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [],
+ "title": "Model links",
+ "properties": {
+ "widget_ue_connectable": {}
+ },
+ "widgets_values": [
+ "[Tutorial](https://docs.comfy.org/tutorials/image/qwen/qwen-image-edit) | [教程](https://docs.comfy.org/zh-CN/tutorials/image/qwen/qwen-image-edit)\n\n\n## Model links\n\nYou can find all the models on [Comfy-Org/Qwen-Image_ComfyUI](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main) and [Comfy-Org/Qwen-Image-Edit_ComfyUI](https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI) \n\n**Diffusion model**\n\n- [qwen_image_edit_fp8_e4m3fn.safetensors](https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_fp8_e4m3fn.safetensors)\n\n**LoRA**\n\n- [Qwen-Image-Lightning-4steps-V1.0.safetensors](https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0.safetensors)\n\n**Text encoder**\n\n- [qwen_2.5_vl_7b_fp8_scaled.safetensors](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors)\n\n**VAE**\n\n- [qwen_image_vae.safetensors](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors)\n\nModel Storage Location\n\n```\n📂 ComfyUI/\n├── 📂 models/\n│ ├── 📂 diffusion_models/\n│ │ └── qwen_image_edit_fp8_e4m3fn.safetensors\n│ ├── 📂 loras/\n│ │ └── Qwen-Image-Lightning-4steps-V1.0.safetensors\n│ ├── 📂 vae/\n│ │ └── qwen_image_vae.safetensors\n│ └── 📂 text_encoders/\n│ └── qwen_2.5_vl_7b_fp8_scaled.safetensors\n```\n"
+ ],
+ "color": "#432",
+ "bgcolor": "#653"
+ },
+ {
+ "id": 126,
+ "type": "VAELoader",
+ "pos": [
+ -249.80447387695312,
+ 337.1279296875
+ ],
+ "size": [
+ 330,
+ 60
+ ],
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "VAE",
+ "type": "VAE",
+ "slot_index": 0,
+ "links": [
+ 256,
+ 258,
+ 276,
+ 288
+ ]
+ }
+ ],
+ "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": 128,
+ "type": "TextEncodeQwenImageEdit",
+ "pos": [
+ 137.7421112060547,
+ 395.8420104980469
+ ],
+ "size": [
+ 360,
+ 150
+ ],
+ "flags": {},
+ "order": 14,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "clip",
+ "type": "CLIP",
+ "link": 257
+ },
+ {
+ "name": "vae",
+ "shape": 7,
+ "type": "VAE",
+ "link": 258
+ },
+ {
+ "name": "image",
+ "shape": 7,
+ "type": "IMAGE",
+ "link": 283
+ }
+ ],
+ "outputs": [
+ {
+ "name": "CONDITIONING",
+ "type": "CONDITIONING",
+ "links": [
+ 268
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "TextEncodeQwenImageEdit",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "prompt": true
+ }
+ }
+ },
+ "widgets_values": [
+ ""
+ ],
+ "color": "#223",
+ "bgcolor": "#335"
+ },
+ {
+ "id": 129,
+ "type": "ModelSamplingAuraFlow",
+ "pos": [
+ 550.1956176757812,
+ 27.127967834472656
+ ],
+ "size": [
+ 290,
+ 60
+ ],
+ "flags": {},
+ "order": 16,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "model",
+ "type": "MODEL",
+ "link": 293
+ }
+ ],
+ "outputs": [
+ {
+ "name": "MODEL",
+ "type": "MODEL",
+ "links": [
+ 260
+ ]
+ }
+ ],
+ "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
+ ]
+ },
+ {
+ "id": 130,
+ "type": "CFGNorm",
+ "pos": [
+ 550.1956176757812,
+ 137.1279754638672
+ ],
+ "size": [
+ 290,
+ 60
+ ],
+ "flags": {},
+ "order": 18,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "model",
+ "type": "MODEL",
+ "link": 260
+ }
+ ],
+ "outputs": [
+ {
+ "name": "patched_model",
+ "type": "MODEL",
+ "links": [
+ 269
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "CFGNorm",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "strength": true
+ }
+ }
+ },
+ "widgets_values": [
+ 1
+ ]
+ },
+ {
+ "id": 135,
+ "type": "VAEEncode",
+ "pos": [
+ 248.32186889648438,
+ 647.2574462890625
+ ],
+ "size": [
+ 140,
+ 46
+ ],
+ "flags": {},
+ "order": 12,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pixels",
+ "type": "IMAGE",
+ "link": 274
+ },
+ {
+ "name": "vae",
+ "type": "VAE",
+ "link": 276
+ }
+ ],
+ "outputs": [
+ {
+ "name": "LATENT",
+ "type": "LATENT",
+ "links": [
+ 272
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "VAEEncode",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {}
+ }
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 131,
+ "type": "ImageScaleToTotalPixels",
+ "pos": [
+ -198.91114807128906,
+ 893.8960571289062
+ ],
+ "size": [
+ 270,
+ 82
+ ],
+ "flags": {},
+ "order": 9,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 275
+ }
+ ],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 274,
+ 282,
+ 283,
+ 289
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "ImageScaleToTotalPixels",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "upscale_method": true,
+ "megapixels": true
+ }
+ }
+ },
+ "widgets_values": [
+ "nearest-exact",
+ 1
+ ]
+ },
+ {
+ "id": 102,
+ "type": "LanPaint_KSampler",
+ "pos": [
+ 916.4383544921875,
+ 29.786056518554688
+ ],
+ "size": [
+ 400,
+ 572
+ ],
+ "flags": {},
+ "order": 20,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "model",
+ "type": "MODEL",
+ "link": 269
+ },
+ {
+ "name": "positive",
+ "type": "CONDITIONING",
+ "link": 267
+ },
+ {
+ "name": "negative",
+ "type": "CONDITIONING",
+ "link": 268
+ },
+ {
+ "name": "latent_image",
+ "type": "LATENT",
+ "link": 284
+ }
+ ],
+ "outputs": [
+ {
+ "name": "LATENT",
+ "type": "LATENT",
+ "links": [
+ 287
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "LanPaint",
+ "ver": "453b6c090e400123bb82f5bc0c97854031b09274",
+ "Node name for S&R": "LanPaint_KSampler"
+ },
+ "widgets_values": [
+ 0,
+ "fixed",
+ 20,
+ 2.5,
+ "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": 110,
+ "type": "LoadImage",
+ "pos": [
+ -207.1995086669922,
+ 499.05206298828125
+ ],
+ "size": [
+ 274.080078125,
+ 314.0000305175781
+ ],
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 275
+ ]
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [
+ 277
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "LoadImage",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "image": true,
+ "upload": true
+ }
+ }
+ },
+ "widgets_values": [
+ "clipspace/clipspace-painted-masked-23308506.png [input]",
+ "image"
+ ]
+ },
+ {
+ "id": 127,
+ "type": "TextEncodeQwenImageEdit",
+ "pos": [
+ 140.195556640625,
+ 207.12796020507812
+ ],
+ "size": [
+ 360,
+ 150
+ ],
+ "flags": {},
+ "order": 13,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "clip",
+ "type": "CLIP",
+ "link": 255
+ },
+ {
+ "name": "vae",
+ "shape": 7,
+ "type": "VAE",
+ "link": 256
+ },
+ {
+ "name": "image",
+ "shape": 7,
+ "type": "IMAGE",
+ "link": 282
+ }
+ ],
+ "outputs": [
+ {
+ "name": "CONDITIONING",
+ "type": "CONDITIONING",
+ "links": [
+ 267
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "TextEncodeQwenImageEdit",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "prompt": true
+ }
+ }
+ },
+ "widgets_values": [
+ "change the girl's cloth to red evening gown "
+ ],
+ "color": "#232",
+ "bgcolor": "#353"
+ },
+ {
+ "id": 138,
+ "type": "LanPaint_MaskBlend",
+ "pos": [
+ 1746.5638427734375,
+ 60.70713806152344
+ ],
+ "size": [
+ 210,
+ 98
+ ],
+ "flags": {},
+ "order": 22,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image1",
+ "type": "IMAGE",
+ "link": 289
+ },
+ {
+ "name": "image2",
+ "type": "IMAGE",
+ "link": 286
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 290
+ }
+ ],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 291
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "LanPaint",
+ "ver": "4d3d5d17f0105b673df92da5b084cce567c9c712",
+ "Node name for S&R": "LanPaint_MaskBlend"
+ },
+ "widgets_values": [
+ 9
+ ]
+ },
+ {
+ "id": 137,
+ "type": "VAEDecode",
+ "pos": [
+ 1456.255615234375,
+ 0.358134925365448
+ ],
+ "size": [
+ 210,
+ 46
+ ],
+ "flags": {},
+ "order": 21,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "samples",
+ "type": "LATENT",
+ "link": 287
+ },
+ {
+ "name": "vae",
+ "type": "VAE",
+ "link": 288
+ }
+ ],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "slot_index": 0,
+ "links": [
+ 286
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.23",
+ "Node name for S&R": "VAEDecode"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 104,
+ "type": "SaveImage",
+ "pos": [
+ 2063.178955078125,
+ 10.873106002807617
+ ],
+ "size": [
+ 311.2532653808594,
+ 484.7096252441406
+ ],
+ "flags": {},
+ "order": 23,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 291
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.23"
+ },
+ "widgets_values": [
+ "ComfyUI"
+ ]
+ },
+ {
+ "id": 136,
+ "type": "MaskToImage",
+ "pos": [
+ -125.77210235595703,
+ 1047.446533203125
+ ],
+ "size": [
+ 184.62362670898438,
+ 26
+ ],
+ "flags": {},
+ "order": 10,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 277
+ }
+ ],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 270
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.51",
+ "Node name for S&R": "MaskToImage"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 133,
+ "type": "ImageToMask",
+ "pos": [
+ -196.46453857421875,
+ 1267.0250244140625
+ ],
+ "size": [
+ 270,
+ 58
+ ],
+ "flags": {},
+ "order": 17,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 271
+ }
+ ],
+ "outputs": [
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [
+ 273,
+ 290
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.51",
+ "Node name for S&R": "ImageToMask"
+ },
+ "widgets_values": [
+ "red"
+ ]
+ },
+ {
+ "id": 132,
+ "type": "ImageScaleToTotalPixels",
+ "pos": [
+ -191.1123046875,
+ 1125.3765869140625
+ ],
+ "size": [
+ 270,
+ 82
+ ],
+ "flags": {},
+ "order": 15,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 270
+ }
+ ],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 271
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "ImageScaleToTotalPixels",
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "upscale_method": true,
+ "megapixels": true
+ }
+ }
+ },
+ "widgets_values": [
+ "nearest-exact",
+ 1
+ ]
+ },
+ {
+ "id": 134,
+ "type": "SetLatentNoiseMask",
+ "pos": [
+ 425.3291931152344,
+ 644.5993041992188
+ ],
+ "size": [
+ 180.74765014648438,
+ 46
+ ],
+ "flags": {},
+ "order": 19,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "samples",
+ "type": "LATENT",
+ "link": 272
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 273
+ }
+ ],
+ "outputs": [
+ {
+ "name": "LATENT",
+ "type": "LATENT",
+ "links": [
+ 284
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.51",
+ "Node name for S&R": "SetLatentNoiseMask"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 96,
+ "type": "MarkdownNote",
+ "pos": [
+ -205.49290466308594,
+ 1360.4835205078125
+ ],
+ "size": [
+ 280,
+ 88
+ ],
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [],
+ "properties": {},
+ "widgets_values": [
+ "This node is to avoid poor output results caused by excessively large input image sizes."
+ ],
+ "color": "#432",
+ "bgcolor": "#653"
+ },
+ {
+ "id": 139,
+ "type": "MarkdownNote",
+ "pos": [
+ -544.4594116210938,
+ 908.2171630859375
+ ],
+ "size": [
+ 280,
+ 88
+ ],
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [],
+ "properties": {},
+ "widgets_values": [
+ "If you have multiple of 8 issue, replace Scale Image to total pixels with Upscale Image and manually set the size."
+ ],
+ "color": "#432",
+ "bgcolor": "#653"
+ },
+ {
+ "id": 97,
+ "type": "MarkdownNote",
+ "pos": [
+ 960.13525390625,
+ 776.3380737304688
+ ],
+ "size": [
+ 300,
+ 190
+ ],
+ "flags": {},
+ "order": 5,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [],
+ "title": "KSampler settings",
+ "properties": {},
+ "widgets_values": [
+ "You can test and find the best setting by yourself. The following table is for reference.\n\n| Model | Steps | CFG |\n|---------------------|---------------|---------------|\n| Offical | 50 | 4.0 \n| fp8_e4m3fn | 20 | 2.5 |\n| fp8_e4m3fn + 4steps LoRA | 4 | 1.0 |\n"
+ ],
+ "color": "#432",
+ "bgcolor": "#653"
+ },
+ {
+ "id": 124,
+ "type": "UNETLoader",
+ "pos": [
+ -249.80447387695312,
+ 37.12797164916992
+ ],
+ "size": [
+ 330,
+ 90
+ ],
+ "flags": {},
+ "order": 6,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "MODEL",
+ "type": "MODEL",
+ "slot_index": 0,
+ "links": [
+ 292
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.48",
+ "Node name for S&R": "UNETLoader",
+ "models": [
+ {
+ "name": "qwen_image_edit_fp8_e4m3fn.safetensors",
+ "url": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_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_edit_fp8_e4m3fn.safetensors",
+ "default"
+ ]
+ },
+ {
+ "id": 141,
+ "type": "LoraLoaderModelOnly",
+ "pos": [
+ 199.41346740722656,
+ 19.401098251342773
+ ],
+ "size": [
+ 270,
+ 82
+ ],
+ "flags": {},
+ "order": 11,
+ "mode": 4,
+ "inputs": [
+ {
+ "name": "model",
+ "type": "MODEL",
+ "link": 292
+ }
+ ],
+ "outputs": [
+ {
+ "name": "MODEL",
+ "type": "MODEL",
+ "links": [
+ 293
+ ]
+ }
+ ],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.3.50",
+ "Node name for S&R": "LoraLoaderModelOnly",
+ "models": [
+ {
+ "name": "Qwen-Image-Lightning-4steps-V1.0.safetensors",
+ "url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0.safetensors",
+ "directory": "loras"
+ }
+ ],
+ "enableTabs": false,
+ "tabWidth": 65,
+ "tabXOffset": 10,
+ "hasSecondTab": false,
+ "secondTabText": "Send Back",
+ "secondTabOffset": 80,
+ "secondTabWidth": 65,
+ "ue_properties": {
+ "widget_ue_connectable": {
+ "lora_name": true,
+ "strength_model": true
+ }
+ }
+ },
+ "widgets_values": [
+ "Qwen-Image-Lightning-4steps-V1.0.safetensors",
+ 1
+ ]
+ },
+ {
+ "id": 125,
+ "type": "CLIPLoader",
+ "pos": [
+ -249.80447387695312,
+ 177.12796020507812
+ ],
+ "size": [
+ 330,
+ 110
+ ],
+ "flags": {},
+ "order": 7,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "CLIP",
+ "type": "CLIP",
+ "slot_index": 0,
+ "links": [
+ 255,
+ 257
+ ]
+ }
+ ],
+ "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": 140,
+ "type": "MarkdownNote",
+ "pos": [
+ 1334.337646484375,
+ 368.5771484375
+ ],
+ "size": [
+ 300,
+ 190
+ ],
+ "flags": {},
+ "order": 8,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [],
+ "title": "KSampler settings",
+ "properties": {},
+ "widgets_values": [
+ "Decrease **LanPaint_NumSteps** for faster generation. \n"
+ ],
+ "color": "#432",
+ "bgcolor": "#653"
+ }
+ ],
+ "links": [
+ [
+ 255,
+ 125,
+ 0,
+ 127,
+ 0,
+ "CLIP"
+ ],
+ [
+ 256,
+ 126,
+ 0,
+ 127,
+ 1,
+ "VAE"
+ ],
+ [
+ 257,
+ 125,
+ 0,
+ 128,
+ 0,
+ "CLIP"
+ ],
+ [
+ 258,
+ 126,
+ 0,
+ 128,
+ 1,
+ "VAE"
+ ],
+ [
+ 260,
+ 129,
+ 0,
+ 130,
+ 0,
+ "MODEL"
+ ],
+ [
+ 267,
+ 127,
+ 0,
+ 102,
+ 1,
+ "CONDITIONING"
+ ],
+ [
+ 268,
+ 128,
+ 0,
+ 102,
+ 2,
+ "CONDITIONING"
+ ],
+ [
+ 269,
+ 130,
+ 0,
+ 102,
+ 0,
+ "MODEL"
+ ],
+ [
+ 270,
+ 136,
+ 0,
+ 132,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 271,
+ 132,
+ 0,
+ 133,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 272,
+ 135,
+ 0,
+ 134,
+ 0,
+ "LATENT"
+ ],
+ [
+ 273,
+ 133,
+ 0,
+ 134,
+ 1,
+ "MASK"
+ ],
+ [
+ 274,
+ 131,
+ 0,
+ 135,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 275,
+ 110,
+ 0,
+ 131,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 276,
+ 126,
+ 0,
+ 135,
+ 1,
+ "VAE"
+ ],
+ [
+ 277,
+ 110,
+ 1,
+ 136,
+ 0,
+ "MASK"
+ ],
+ [
+ 282,
+ 131,
+ 0,
+ 127,
+ 2,
+ "IMAGE"
+ ],
+ [
+ 283,
+ 131,
+ 0,
+ 128,
+ 2,
+ "IMAGE"
+ ],
+ [
+ 284,
+ 134,
+ 0,
+ 102,
+ 3,
+ "LATENT"
+ ],
+ [
+ 286,
+ 137,
+ 0,
+ 138,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 287,
+ 102,
+ 0,
+ 137,
+ 0,
+ "LATENT"
+ ],
+ [
+ 288,
+ 126,
+ 0,
+ 137,
+ 1,
+ "VAE"
+ ],
+ [
+ 289,
+ 131,
+ 0,
+ 138,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 290,
+ 133,
+ 0,
+ 138,
+ 2,
+ "MASK"
+ ],
+ [
+ 291,
+ 138,
+ 0,
+ 104,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 292,
+ 124,
+ 0,
+ 141,
+ 0,
+ "MODEL"
+ ],
+ [
+ 293,
+ 141,
+ 0,
+ 129,
+ 0,
+ "MODEL"
+ ]
+ ],
+ "groups": [
+ {
+ "id": 1,
+ "title": "Step1 - Load models",
+ "bounding": [
+ -270,
+ -40,
+ 370,
+ 450
+ ],
+ "color": "#3f789e",
+ "font_size": 24,
+ "flags": {}
+ },
+ {
+ "id": 2,
+ "title": "Step 2 - Upload image for editing",
+ "bounding": [
+ -270,
+ 430,
+ 370,
+ 400
+ ],
+ "color": "#3f789e",
+ "font_size": 24,
+ "flags": {}
+ },
+ {
+ "id": 3,
+ "title": "Step 3 - Prompt",
+ "bounding": [
+ 130,
+ 130,
+ 380,
+ 433.6000061035156
+ ],
+ "color": "#3f789e",
+ "font_size": 24,
+ "flags": {}
+ }
+ ],
+ "config": {},
+ "extra": {
+ "ds": {
+ "scale": 0.40914901834711453,
+ "offset": [
+ 1600.1095769779836,
+ 518.9695463016635
+ ]
+ },
+ "frontendVersion": "1.25.9",
+ "ue_links": [],
+ "links_added_by_ue": [],
+ "VHS_latentpreview": false,
+ "VHS_latentpreviewrate": 0,
+ "VHS_MetadataImage": true,
+ "VHS_KeepIntermediate": true,
+ "groupNodes": {}
+ },
+ "version": 0.4
+}
\ No newline at end of file
diff --git a/example_workflows/Qwen Image Inpaint.json b/example_workflows/Qwen Image Inpaint.json
new file mode 100644
index 0000000000000000000000000000000000000000..f908921ed1f280b233b2af923d0c8fc8b97edcf0
--- /dev/null
+++ b/example_workflows/Qwen Image Inpaint.json
@@ -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
+}
\ No newline at end of file
diff --git a/example_workflows/Qwen Image Outpaint.json b/example_workflows/Qwen Image Outpaint.json
new file mode 100644
index 0000000000000000000000000000000000000000..81a221c655737505112cf99f99a7ac575cfe3378
--- /dev/null
+++ b/example_workflows/Qwen Image Outpaint.json
@@ -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
+}
\ No newline at end of file
diff --git a/example_workflows/SDXL Inpaint.json b/example_workflows/SDXL Inpaint.json
new file mode 100644
index 0000000000000000000000000000000000000000..04449ff4c8095d65c428997f305cd9246edd64c7
--- /dev/null
+++ b/example_workflows/SDXL Inpaint.json
@@ -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
+}
\ No newline at end of file
diff --git a/example_workflows/SDXL_InPaint.png b/example_workflows/SDXL_InPaint.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd40e9236e80b5078945e4af1821b4adf456a9ae
--- /dev/null
+++ b/example_workflows/SDXL_InPaint.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2783ed490ec6d2a4cc7f79d7b40383dec3f893920c49f74cebbea8515afde775
+size 1475758
diff --git a/examples/Example_1/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_1/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..a52f36b9d6fcd269f45e819d1b5353402e60853e
--- /dev/null
+++ b/examples/Example_1/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c12871ed1f7240652ea234dbb2b2ee05639d239b373d316c45de7e209de472bd
+size 1641900
diff --git a/examples/Example_1/Masked_Load_Me_in_Loader.png b/examples/Example_1/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..87c6a39a2c6b0935eb8486e89a726dd7f13f0bec
--- /dev/null
+++ b/examples/Example_1/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0fb2cf92b2d13e70914750775cd665ebf6925177aea57d2c7c11053de629323c
+size 1816744
diff --git a/examples/Example_1/Original_No_Mask.png b/examples/Example_1/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc3c1eee0df5542279a98d89cb5248319094224b
--- /dev/null
+++ b/examples/Example_1/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:01fb3adef12243ad4995de22b0b18e183f1a54c22d4bde91cd06d0656f2b393a
+size 1584211
diff --git a/examples/Example_10/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_10/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..50d9d4980015a2c6ddda7f4db3acbfb96895bda4
--- /dev/null
+++ b/examples/Example_10/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1da4fc96bd3b1f8fade0b2ae5c1048c05b133cab7fa280beb3441df2f522bf7
+size 1651846
diff --git a/examples/Example_10/Masked_Load_Me_in_Loader.png b/examples/Example_10/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..27febd79fc064d2f0ec3bcb9510b20b457724651
--- /dev/null
+++ b/examples/Example_10/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f7963487802b3ff300012e253730de79c6669fde8467f71223c4490a9ea6bee3
+size 1333530
diff --git a/examples/Example_11/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_11/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..0ee79f0dbf2ee6311532c24c913362e2c65905a5
--- /dev/null
+++ b/examples/Example_11/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e09023e36178ffe2cc808533832d7dc44866ad81bfe57c03f07ec003b8556cc
+size 2658600
diff --git a/examples/Example_11/Masked_Load_Me_in_Loader.png b/examples/Example_11/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..357153d6b455b1afd9db0f650f7ec9dac23db487
--- /dev/null
+++ b/examples/Example_11/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64f301dee695b08e2c248de8344f66aa5f01de7cc1c99b7d786410a9ac1ae01e
+size 2992000
diff --git a/examples/Example_12/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_12/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..7cd480c7b71770628d2e282e0dd0f695457374b2
--- /dev/null
+++ b/examples/Example_12/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b527f6bf1f7d218580928c62fed68af97dc272a8e726931da5acd5a6bdacc048
+size 1796016
diff --git a/examples/Example_12/Masked_Load_Me_in_Loader.png b/examples/Example_12/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac316e51e9b4adc5f125c63240cd6b082a49106c
--- /dev/null
+++ b/examples/Example_12/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7be87e02c72d9701f6c86bbfff31dcd46109d58e387c0297ddd2099c13c51a94
+size 850264
diff --git a/examples/Example_13/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_13/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..8d4862f9cc906c42cfb6eba1e80f49307e60a7fb
--- /dev/null
+++ b/examples/Example_13/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:667a6a059899c5a75ddac29e2a629775dcf54812a5373c88a23bbb5a6a951db2
+size 1979107
diff --git a/examples/Example_13/Masked_Load_Me_in_Loader.png b/examples/Example_13/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..bfd20e873b4b091c9f5f0f9f7e619bf9e5e37e13
--- /dev/null
+++ b/examples/Example_13/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e780881a5bcee55014f2269d4a14a5d8e47eaaa4050c63aa5316d9ca41a95ad6
+size 2262963
diff --git a/examples/Example_13/Original_No_Mask.png b/examples/Example_13/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b287cfeef7c4ace62e1e9b727c2b10863527bc9
--- /dev/null
+++ b/examples/Example_13/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:36645d07d92ea0a9ec14e5bb6d9f43494ef52850c00a94a3316f1ca4c6dfb339
+size 2253703
diff --git a/examples/Example_14/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_14/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8bfe1ffca8a5067c07a0f4bae783f57860b107d
--- /dev/null
+++ b/examples/Example_14/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:98281486dd997258d69974892e4322ad75b8734af247c9fdc52c7934b92e2ce6
+size 1398145
diff --git a/examples/Example_14/Masked_Load_Me_in_Loader.png b/examples/Example_14/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..2dc40321763ae9c33919bfb0b517f04ca0f2faf6
--- /dev/null
+++ b/examples/Example_14/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc5bd8fedd04343860d1b596b9c192c33c0c4c2d9c6638effe9d4df8f4ab7a9a
+size 2634040
diff --git a/examples/Example_14/Original_No_Mask.png b/examples/Example_14/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..8210a209d8818bd9692b6b66f5be2f9394a44896
--- /dev/null
+++ b/examples/Example_14/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c0cc49737b1494e8fce8ab0e180b98de73d9e751821e76baa7a37cff639f3e3e
+size 2301687
diff --git a/examples/Example_2/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_2/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc8f7eaf70be0d21487afd9336bcff7edd4b2e87
--- /dev/null
+++ b/examples/Example_2/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb478ff2bdc9a41cd2914ae7068d94bce509e671839b33655728c2b53825b769
+size 1497073
diff --git a/examples/Example_2/Masked_Load_Me_in_Loader.png b/examples/Example_2/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a8f7632296762c12ae833487f7e9b9a828670bb
--- /dev/null
+++ b/examples/Example_2/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e95dc65b370557b47053a07881736edb1332c87e2bb737a9feb5ad4e534c457b
+size 1680231
diff --git a/examples/Example_2/Original_No_Mask.png b/examples/Example_2/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..6127c0cd0273f3b1676e47b1e6e28825109eaff3
--- /dev/null
+++ b/examples/Example_2/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:828b21fbd0c062c73a9f22a7d89555342eb7515ae8347ca8cc0aadb15fe74f8d
+size 1481450
diff --git a/examples/Example_3/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_3/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ed7c308ddd007d963555dfcbe996adb354b04a6
--- /dev/null
+++ b/examples/Example_3/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a66153a1988aeb9b1f880f35badee4f46a8f4ed35cc1f29d08855f9c8036801
+size 1300442
diff --git a/examples/Example_3/Masked_Load_Me_in_Loader.png b/examples/Example_3/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..65ff51d93b46d6336824d8572fe9cf2d37139ebf
--- /dev/null
+++ b/examples/Example_3/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:038d3eb9f8592408b28c209af97927d7dc4ac543ddf1b3d7a560f81311dddea3
+size 1480882
diff --git a/examples/Example_3/Original_No_Mask.png b/examples/Example_3/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..937c18e81cab21a5bf2be568dad22813731bf55b
--- /dev/null
+++ b/examples/Example_3/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46eb04f9c7387ce4befe7b0cdc42a2d7f2c332b68a2b4e6c382bd06fdc6350a9
+size 1292047
diff --git a/examples/Example_4/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_4/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..afaabbe9e61c60028fd82b11465ccb6c6a2afe45
--- /dev/null
+++ b/examples/Example_4/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efb4fff6cfac048a97227fef52bfb34cdbbd53f1c74db25982f8c59bedc2f102
+size 1036191
diff --git a/examples/Example_4/Masked_Load_Me_in_Loader.png b/examples/Example_4/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..a75be258255bb9ebdb8e12f9c6d11347b25d0e41
--- /dev/null
+++ b/examples/Example_4/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33c42e51d999ae6442d2a4340a5fab4fd31e7ca430d92e8fe04fd633ef209897
+size 1185754
diff --git a/examples/Example_4/Original_No_Mask.png b/examples/Example_4/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a0c4b731e00c7f7723f019cf536db88193b710a
--- /dev/null
+++ b/examples/Example_4/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:950db549a240c9376a8e3f65a912777b6589206f936757d1573aca5fbb2c5f91
+size 1015973
diff --git a/examples/Example_5/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_5/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ca217ca73ebae8d465a07668c021c319006623b
--- /dev/null
+++ b/examples/Example_5/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:458af733e71700aba445835427e6b80a185a90c68eb095d506c882dbb52cffad
+size 1079917
diff --git a/examples/Example_5/Masked_Load_Me_in_Loader.png b/examples/Example_5/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc2219fc2082e56eb80fa952aa521afd98628f7d
--- /dev/null
+++ b/examples/Example_5/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33a992feb239126c73bd142a34d77f65db14d3b7ea42322248fc8ec0408700af
+size 1188980
diff --git a/examples/Example_5/Original_No_Mask.png b/examples/Example_5/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a0c4b731e00c7f7723f019cf536db88193b710a
--- /dev/null
+++ b/examples/Example_5/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:950db549a240c9376a8e3f65a912777b6589206f936757d1573aca5fbb2c5f91
+size 1015973
diff --git a/examples/Example_6/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_6/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..5988476ba00884976c8dce07c63a32c49fee4c86
--- /dev/null
+++ b/examples/Example_6/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:61abc7eddea2d3f84c577f915ee0b8e197de7289800ff87e2a043aa62adc52a9
+size 1120740
diff --git a/examples/Example_6/Masked_Load_Me_in_Loader.png b/examples/Example_6/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..8964ff901332b7e953619b2d369b3f032a660796
--- /dev/null
+++ b/examples/Example_6/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fffdf28a6e513626bfe9b73481d2f47d7d8efabd8ef292a12d47d34eba4841e8
+size 876447
diff --git a/examples/Example_6/Original_No_Mask.png b/examples/Example_6/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..32dcd212d309a563dbeb7eee35b312ebce4af904
--- /dev/null
+++ b/examples/Example_6/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:09cc741530696d65c65e04bb37d6a9aae347a810e7bc7a975ad4df3e28fec610
+size 824464
diff --git a/examples/Example_7/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_7/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef3f853dc17f02baa4b3d1b0ef018b29ff633a94
--- /dev/null
+++ b/examples/Example_7/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5dd0733de4c62366cc26f3bab36c440a1ddb1c52b4cbb4708cfa1e5e86fda4e0
+size 1217100
diff --git a/examples/Example_7/Masked_Load_Me_in_Loader.png b/examples/Example_7/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..c222c91023d8bf2c51de01c4ad7e9f8425b40c12
--- /dev/null
+++ b/examples/Example_7/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1f93f154770605f1e194ef62f82829b811e0d709971437dfe5786c62a1b8c79
+size 1427163
diff --git a/examples/Example_7/Original_No_Mask.png b/examples/Example_7/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef45fa17a59e796c5489bdc445ebfa7cc4dbf3e6
--- /dev/null
+++ b/examples/Example_7/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8d2c5c79e557a45fde49bec20ca03efe8e94d171e3c0efda97f1ea46a6645716
+size 1401363
diff --git a/examples/Example_8/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_8/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1c6b3cc95886566787ff7d1f0e8114bb137fb8e
--- /dev/null
+++ b/examples/Example_8/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2019812155f293b55af748db3474fc97d6f199d449b1c4044afb28ecaebef246
+size 1459253
diff --git a/examples/Example_8/Masked_Load_Me_in_Loader.png b/examples/Example_8/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..524f3a076559aeca92589daee4b9e2c2d5584dfa
--- /dev/null
+++ b/examples/Example_8/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:467139b9e026728a6e998bfb19cc0b42320765c23db954329a85092314de2292
+size 1616700
diff --git a/examples/Example_8/Original_No_Mask.png b/examples/Example_8/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..239ca9462026a494c8d087b4eb42c87592239d4f
--- /dev/null
+++ b/examples/Example_8/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1066c4b73b56adb997381af064e11c804f9474d01a51db4375f6662bae7352dc
+size 1389123
diff --git a/examples/Example_9/InPainted_Drag_Me_to_ComfyUI.png b/examples/Example_9/InPainted_Drag_Me_to_ComfyUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..88be726ec7139160893c485cbdd6e4b9bdc2fa85
--- /dev/null
+++ b/examples/Example_9/InPainted_Drag_Me_to_ComfyUI.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:75730c8781dd8acfe8b0281df4a0d20d4ca1b58dfc51b309051018aac793925f
+size 1421249
diff --git a/examples/Example_9/Masked_Load_Me_in_Loader.png b/examples/Example_9/Masked_Load_Me_in_Loader.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c0929106a8c2a127533a5cf00194e775edc956d
--- /dev/null
+++ b/examples/Example_9/Masked_Load_Me_in_Loader.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2920e791b922d8bbc901828731c0d72b4f1383d7a077b0c936f08525a7a0aad3
+size 1619485
diff --git a/examples/Example_9/Original_No_Mask.png b/examples/Example_9/Original_No_Mask.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0ba803fd8d8191486377b9490f1023a7d04a9c3
--- /dev/null
+++ b/examples/Example_9/Original_No_Mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:049bb0acff6af3ae1a7f43acf5309a15fbffdc352d7afd8b0f03443fe9254891
+size 1379780
diff --git a/examples/InpaintChara_04.jpg b/examples/InpaintChara_04.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e101592004d7df88278f9d72836437533da622ff
--- /dev/null
+++ b/examples/InpaintChara_04.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a196396cab7b98160dd007504b8036b70c8432323c93216747c50dd4e802a8bd
+size 719312
diff --git a/examples/InpaintChara_05.jpg b/examples/InpaintChara_05.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b33b0b0b93b931d45c1ad291616fd9dd4d512c30
--- /dev/null
+++ b/examples/InpaintChara_05.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f23ac797e3e60c56ac43d32001fd34b47360b7680aa826ab35c67b2b4ae0fbef
+size 659507
diff --git a/examples/InpaintChara_06.jpg b/examples/InpaintChara_06.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..50c1a071d3fdd8938407c8c6bf3ef66cd563bd31
--- /dev/null
+++ b/examples/InpaintChara_06.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:921db97a029150a8f075bbd1e396db11c9ec5e27daefa7d70b652e415445876e
+size 365684
diff --git a/examples/InpaintChara_07.jpg b/examples/InpaintChara_07.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..308785f544b9572c5dae1ed9e78ae75cc52bcf07
--- /dev/null
+++ b/examples/InpaintChara_07.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f139ace3aa5f120fe0e7264df9313fa992837c0c4b8ef82e66c4aa15e828e6c7
+size 357061
diff --git a/examples/InpaintChara_08.jpg b/examples/InpaintChara_08.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6bfa125688005dcb60a0e16e2513d2a87f0a15fd
--- /dev/null
+++ b/examples/InpaintChara_08.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58e2947531a63a9cbbccd5f088e14625da5f3af311265ec4af42ea78b8bdb0e6
+size 388017
diff --git a/examples/InpaintChara_09.jpg b/examples/InpaintChara_09.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..7df88c5bd010403f16963d2d217b4103936f86a3
--- /dev/null
+++ b/examples/InpaintChara_09.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47e04ff8f5fd566c6b5eb4e3fe366fa3cd9c6773031b00b4fb75f740be81ee0d
+size 406887
diff --git a/examples/InpaintChara_10.jpg b/examples/InpaintChara_10.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fdf5a481394470ede00e838698045cc627d4318b
--- /dev/null
+++ b/examples/InpaintChara_10.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:34035b0b3104ac0e880897d4f5e69ebaf25e06554affdf5d59ad78766634e246
+size 562381
diff --git a/examples/InpaintChara_11.jpg b/examples/InpaintChara_11.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..12cebdf8ebc4d194d3cac8b587630d02f66267e0
--- /dev/null
+++ b/examples/InpaintChara_11.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58c66c4cb6076fbd519d00a87e4531e649e22c8feeae733bde630bba1e020f60
+size 620859
diff --git a/examples/InpaintChara_12.jpg b/examples/InpaintChara_12.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..51c93c4f15fa8ff9b39366437f4e31478dca6fa5
--- /dev/null
+++ b/examples/InpaintChara_12.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7233fcdef65ca503f15df459a58ff6e8212aeb8042d20f6e1f044aeb07eff8e3
+size 477936
diff --git a/examples/InpaintChara_13(1).jpg b/examples/InpaintChara_13(1).jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e56b7bad44dc9dd919d1cfad06159a6e7bdc65df
--- /dev/null
+++ b/examples/InpaintChara_13(1).jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dddc9fbddc3134a2dd3888fa22f3b1214427bf2b64dd1814ad728bd9a8cb4c61
+size 416620
diff --git a/examples/InpaintChara_13.jpg b/examples/InpaintChara_13.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a392230d873331a9432f26c1a7c26a65057c0189
--- /dev/null
+++ b/examples/InpaintChara_13.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc4841156fbd83551f79fbedcd0f2746da6e245fdae501f6d5069c35977605ae
+size 800349
diff --git a/examples/InpaintChara_14.jpg b/examples/InpaintChara_14.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..52df8412a8d84feb648839ca65612fd7ab7ffc2c
--- /dev/null
+++ b/examples/InpaintChara_14.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af8584ecb9f19c65708786b07e0e51feaab5cffc6738c8af50195b8cc74348a7
+size 266479
diff --git a/examples/LanPaintQwen_01.jpg b/examples/LanPaintQwen_01.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..cfd594d5e5ed063cfa4e7fe36dad5bdfb284108e
--- /dev/null
+++ b/examples/LanPaintQwen_01.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:467e970267d7cb64a649f063ac566dfd9642f8400ba337f5a9d331e8a4026254
+size 298489
diff --git a/examples/LanPaintQwen_03.jpg b/examples/LanPaintQwen_03.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..28d0a278dd62e7deeefd50e50a4cfbd432d9e6b5
--- /dev/null
+++ b/examples/LanPaintQwen_03.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c23295f65a0db516994a581aa26a72c02eb2891a8f28206aa44de35dd0fa308e
+size 408341
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..c1f3b4163f58f150b9c10f83b57d2185ea2e9159
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,79 @@
+[build-system]
+requires = ["setuptools>=70.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "LanPaint"
+version = "1.3.1"
+description = "Achieve seamless inpainting results without needing a specialized inpainting model."
+authors = [
+ {name = "LanPaint", email = "czhengac@connect.ust.hk"}
+]
+readme = "README.md"
+license = {text = "GNU General Public License v3"}
+classifiers = []
+dependencies = [
+
+]
+
+[project.optional-dependencies]
+dev = [
+ "bump-my-version",
+ "coverage", # testing
+ "mypy", # linting
+ "pre-commit", # runs linting on commit
+ "pytest", # testing
+ "ruff", # linting
+]
+
+[project.urls]
+bugs = "https://github.com/scraed/LanPaint/issues"
+homepage = "https://github.com/scraed/LanPaint"
+Repository = "https://github.com/scraed/LanPaint"
+
+[tool.comfy]
+PublisherId = "scraed"
+DisplayName = "LanPaint"
+Icon = ""
+
+[tool.setuptools.package-data]
+"*" = ["*.*"]
+
+[tool.pytest.ini_options]
+minversion = "8.0"
+testpaths = [
+ "tests",
+]
+
+[tool.mypy]
+files = "."
+
+# Use strict defaults
+strict = true
+warn_unreachable = true
+warn_no_return = true
+
+[[tool.mypy.overrides]]
+# Don't require test functions to include types
+module = "tests.*"
+allow_untyped_defs = true
+disable_error_code = "attr-defined"
+
+[tool.ruff]
+# extend-exclude = ["static", "ci/templates"]
+line-length = 140
+src = ["src", "tests"]
+target-version = "py39"
+
+# Add rules to ban exec/eval
+[tool.ruff.lint]
+select = [
+ "S102", # exec-builtin
+ "S307", # eval-used
+ "W293",
+ "F", # The "F" series in Ruff stands for "Pyflakes" rules, which catch various Python syntax errors and undefined names.
+ # See all rules here: https://docs.astral.sh/ruff/rules/#pyflakes-f
+]
+
+[tool.ruff.lint.flake8-quotes]
+inline-quotes = "double"
diff --git a/src/LanPaint/__init__.py b/src/LanPaint/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/LanPaint/lanpaint.py b/src/LanPaint/lanpaint.py
new file mode 100644
index 0000000000000000000000000000000000000000..b02bc9f37aebb655b2c2fd040e5a77ca71fb1d03
--- /dev/null
+++ b/src/LanPaint/lanpaint.py
@@ -0,0 +1,173 @@
+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
+ self.chara_lamb = Lambda
+ self.IS_FLUX = IS_FLUX
+ self.IS_FLOW = IS_FLOW
+ self.step_size = StepSize
+ 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:
+ 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):
+ VE_Sigma, abt, Flow_t = current_times
+
+
+ step_size = self.step_size * (1 - abt)
+ 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 * ( self.add_none_dims(abt)**0.5 + (1-self.add_none_dims(abt))**0.5 )
+ else:
+ 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 = 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 / ( 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 = out * (1-latent_mask) + self.latent_image * latent_mask
+ 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)
+ 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)
+
+ score_x = -(x_t - x_0)
+ 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
+ return abt**0
+ def sigma_y(self, abt):
+ beta = self.chara_beta * abt ** 0
+ return beta
+
+ def langevin_dynamics(self, x_t, score, mask, step_size, current_times, sigma_x=1, sigma_y=0, args=None):
+ # 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)
+ sigma, abt, dtx, dty, Gamma_x, Gamma_y, A_x, A_y, D_x, D_y = step_sizes
+ # print('mask',mask.device)
+ if torch.mean(dtx) <= 0.:
+ return x_t, args
+ # -------------------------------------------------------------------------
+ # 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)
+ 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, C = args
+
+ 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, 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 = 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
+
+ # -------------------------------------------------------------------------
+ # Define friction parameter Gamma_hat for each branch.
+ # Using dtx**0 provides a tensor of the proper device/dtype.
+
+ Gamma_hat_x = self.friction **2 * self.step_size * sigma_x / 0.1 * sigma**0
+ Gamma_hat_y = self.friction **2 * self.step_size * sigma_y / 0.1 * sigma**0
+ #print("Gamma_hat_x", torch.mean(Gamma_hat_x).item(), "Gamma_hat_y", torch.mean(Gamma_hat_y).item())
+ # 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+self.chara_lamb) / ( 1 - abt ) * dty / 2
+
+
+ A_x = A_t_x / (dtx/2)
+ A_y = A_t_y / (dty/2)
+ Gamma_x = Gamma_hat_x / (dtx/2)
+ Gamma_y = Gamma_hat_y / (dty/2)
+
+ #D_x = (2 * (1 + sigma**2) )**0.5
+ #D_y = (2 * (1 + sigma**2) )**0.5
+ 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
\ No newline at end of file
diff --git a/src/LanPaint/nodes.py b/src/LanPaint/nodes.py
new file mode 100644
index 0000000000000000000000000000000000000000..c853a652d78356511c83912cd625eec6eaed83be
--- /dev/null
+++ b/src/LanPaint/nodes.py
@@ -0,0 +1,565 @@
+from contextlib import contextmanager
+from inspect import cleandoc
+import inspect
+# import nodes.py
+import comfy
+import nodes
+import latent_preview
+from functools import partial
+from comfy.utils import repeat_to_batch_size
+from comfy.samplers import *
+from comfy.model_base import ModelType
+from .utils import *
+from .lanpaint import LanPaint
+
+
+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)
+ 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])
+ return mask
+def prepare_mask(noise_mask, shape, device):
+ return reshape_mask(noise_mask, shape).to(device)
+def sampling_function_LanPaint(model, x, timestep, uncond, cond, cond_scale, cond_scale_BIG, model_options={}, seed=None):
+ if math.isclose(cond_scale, 1.0) and model_options.get("disable_cfg1_optimization", False) == False:
+ uncond_ = None
+ else:
+ uncond_ = uncond
+
+ conds = [cond, uncond_]
+ out = calc_cond_batch(model, conds, x, timestep, model_options)
+
+ for fn in model_options.get("sampler_pre_cfg_function", []):
+ args = {"conds":conds, "conds_out": out, "cond_scale": cond_scale, "timestep": timestep,
+ "input": x, "sigma": timestep, "model": model, "model_options": model_options}
+ out = fn(args)
+
+ return cfg_function(model, out[0], out[1], cond_scale, x, timestep, model_options=model_options, cond=cond, uncond=uncond_), cfg_function(model, out[0], out[1], cond_scale_BIG, x, timestep, model_options=model_options, cond=cond, uncond=uncond_)
+
+
+class CFGGuider_LanPaint:
+ def outer_sample(self, noise, latent_image, sampler, sigmas, denoise_mask=None, callback=None, disable_pbar=False, seed=None):
+ print("CFGGuider outer_sample")
+ self.inner_model, self.conds, self.loaded_models = comfy.sampler_helpers.prepare_sampling(self.model_patcher, noise.shape, self.conds, self.model_options)
+ device = self.model_patcher.load_device
+
+ if denoise_mask is not None:
+ denoise_mask = prepare_mask(denoise_mask, noise.shape, device)
+
+ noise = noise.to(device)
+ latent_image = latent_image.to(device)
+ sigmas = sigmas.to(device)
+ cast_to_load_options(self.model_options, device=device, dtype=self.model_patcher.model_dtype())
+
+ try:
+ self.model_patcher.pre_run()
+ output = self.inner_sample(noise, latent_image, device, sampler, sigmas, denoise_mask, callback, disable_pbar, seed)
+ finally:
+ self.model_patcher.cleanup()
+
+ comfy.sampler_helpers.cleanup_models(self.conds, self.loaded_models)
+ del self.inner_model
+ del self.loaded_models
+ return output
+ def predict_noise(self, x, timestep, model_options={}, seed=None):
+ return sampling_function_LanPaint(self.inner_model, x, timestep, self.conds.get("negative", None), self.conds.get("positive", None), self.cfg, self.cfg_BIG, model_options=model_options, seed=seed)
+
+#CFGGuider.outer_sample = CFGGuider_LanPaint.outer_sample
+#CFGGuider.predict_noise = CFGGuider_LanPaint.predict_noise
+
+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 )
+ 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
+ # sigma is the noise level
+ ### For flux model
+ # x is rectified flow x_t = sigma * noise + (1.0 - sigma) * x_0
+
+ IS_FLUX = self.inner_model.inner_model.model_type == ModelType.FLUX
+ IS_FLOW = self.inner_model.inner_model.model_type == ModelType.FLOW
+
+ # unify the notations into variance exploding diffusion model
+ if IS_FLUX or IS_FLOW:
+ Flow_t = sigma
+ abt = (1 - Flow_t)**2 / ((1 - Flow_t)**2 + Flow_t**2 )
+ VE_Sigma = Flow_t / (1 - Flow_t)
+ #print("t", torch.mean( sigma ).item(), "VE_Sigma", torch.mean( VE_Sigma ).item())
+
+
+ else:
+ VE_Sigma = sigma
+ abt = 1/( 1+VE_Sigma**2 )
+ Flow_t = (1-abt)**0.5 / ( (1-abt)**0.5 + abt**0.5 )
+
+ if denoise_mask is not None:
+ if "denoise_mask_function" in model_options:
+ denoise_mask = model_options["denoise_mask_function"](sigma, denoise_mask, extra_options={"model": self.inner_model, "sigmas": self.sigmas})
+
+ denoise_mask = (denoise_mask > 0.5).float()
+
+ latent_mask = 1 - denoise_mask
+ current_times = (VE_Sigma, abt, Flow_t)
+
+ 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:
+ 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)
+ else:
+ out, _ = self.inner_model(x, sigma, model_options=model_options, seed=seed)
+
+ # Add TAESD preview support - directly use the latent_preview module
+ current_step = model_options.get("i", kwargs.get("i", 0))
+ total_steps = model_options.get("total_steps", 0)
+
+ # Only show preview every few steps to improve performance
+ if current_step % 2 == 0:
+ # Directly call the preview callback if it exists
+ callback = model_options.get("callback", None)
+ if callback is not None:
+ callback({"i": current_step, "denoised": out, "x": x})
+
+ return out
+
+# Custom sampler class extending ComfyUI's KSAMPLER for LanPaint
+class KSAMPLER(comfy.samplers.KSAMPLER):
+ def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
+ #noise here is a randn noise from comfy.sample.prepare_noise
+ #latent_image is the latent image as input of the KSampler node. For inpainting, it is the masked latent image. Otherwise it is zero tensor.
+ extra_args["denoise_mask"] = denoise_mask
+ model_k = KSamplerX0Inpaint(model_wrap, sigmas)
+ model_k.latent_image = latent_image
+ if self.inpaint_options.get("random", False): #TODO: Should this be the default?
+ generator = torch.manual_seed(extra_args.get("seed", 41) + 1)
+ model_k.noise = torch.randn(noise.shape, generator=generator, device="cpu").to(noise.dtype).to(noise.device)
+ else:
+ model_k.noise = noise
+
+ IS_FLUX = model_wrap.inner_model.model_type == ModelType.FLUX
+ IS_FLOW = model_wrap.inner_model.model_type == ModelType.FLOW
+ # unify the notations into variance exploding diffusion model
+ if IS_FLUX:
+ model_wrap.cfg_BIG = 1.0
+ else:
+ model_wrap.cfg_BIG = model_wrap.model_patcher.LanPaint_cfg_BIG
+ noise = model_wrap.inner_model.model_sampling.noise_scaling(sigmas[0], noise, latent_image, self.max_denoise(model_wrap, sigmas))
+
+ model_k.PaintMethod = LanPaint(model_k.inner_model,
+ model_wrap.model_patcher.LanPaint_NumSteps,
+ model_wrap.model_patcher.LanPaint_Friction,
+ model_wrap.model_patcher.LanPaint_Lambda,
+ model_wrap.model_patcher.LanPaint_Beta,
+ model_wrap.model_patcher.LanPaint_StepSize,
+ IS_FLUX = IS_FLUX,
+ IS_FLOW = IS_FLOW)
+ model_k.LanPaint_early_stop = model_wrap.model_patcher.LanPaint_EarlyStop
+ #if not inpainting, after noise_scaling, noise = noise * sigma, which is the noise added to the clean latent image in the variance exploding diffusion model notation.
+ #if inpainting, after noise_scaling, noise = latent_image + noise * sigma, which is x_t in the variance exploding diffusion model notation for the known region.
+ k_callback = None
+ total_steps = len(sigmas) - 1
+ if callback is not None:
+ k_callback = lambda x: callback(x["i"], x["denoised"], x["x"], total_steps)
+ #print("LanPaint KSampler call sampler_function", self.sampler_function)
+ # The main loop!
+ #print("##########")
+ #print("Sampling with ", self.sampler_function)
+ #print("##########")
+ samples = self.sampler_function(model_k, noise, sigmas, extra_args=extra_args, callback=k_callback, disable=disable_pbar, **self.extra_options)
+ #print("LanPaint KSampler end sampler_function")
+ samples = model_wrap.inner_model.model_sampling.inverse_noise_scaling(sigmas[-1], samples)
+ return samples
+
+@contextmanager
+def override_sample_function():
+ original_outer_sample = comfy.samplers.CFGGuider.outer_sample
+ comfy.samplers.CFGGuider.outer_sample = CFGGuider_LanPaint.outer_sample
+
+ original_predict_noise = comfy.samplers.CFGGuider.predict_noise
+ comfy.samplers.CFGGuider.predict_noise = CFGGuider_LanPaint.predict_noise
+
+ original_sample = comfy.samplers.KSAMPLER.sample
+ comfy.samplers.KSAMPLER.sample = KSAMPLER.sample
+
+ try:
+ yield
+ finally:
+ comfy.samplers.KSAMPLER.sample = original_sample
+ comfy.samplers.CFGGuider.predict_noise = original_predict_noise
+ comfy.samplers.CFGGuider.outer_sample = original_outer_sample
+
+
+class LanPaint_UpSale_LatentNoiseMask:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {"required": { "samples": ("LATENT",),
+ "scale": ("INT", {"default": 2, "min": 2, "max": 8, "step": 1}),
+ }}
+ RETURN_TYPES = ("LATENT",)
+ FUNCTION = "set_mask"
+
+
+ CATEGORY = "latent/inpaint"
+
+ def set_mask(self, samples, scale):
+ s = samples.copy()
+ samples = s['samples']
+ # generate a mask with every scaleth pixel set to 1
+ mask = torch.zeros(samples.shape[0], 1, samples.shape[2], samples.shape[3], device=samples.device) + 1
+ mask[:, :, ::scale, ::scale] = 0
+ s["noise_mask"] = mask
+ return (s,)
+
+#KSAMPLER_NAMES = ["euler", "dpmpp_2m", "uni_pc"]
+KSAMPLER_NAMES = ["euler","euler_ancestral", "heun", "heunpp2","dpm_2", "dpm_2_ancestral",
+ "dpm_fast", "dpmpp_sde", "dpmpp_sde_gpu",
+ "dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm",
+ "deis", "res_multistep", "res_multistep_ancestral",
+ "gradient_estimation", "er_sde", "seeds_2", "seeds_3"]
+
+class LanPaint_KSampler():
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "model": ("MODEL", {"tooltip": "The model used for denoising the input latent."}),
+ "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "The random seed used for creating the noise."}),
+ "steps": ("INT", {"default": 30, "min": 1, "max": 10000, "tooltip": "The number of steps used in the denoising process."}),
+ "cfg": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality."}),
+ "sampler_name": (KSAMPLER_NAMES, {"tooltip": "Recommended: euler."}),
+ "scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "karras", "tooltip": "The scheduler controls how noise is gradually removed to form the image."}),
+ "positive": ("CONDITIONING", {"tooltip": "The conditioning describing the attributes you want to include in the image."}),
+ "negative": ("CONDITIONING", {"tooltip": "The conditioning describing the attributes you want to exclude from the image."}),
+ "latent_image": ("LATENT", {"tooltip": "The latent image to denoise."}),
+ "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The amount of denoising applied, lower values will maintain the structure of the initial image allowing for image to image sampling."}),
+ "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_PromptMode": (["Image First", "Prompt First"], {"tooltip": "Image First: emphasis image quality, Prompt First: emphasis prompt following"}),
+ "LanPaint_Info": ("STRING", {"default": "LanPaint KSampler. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!", "multiline": True}),
+ }
+ }
+
+ RETURN_TYPES = ("LATENT",)
+ OUTPUT_TOOLTIPS = ("The denoised latent.",)
+ FUNCTION = "sample"
+
+ CATEGORY = "sampling"
+ DESCRIPTION = "Uses the provided model, positive and negative conditioning to denoise the latent image."
+
+ 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 = 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():
+ return nodes.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise)
+class LanPaint_KSamplerAdvanced:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {"required":
+ {"model": ("MODEL",),
+ "add_noise": (["enable", "disable"], ),
+ "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
+ "steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
+ "cfg": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
+ "sampler_name": (KSAMPLER_NAMES, ),
+ "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
+ "positive": ("CONDITIONING", ),
+ "negative": ("CONDITIONING", ),
+ "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": "The number of steps for the Langevin dynamics, representing the turns of thinking per step."}),
+ "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."}),
+ "LanPaint_PromptMode": (["Image First", "Prompt First"], {"tooltip": "Image First: emphasis image quality, Prompt First: emphasis prompt following"}),
+ "LanPaint_EarlyStop": ("INT", {"default": 1, "min": 0, "max": 10000, "tooltip": "The number of steps to stop the LanPaint early, useful for preventing the image from irregular patterns."}),
+ "LanPaint_Info": ("STRING", {"default": "LanPaint KSampler Adv. For more info, visit https://github.com/scraed/LanPaint. If you find it useful, please give a star ⭐️!", "multiline": True}),
+ },
+ }
+
+ RETURN_TYPES = ("LATENT",)
+ FUNCTION = "sample"
+
+ CATEGORY = "sampling"
+
+ def sample(self, model, add_noise, noise_seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, start_at_step, end_at_step, return_with_leftover_noise, denoise=1.0, LanPaint_StepSize=0.05, LanPaint_Lambda=5, LanPaint_Beta=1, LanPaint_NumSteps=5, LanPaint_Friction=5, LanPaint_PromptMode = "Image First", LanPaint_EarlyStop = 1, LanPaint_Info=""):
+ force_full_denoise = True
+ if return_with_leftover_noise == "enable":
+ force_full_denoise = False
+ disable_noise = False
+ if add_noise == "disable":
+ disable_noise = True
+ 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 = cfg
+ else:
+ model.LanPaint_cfg_BIG = 0*cfg - 0.5
+
+ with override_sample_function():
+ 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,
+}
+
+# A dictionary that contains the friendly/humanly readable titles for the nodes
+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"
+}
diff --git a/src/LanPaint/utils.py b/src/LanPaint/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..7493c0572c88dac021c206d675e8abb45cdbc6b3
--- /dev/null
+++ b/src/LanPaint/utils.py
@@ -0,0 +1,301 @@
+import torch
+def epxm1_x(x):
+ # Compute the (exp(x) - 1) / x term with a small value to avoid division by zero.
+ result = torch.special.expm1(x) / x
+ # replace NaN or inf values with 0
+ result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
+ mask = torch.abs(x) < 1e-2
+ result = torch.where(mask, 1 + x/2. + x**2 / 6., result)
+ return result
+def epxm1mx_x2(x):
+ # Compute the (exp(x) - 1 - x) / x**2 term with a small value to avoid division by zero.
+ result = (torch.special.expm1(x) - x) / x**2
+ # replace NaN or inf values with 0
+ result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
+ mask = torch.abs(x**2) < 1e-2
+ result = torch.where(mask, 1/2. + x/6 + x**2 / 24 + x**3 / 120, result)
+ return result
+
+def expm1mxmhx2_x3(x):
+ # Compute the (exp(x) - 1 - x - x**2 / 2) / x**3 term with a small value to avoid division by zero.
+ result = (torch.special.expm1(x) - x - x**2 / 2) / x**3
+ # replace NaN or inf values with 0
+ result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
+ mask = torch.abs(x**3) < 1e-2
+ result = torch.where(mask, 1/6 + x/24 + x**2 / 120 + x**3 / 720 + x**4 / 5040, result)
+ return result
+
+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
+ """
+ # Main computation
+ is_positive = delta > 0
+ sqrt_abs_delta = torch.sqrt(torch.abs(delta))
+ gamma_t_sqrt_delta = gamma_t * sqrt_abs_delta
+ numerator_pos = torch.exp(-gamma_t) - (torch.exp(gamma_t * (sqrt_abs_delta - 1)) + torch.exp(gamma_t * (-sqrt_abs_delta - 1))) / 2
+ numerator_neg = torch.exp(-gamma_t) * ( 1 - torch.cos(gamma_t * sqrt_abs_delta ) )
+ numerator = torch.where(is_positive, numerator_pos, numerator_neg)
+ result = numerator / (delta * gamma_t**2 )
+ # Handle NaN/inf cases
+ result = torch.where(torch.isfinite(result), result, torch.zeros_like(result))
+ # Handle numerical instability for small delta
+ mask = torch.abs(gamma_t_sqrt_delta**2) < 5e-2
+ taylor = ( -0.5 - gamma_t**2 / 24 * delta - gamma_t**4 / 720 * delta**2 ) * torch.exp(-gamma_t)
+ result = torch.where(mask, taylor, result)
+ return result
+
+def exp_sinh_GsqrtD(gamma_t, delta):
+ """
+ Compute e^(-Γt) * sinh(Γt√Δ) / (Γt√Δ)
+
+ 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
+ """
+ # Main computation
+ is_positive = delta > 0
+ 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))
+
+ # Taylor expansion for small gamma_t_sqrt_delta
+ mask = torch.abs(gamma_t_sqrt_delta) < 1e-2
+ taylor = ( 1 + gamma_t**2 / 6 * delta + gamma_t**4 / 120 * delta**2 ) * torch.exp(-gamma_t)
+ result_pos = torch.where(mask, taylor, result_pos)
+
+ # Handle negative delta
+ result_neg = torch.exp(-gamma_t) * torch.special.sinc(gamma_t_sqrt_delta/torch.pi)
+ result = torch.where(is_positive, result_pos, result_neg)
+ return result
+
+def exp_cosh(gamma_t, delta):
+ """
+ Compute e^(-Γt) * cosh(Γt√Δ)
+
+ 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
+ """
+ exp_1mcosh_GD_result = exp_1mcosh_GD(gamma_t, delta) # e^(-Γt) * (1 - cosh(Γt√Δ))/ ( (Γt)**2 Δ )
+ result = torch.exp(-gamma_t) - gamma_t**2 * delta * exp_1mcosh_GD_result
+ return result
+def exp_sinh_sqrtD(gamma_t, delta):
+ """
+ Compute e^(-Γt) * sinh(Γt√Δ) / √Δ
+ 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
+ """
+ exp_sinh_GsqrtD_result = exp_sinh_GsqrtD(gamma_t, delta) # e^(-Γt) * sinh(Γt√Δ) / (Γt√Δ)
+ result = gamma_t * exp_sinh_GsqrtD_result
+ return result
+
+
+
+def zeta1(gamma_t, delta):
+ # Compute hyperbolic terms and exponential
+ half_gamma_t = gamma_t / 2
+ 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)
+ term2 = epxm1mx_x2(-gamma_t)
+ 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
+ """
+ exp_term = torch.exp(-gamma_t)
+ # 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)
+ taylor = (
+ gamma_t*exp_1mcosh_GD_term + 0.5 * gamma_t * exp_sinh_GsqrtD(gamma_t, delta**0)
+ - 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
+
+
+def zeta2(gamma_t, delta):
+ half_gamma_t = gamma_t / 2
+ return exp_sinh_GsqrtD(half_gamma_t, delta)
+
+def sig11(gamma_t, delta):
+ return 1 - torch.exp(-gamma_t) + gamma_t**2 * exp_1mcosh_GD(gamma_t, delta) + exp_sinh_sqrtD(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
+ Zcoef2 = Zcoef1 * gamma_t *( - 2 * exp_1mcosh_GD(gamma_t, delta) / sig11(gamma_t, delta) ) ** 0.5
+ #cterm = exp_cosh_minus_terms(gamma_t, delta)
+ #sterm = exp_sinh_sqrtD(gamma_t, delta**0) + exp_sinh_sqrtD(gamma_t, delta)
+ #Zcoef3 = 2 * torch.sqrt( cterm / ( gamma_t * (1 - delta) * cterm + sterm ) )
+ Zcoef3 = torch.sqrt( torch.maximum(1 - Zcoef1**2 - Zcoef2**2, sq_total.new_zeros(sq_total.shape)) )
+
+ return Zcoef1 * amplitude, Zcoef2 * amplitude, Zcoef3 * amplitude, amplitude
+
+def Zcoefs_asymp(gamma_t, delta):
+ A_t = (gamma_t * (1 - delta) )/4
+ return epxm1_x(- 2 * A_t)
+
+class StochasticHarmonicOscillator:
+ """
+ Simulates a stochastic harmonic oscillator governed by the equations:
+ dy(t) = q(t) dt
+ 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
+ Γ - Damping coefficient
+ A - Harmonic potential strength
+ C - Constant force term
+ D - Noise amplitude
+ dw(t) - Wiener process (Brownian motion)
+ """
+ def __init__(self, Gamma, A, C, D):
+ self.Gamma = Gamma
+ self.A = A
+ self.C = C
+ self.D = D
+ self.Delta = 1 - 4 * A / Gamma
+ def sig11(self, gamma_t, delta):
+ return 1 - torch.exp(-gamma_t) + gamma_t**2 * exp_1mcosh_GD(gamma_t, delta) + exp_sinh_sqrtD(gamma_t, delta)
+ def sig22(self, gamma_t, delta):
+ return 1- zeta1(2*gamma_t, delta) + 2 * gamma_t * exp_1mcosh_GD(gamma_t, delta)
+ def dynamics(self, y0, v0, t):
+ """
+ Calculates the position and velocity variables at time t.
+
+ Parameters:
+ y0 (float): Initial position
+ v0 (float): Initial velocity v(0) = q(0) / √Γ
+ t (float): Time at which to evaluate the dynamics
+ 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
+ A = self.A + dummyzero
+ C = self.C + dummyzero
+ D = self.D + dummyzero
+ Gamma = self.Gamma + dummyzero
+ zeta_1 = zeta1( Gamma_hat, Delta)
+ zeta_2 = zeta2( Gamma_hat, Delta)
+ 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
+
+ # 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)
+
+ # sample new position and velocity with multivariate normal distribution
+
+ batch_shape = y0.shape
+ cov_matrix = torch.zeros(*batch_shape, 2, 2, device=y0.device, dtype=y0.dtype)
+ cov_matrix[..., 0, 0] = cov_yy
+ 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,
+ scale_tril=scale_tril
+ ).sample()
+
+ return new_yv[...,0], new_yv[...,1]
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e4c58dd75a8065552155782cd1cc78da90fb526
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+"""Unit test package for LanPaint."""
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..29930e085a7e721d0d3976d52ac8cd734754ed55
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,6 @@
+import os
+import sys
+
+# Add the project root directory to Python path
+# This allows the tests to import the project
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
diff --git a/tests/pytest.ini b/tests/pytest.ini
new file mode 100644
index 0000000000000000000000000000000000000000..4666dfd90f98af196ccbfde221c3200cc2d326b3
--- /dev/null
+++ b/tests/pytest.ini
@@ -0,0 +1,4 @@
+[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
diff --git a/tests/test_LanPaint.py b/tests/test_LanPaint.py
new file mode 100644
index 0000000000000000000000000000000000000000..ad8aab1d42fb6b57d4b122ece380d43e34bb4798
--- /dev/null
+++ b/tests/test_LanPaint.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+"""Tests for `LanPaint` package."""
+
+import pytest
+from src.LanPaint.nodes import Example
+
+@pytest.fixture
+def example_node():
+ """Fixture to create an Example node instance."""
+ return Example()
+
+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"
diff --git a/web/js/example.js b/web/js/example.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd9e80d4744359789b43bbb370a049f98287999a
--- /dev/null
+++ b/web/js/example.js
@@ -0,0 +1 @@
+console.log(app);