Merge pull request #19 from GiusTex/New-Pad-Node-Options

add new pad options
This commit is contained in:
GiusTex
2024-11-17 18:42:18 +01:00
committed by GitHub
5 changed files with 1215 additions and 1147 deletions
File diff suppressed because it is too large Load Diff
@@ -1,525 +0,0 @@
{
"last_node_id": 591,
"last_link_id": 1263,
"nodes": [
{
"id": 530,
"type": "VAEDecode",
"pos": {
"0": 660,
"1": 90
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 1241
},
{
"name": "vae",
"type": "VAE",
"link": 1261
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1262
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 584,
"type": "DiffusersImageOutpaint",
"pos": {
"0": 320,
"1": 90
},
"size": {
"0": 300,
"1": 214
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1255
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 1254
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 1258
},
{
"name": "diffuser_outpaint_cnet_image",
"type": "IMAGE",
"link": 1251
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
1241
],
"slot_index": 0
}
],
"title": "DiffusersImageOutpaint",
"properties": {
"Node name for S&R": "DiffusersImageOutpaint"
},
"widgets_values": [
1.5,
1,
43078817542338,
"randomize",
8
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 529,
"type": "PadImageForDiffusersOutpaint",
"pos": {
"0": 0,
"1": 390
},
"size": {
"0": 290,
"1": 150
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 1263
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
},
{
"name": "MASK",
"type": "MASK",
"links": null
},
{
"name": "diffuser_outpaint_cnet_image",
"type": "IMAGE",
"links": [
1251
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "PadImageForDiffusersOutpaint"
},
"widgets_values": [
720,
1280,
"Top"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 534,
"type": "LoadDiffusersOutpaintModels",
"pos": {
"0": -480,
"1": 60
},
"size": {
"0": 320,
"1": 154
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [
1253,
1257
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LoadDiffusersOutpaintModels"
},
"widgets_values": [
"RealVisXL_V5.0_Lightning",
"controlnet-union-sdxl-1.0",
"auto",
"auto",
false
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 588,
"type": "EncodeDiffusersOutpaintPrompt",
"pos": {
"0": -110,
"1": 220
},
"size": {
"0": 400,
"1": 96
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1257
},
{
"name": "clip",
"type": "CLIP",
"link": 1259
}
],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [],
"slot_index": 0
},
{
"name": "diffusers_conditioning",
"type": "CONDITIONING",
"links": [
1258
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "EncodeDiffusersOutpaintPrompt"
},
"widgets_values": [
""
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 351,
"type": "PreviewImage",
"pos": {
"0": 650,
"1": 180
},
"size": {
"0": 510,
"1": 490
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1262
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 591,
"type": "LoadImage",
"pos": {
"0": -350,
"1": 400
},
"size": [
320,
310
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1263
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"20230403_183417.jpg",
"image"
]
},
{
"id": 587,
"type": "EncodeDiffusersOutpaintPrompt",
"pos": {
"0": -120,
"1": 70
},
"size": {
"0": 400,
"1": 96
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1253
},
{
"name": "clip",
"type": "CLIP",
"link": 1260
}
],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [
1255
],
"slot_index": 0
},
{
"name": "diffusers_conditioning",
"type": "CONDITIONING",
"links": [
1254
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "EncodeDiffusersOutpaintPrompt"
},
"widgets_values": [
""
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 589,
"type": "CheckpointLoaderSimple",
"pos": {
"0": -500,
"1": 250
},
"size": {
"0": 360,
"1": 100
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": null
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
1259,
1260
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
1261
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"realvisxlV50_v50LightningBakedvae.safetensors"
],
"color": "#223",
"bgcolor": "#335"
}
],
"links": [
[
1241,
584,
0,
530,
0,
"LATENT"
],
[
1251,
529,
2,
584,
3,
"IMAGE"
],
[
1253,
534,
0,
587,
0,
"PIPE"
],
[
1254,
587,
1,
584,
1,
"CONDITIONING"
],
[
1255,
587,
0,
584,
0,
"PIPE"
],
[
1257,
534,
0,
588,
0,
"PIPE"
],
[
1258,
588,
1,
584,
2,
"CONDITIONING"
],
[
1259,
589,
1,
588,
1,
"CLIP"
],
[
1260,
589,
1,
587,
1,
"CLIP"
],
[
1261,
589,
2,
530,
1,
"VAE"
],
[
1262,
530,
0,
351,
0,
"IMAGE"
],
[
1263,
591,
0,
529,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8769226950000005,
"offset": [
570.3286926097892,
6.798044267632111
]
}
},
"version": 0.4
}
-546
View File
@@ -1,546 +0,0 @@
{
"last_node_id": 591,
"last_link_id": 1259,
"nodes": [
{
"id": 584,
"type": "DiffusersImageOutpaint",
"pos": {
"0": 320,
"1": 90
},
"size": {
"0": 300,
"1": 214
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1255
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 1254
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 1258
},
{
"name": "diffuser_outpaint_cnet_image",
"type": "IMAGE",
"link": 1251
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
1241
],
"slot_index": 0
}
],
"title": "DiffusersImageOutpaint",
"properties": {
"Node name for S&R": "DiffusersImageOutpaint"
},
"widgets_values": [
1.5,
1,
1009037337630565,
"randomize",
8
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 529,
"type": "PadImageForDiffusersOutpaint",
"pos": {
"0": 0,
"1": 390
},
"size": {
"0": 290,
"1": 150
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 1005
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
},
{
"name": "MASK",
"type": "MASK",
"links": null
},
{
"name": "diffuser_outpaint_cnet_image",
"type": "IMAGE",
"links": [
1251
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "PadImageForDiffusersOutpaint"
},
"widgets_values": [
720,
1280,
"Top"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 531,
"type": "VAELoader",
"pos": {
"0": 370,
"1": 350
},
"size": {
"0": 260,
"1": 60
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [
1007
]
}
],
"properties": {
"Node name for S&R": "VAELoader"
},
"widgets_values": [
"sdxl_vae.safetensors"
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 534,
"type": "LoadDiffusersOutpaintModels",
"pos": {
"0": -480,
"1": 60
},
"size": {
"0": 320,
"1": 154
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [
1253,
1257
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LoadDiffusersOutpaintModels"
},
"widgets_values": [
"RealVisXL_V5.0_Lightning",
"controlnet-union-sdxl-1.0",
"auto",
"auto",
false
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 588,
"type": "EncodeDiffusersOutpaintPrompt",
"pos": {
"0": -110,
"1": 220
},
"size": {
"0": 400,
"1": 96
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1257
},
{
"name": "clip",
"type": "CLIP",
"link": 1256
}
],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [],
"slot_index": 0
},
{
"name": "diffusers_conditioning",
"type": "CONDITIONING",
"links": [
1258
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "EncodeDiffusersOutpaintPrompt"
},
"widgets_values": [
""
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 580,
"type": "DualCLIPLoader",
"pos": {
"0": -420,
"1": 260
},
"size": {
"0": 260,
"1": 110
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [
1252,
1256
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "DualCLIPLoader"
},
"widgets_values": [
"clip_l.safetensors",
"model.fp16.safetensors",
"sdxl"
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 530,
"type": "VAEDecode",
"pos": {
"0": 660,
"1": 90
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 1241
},
{
"name": "vae",
"type": "VAE",
"link": 1007
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1259
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 528,
"type": "LoadImage",
"pos": {
"0": -360,
"1": 430
},
"size": [
320,
310
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1005
]
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"Emilia3.png",
"image"
]
},
{
"id": 351,
"type": "PreviewImage",
"pos": {
"0": 670,
"1": 190
},
"size": {
"0": 510,
"1": 490
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1259
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 587,
"type": "EncodeDiffusersOutpaintPrompt",
"pos": {
"0": -120,
"1": 70
},
"size": {
"0": 400,
"1": 96
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"link": 1253
},
{
"name": "clip",
"type": "CLIP",
"link": 1252
}
],
"outputs": [
{
"name": "diffusers_outpaint_pipe",
"type": "PIPE",
"links": [
1255
],
"slot_index": 0
},
{
"name": "diffusers_conditioning",
"type": "CONDITIONING",
"links": [
1254
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "EncodeDiffusersOutpaintPrompt"
},
"widgets_values": [
""
],
"color": "#232",
"bgcolor": "#353"
}
],
"links": [
[
1005,
528,
0,
529,
0,
"IMAGE"
],
[
1007,
531,
0,
530,
1,
"VAE"
],
[
1241,
584,
0,
530,
0,
"LATENT"
],
[
1251,
529,
2,
584,
3,
"IMAGE"
],
[
1252,
580,
0,
587,
1,
"CLIP"
],
[
1253,
534,
0,
587,
0,
"PIPE"
],
[
1254,
587,
1,
584,
1,
"CONDITIONING"
],
[
1255,
587,
0,
584,
0,
"PIPE"
],
[
1256,
580,
0,
588,
1,
"CLIP"
],
[
1257,
534,
0,
588,
0,
"PIPE"
],
[
1258,
588,
1,
584,
2,
"CONDITIONING"
],
[
1259,
530,
0,
351,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.7247295000000004,
"offset": [
679.3196754354946,
80.60613789648367
]
}
},
"version": 0.4
}
+11 -6
View File
@@ -1,9 +1,14 @@
ComfyUI nodes for outpainting images with diffusers, based on [diffusers-image-outpaint](https://huggingface.co/spaces/fffiloni/diffusers-image-outpaint/tree/main) by fffiloni.
![image](https://github.com/user-attachments/assets/8f7665a1-dd8c-44d6-a067-fcc3f48b1865)
![Extension-Overview](https://github.com/user-attachments/assets/b801698e-e666-4179-98bd-42dfb1f033ba)
#### Updates:
- 17/11/2024:
- Added more options to Pad Image node (resize image, custom resize image percentage, mask overlap percentage, overlap left/right/top/bottom).
- Side notes:
- Now images with round angles work, since the new editable mask covers them, like in the original huggingface space.
- You can use "mask" and "diffusers outpaint cnet image" outputs to preview mask and image.
- You can find in the same [workflow file](https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint/blob/New-Pad-Node-Options/Diffusers-Outpaint-DoubleWorkflow.json) the workflow with the checkpoint-loader-simple node and another one with clip + vae loader nodes.
- 22/10/2024:
- Unet and Controlnet Models Loader using ComfYUI nodes canceled, since I can't find a way to load them properly; more info at the end.
- Guide to change model used.
@@ -27,17 +32,17 @@ ComfyUI nodes for outpainting images with diffusers, based on [diffusers-image-o
- `model_index.json` ([example](https://huggingface.co/SG161222/RealVisXL_V5.0_Lightning/blob/main/model_index.json))
- controlnet_name:
- `config_promax.json` ([example](https://huggingface.co/xinsir/controlnet-union-sdxl-1.0/blob/main/config_promax.json)), `diffusion_pytorch_model_promax.safetensors` ([example](https://huggingface.co/xinsir/controlnet-union-sdxl-1.0/blob/main/diffusion_pytorch_model_promax.safetensors))
- (Dual) Clip Loader node: if you use the Clip Loader instead of Checkpoint Loader Simple, and want to use RealVisXL_V5.0_Lightning, it works with [`clip_I`](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/text_encoder/model.fp16.safetensors) and [`model.fp16`](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/text_encoder_2/model.fp16.safetensors) (from sdxl-base), and `sdxl type`; you can use [this workflow](https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint/blob/main/Diffusers-Outpaint-Workflow.json).
- (Dual) Clip Loader node: if you use the Clip Loader instead of Checkpoint Loader Simple, and want to use RealVisXL_V5.0_Lightning, it works with [`clip_I`](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/text_encoder/model.fp16.safetensors) and [`model.fp16`](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/text_encoder_2/model.fp16.safetensors) (from sdxl-base), and `sdxl type`; you can use [this workflow](https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint/blob/New-Pad-Node-Options/Diffusers-Outpaint-DoubleWorkflow.json).
## Overview
- **Minimum VRAM**: 6 gb with 1280x720 image, rtx 3060, RealVisXL_V5.0_Lightning, sdxl-vae-fp16-fix, controlnet-union-sdxl-promax using `sequential_cpu_offload`, otherwise 8,3 gb;
- As seen in [this issue](https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint/issues/7#issuecomment-2410852908), images with **square corners** are required.
- ~As seen in [this issue](https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint/issues/7#issuecomment-2410852908), images with **square corners** are required~.
The extension gives 4 nodes:
- **Load Diffusion Outpaint Models**: a simple node to load diffusion `models`. You can download them from Huggingface (the extension doesn't download them automatically);
- **Paid Image for Diffusers Outpaint**: this node creates an empty image of the `desired size`, fits the original image in the new one based on the chosen `alignment`, then mask the rest;
- **Paid Image for Diffusers Outpaint**: this node resizes the image based on the specified `width` and `height`, then resizes it again based on the `resize_image` percentage, and if possible it will put the mask based on the `alignment` specified, otherwise it will revert back to the default "middle" `alignment`;
- **Encode Diffusers Outpaint Prompt**: self explanatory. Works as `clip text encode (prompt)`, and specifies what to add to the image;
- **Diffusers Image Outpaint**: This is the main node, that outpaints the image. Currently the generation process is based on fffiloni's one, so you can't reproduce a specific a specific outpaint, and the `seed` option you see is only used to change the UI and generate a new image. You can specify the amount of `steps` to generate the image.
- **Diffusers Image Outpaint**: This is the main node, that outpaints the image. Currently the generation process is based on fffiloni's one, so you can't reproduce a specific a specific outpaint, and the `seed` option you see is only used to update the UI and generate a new image. You can specify the amount of `steps` to generate the image.
- You can also pass image and mask to `vae encode (for inpainting)` node, then pass the latent to a `sampler`, but controlnets and ip-adapters are harder to use compared to diffusers outpaint.
+104 -70
View File
@@ -1,67 +1,96 @@
import torch
import os
from PIL import Image
from PIL import Image, ImageDraw
from .utils import get_first_folder_list, tensor2pil, pil2tensor, diffuserOutpaintSamples, get_device_by_name, get_dtype_by_name, clearVram
# Get the absolute path of various directories
my_dir = os.path.dirname(os.path.abspath(__file__))
def can_expand(source_width, source_height, target_width, target_height, alignment):
"""Checks if the image can be expanded based on the alignment."""
if alignment in ("Left", "Right") and source_width >= target_width:
return False
if alignment in ("Top", "Bottom") and source_height >= target_height:
return False
return True
class PadImageForDiffusersOutpaint:
_alignment_options = ["Middle", "Left", "Right", "Top", "Bottom"]
_resize_option = ["Full", "50%", "33%", "25%", "Custom"]
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"width": ("INT", {"default": 720, "min": 320, "max": 1536, "tooltip": "The width used for the image."}),
"height": ("INT", {"default": 1280, "min": 320, "max": 1536, "tooltip": "The height used for the image."}),
"width": ("INT", {"default": 720, "tooltip": "The width used for the image."}),
"height": ("INT", {"default": 1280, "tooltip": "The height used for the image."}),
"alignment": (s._alignment_options, {"tooltip": "Where the original image should be in the outpainted one"}),
"resize_image": (s._resize_option, {"tooltip": "Resize input image"}),
"custom_resize_image_percentage": ("INT", {"min": 1, "default": 50, "max": 100, "step": 1, "tooltip": "Custom resize (%)"}),
"mask_overlap_percentage": ("INT", {"min": 1, "default": 10, "max": 50, "step": 1, "tooltip": "Mask overlap (%)"}),
"overlap_left": ("BOOLEAN", {"default": True}),
"overlap_right": ("BOOLEAN", {"default": True}),
"overlap_top": ("BOOLEAN", {"default": True}),
"overlap_bottom": ("BOOLEAN", {"default": True}),
},
}
RETURN_TYPES = ("IMAGE", "MASK", "IMAGE")
RETURN_NAMES = ("IMAGE", "MASK", "diffuser_outpaint_cnet_image")
FUNCTION = "expand_image"
FUNCTION = "prepare_image_and_mask"
CATEGORY = "DiffusersOutpaint"
def expand_image(self, image, width, height, alignment="Middle"):
# Resize Image
def can_expand(source_width, source_height, target_width, target_height, alignment):
"""Checks if the image can be expanded based on the alignment."""
if alignment in ("Left", "Right") and source_width >= target_width:
return False
if alignment in ("Top", "Bottom") and source_height >= target_height:
return False
return True
def prepare_image_and_mask(self, image, width, height, mask_overlap_percentage, resize_image, custom_resize_image_percentage, overlap_left, overlap_right, overlap_top, overlap_bottom, alignment="Middle"):
im=tensor2pil(image)
source=im.convert('RGB')
target_size = (width, height)
# Raise an error.
if source.width == width and source.height == height:
raise ValueError(f'Input image size is the same as target size, resize input image or change target size.')
# Calculate the scaling factor to fit the image within the target size
scale_factor = min(target_size[0] / source.width, target_size[1] / source.height)
new_width = int(source.width * scale_factor)
new_height = int(source.height * scale_factor)
# Resize the source image to fit within target size
source = source.resize((new_width, new_height), Image.LANCZOS)
# Initialize new_width and new_height
new_width, new_height = source.width, source.height
# Upscale if source is smaller than target in both dimensions
if source.width < target_size[0] and source.height < target_size[1]:
scale_factor = min(target_size[0] / source.width, target_size[1] / source.height)
new_width = int(source.width * scale_factor)
new_height = int(source.height * scale_factor)
source = source.resize((new_width, new_height), Image.LANCZOS)
# Apply resize option using percentages
if resize_image == "Full":
resize_percentage = 100
elif resize_image == "50%":
resize_percentage = 50
elif resize_image == "33%":
resize_percentage = 33
elif resize_image == "25%":
resize_percentage = 25
else: # Custom
resize_percentage = custom_resize_image_percentage
# Calculate new dimensions based on percentage
resize_factor = resize_percentage / 100
new_width = int(source.width * resize_factor)
new_height = int(source.height * resize_factor)
# Ensure minimum size of 64 pixels
new_width = max(new_width, 64)
new_height = max(new_height, 64)
if source.width > target_size[0] or source.height > target_size[1]:
scale_factor = min(target_size[0] / source.width, target_size[1] / source.height)
new_width = int(source.width * scale_factor)
new_height = int(source.height * scale_factor)
source = source.resize((new_width, new_height), Image.LANCZOS)
# Resize the image
source = source.resize((new_width, new_height), Image.LANCZOS)
# Calculate the overlap in pixels based on the percentage
overlap_x = int(new_width * (mask_overlap_percentage / 100))
overlap_y = int(new_height * (mask_overlap_percentage / 100))
# Ensure minimum overlap of 1 pixel
overlap_x = max(overlap_x, 1)
overlap_y = max(overlap_y, 1)
if not can_expand(source.width, source.height, target_size[0], target_size[1], alignment):
alignment = "Middle"
# Calculate margins based on alignment
if alignment == "Middle":
margin_x = (target_size[0] - source.width) // 2
@@ -79,11 +108,17 @@ class PadImageForDiffusersOutpaint:
margin_x = (target_size[0] - source.width) // 2
margin_y = target_size[1] - source.height
# Adjust margins to eliminate gaps
margin_x = max(0, min(margin_x, target_size[0] - new_width))
margin_y = max(0, min(margin_y, target_size[1] - new_height))
# Create a new background image and paste the resized source image
background = Image.new('RGB', target_size, (255, 255, 255))
background.paste(source, (margin_x, margin_y))
image=pil2tensor(background)
#----------------------------------------------------
# Create the mask
d1, d2, d3, d4 = image.size()
left, top, bottom, right = 0, 0, 0, 0
# Image
@@ -92,51 +127,50 @@ class PadImageForDiffusersOutpaint:
dtype=torch.float32,
) * 0.5
new_image[:, top:top + d2, left:left + d3, :] = image
#----------------------------------------------------
# Mask coordinates
if alignment == "Middle":
margin_x = (width - new_width) // 2
margin_y = (height - new_height) // 2
elif alignment == "Left":
margin_x = 0
margin_y = (height - new_height) // 2
elif alignment == "Right":
margin_x = width - new_width
margin_y = (height - new_height) // 2
elif alignment == "Top":
margin_x = (width - new_width) // 2
margin_y = 0
elif alignment == "Bottom":
margin_x = (width - new_width) // 2
margin_y = height - new_height
# Create mask as big as new img
mask = torch.ones(
(height, width),
dtype=torch.float32,
)
# Create hole in mask
t = torch.zeros(
(new_height, new_width),
dtype=torch.float32
)
# Create holed mask
mask[margin_y:margin_y + new_height,
margin_x:margin_x + new_width
] = t
#----------------------------------------------------
# Prepare "cn_image" for diffusers outpaint
im=tensor2pil(new_image)
pil_new_image=im.convert('RGB')
pil_mask=tensor2pil(mask)
#----------------------------------------------------
# Create the mask
mask = Image.new('L', target_size, 255)
mask_draw = ImageDraw.Draw(mask)
#----------------------------------------------------
# Calculate overlap areas
white_gaps_patch = 2
left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
#----------------------------------------------------
# Mask coordinates
if alignment == "Left":
left_overlap = margin_x + overlap_x if overlap_left else margin_x
elif alignment == "Right":
right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width
elif alignment == "Top":
top_overlap = margin_y + overlap_y if overlap_top else margin_y
elif alignment == "Bottom":
bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height
# Draw the mask
mask_draw.rectangle([
(left_overlap, top_overlap),
(right_overlap, bottom_overlap)
], fill=0)
tensor_mask=pil2tensor(mask)
#----------------------------------------------------
if not can_expand(background.width, background.height, width, height, alignment):
alignment = "Middle"
cnet_image = pil_new_image.copy() # copy background as cnet_image
cnet_image.paste(0, (0, 0), pil_mask) # paste mask over cnet_image, cropping it a bit
cnet_image.paste(0, (0, 0), mask) # paste mask over cnet_image, cropping it a bit
tensor_cnet_image=pil2tensor(cnet_image)
return (new_image, mask, tensor_cnet_image,)
return (new_image, tensor_mask, tensor_cnet_image,)
class LoadDiffusersOutpaintModels: