Files
sipherxyz-comfyui-art-venture/modules/nodes.py
T
2023-08-11 23:33:24 +07:00

333 lines
9.8 KiB
Python

import io
import os
import json
import torch
import base64
import requests
from typing import Dict
from PIL import Image, ImageOps
from PIL.PngImagePlugin import PngInfo
import numpy as np
from .logger import logger
from .utils import upload_to_av
import folder_paths
class UtilLoadImageFromUrl:
def __init__(self) -> None:
self.output_dir = folder_paths.get_temp_directory()
self.filename_prefix = "TempImageFromUrl"
@classmethod
def INPUT_TYPES(s):
return {
"required": {"url": ("STRING", {"default": ""})},
}
RETURN_TYPES = ("IMAGE", "MASK")
CATEGORY = "image"
FUNCTION = "load_image_from_url"
def load_image_from_url(self, url: str):
if url.startswith("data:image/"):
i = Image.open(io.BytesIO(base64.b64decode(url.split(",")[1])))
else:
response = requests.get(url, timeout=5)
if response.status_code != 200:
raise Exception(response.text)
i = Image.open(io.BytesIO(response.content))
i = ImageOps.exif_transpose(i)
image = i.convert("RGB")
# save image to temp folder
(
outdir,
filename,
counter,
subfolder,
_,
) = folder_paths.get_save_image_path(
self.filename_prefix, self.output_dir, image.width, image.height
)
file = f"{filename}_{counter:05}.png"
image.save(os.path.join(outdir, file), format="PNG", compress_level=4)
preview = {
"filename": file,
"subfolder": subfolder,
"type": "temp",
}
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
if "A" in i.getbands():
mask = np.array(i.getchannel("A")).astype(np.float32) / 255.0
mask = 1.0 - torch.from_numpy(mask)
else:
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
return {"ui": {"images": [preview]}, "result": (image, mask)}
class AVOutputUploadImage:
@classmethod
def INPUT_TYPES(s):
return {
"required": {"images": ("IMAGE",)},
"optional": {
"folder_id": ("STRING", {"multiline": False}),
},
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO",
},
}
RETURN_TYPES = ()
OUTPUT_NODE = True
CATEGORY = "Art Venture"
FUNCTION = "upload_images"
def upload_images(
self,
images,
folder_id: str = None,
prompt=None,
extra_pnginfo=None,
):
files = list()
for idx, image in enumerate(images):
i = 255.0 * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
metadata = PngInfo()
if prompt is not None:
metadata.add_text("prompt", json.dumps(prompt))
if extra_pnginfo is not None:
for x in extra_pnginfo:
logger.debug(f"Adding {x} to pnginfo: {extra_pnginfo[x]}")
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
buffer = io.BytesIO()
img.save(buffer, format="PNG", pnginfo=metadata, compress_level=4)
buffer.seek(0)
files.append(
(
"files",
(f"image-{idx}.png", buffer, "image/png"),
)
)
additional_data = {"success": "true"}
if folder_id is not None:
additional_data["folderId"] = folder_id
upload_to_av(files, additional_data=additional_data)
return ("Uploaded to ArtVenture!",)
class AVCheckpointModelsToParametersPipe:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"ckpt_name": (
folder_paths.get_filename_list("checkpoints"),
),
},
"optional": {
"pipe": ("PIPE",),
"secondary_ckpt_name": (
["None"] + folder_paths.get_filename_list("checkpoints"),
),
"vae_name": (["None"] + folder_paths.get_filename_list("vae"),),
"upscaler_name": (
["None"] + folder_paths.get_filename_list("upscale_models"),
),
"secondary_upscaler_name": (
["None"] + folder_paths.get_filename_list("upscale_models"),
),
"lora_1_name": (["None"] + folder_paths.get_filename_list("loras"),),
"lora_2_name": (["None"] + folder_paths.get_filename_list("loras"),),
"lora_3_name": (["None"] + folder_paths.get_filename_list("loras"),),
},
}
RETURN_TYPES = ("PIPE",)
CATEGORY = "Art Venture/Parameters"
FUNCTION = "checkpoint_models_to_parameter_pipe"
def checkpoint_models_to_parameter_pipe(
self,
ckpt_name,
pipe: Dict = {},
secondary_ckpt_name="None",
vae_name="None",
upscaler_name="None",
secondary_upscaler_name="None",
lora_1_name="None",
lora_2_name="None",
lora_3_name="None",
):
pipe["ckpt_name"] = ckpt_name if ckpt_name != "None" else None
pipe["secondary_ckpt_name"] = (
secondary_ckpt_name if secondary_ckpt_name != "None" else None
)
pipe["vae_name"] = vae_name if vae_name != "None" else None
pipe["upscaler_name"] = upscaler_name if upscaler_name != "None" else None
pipe["secondary_upscaler_name"] = (
secondary_upscaler_name if secondary_upscaler_name != "None" else None
)
pipe["lora_1_name"] = lora_1_name if lora_1_name != "None" else None
pipe["lora_2_name"] = lora_2_name if lora_2_name != "None" else None
pipe["lora_3_name"] = lora_3_name if lora_3_name != "None" else None
return (pipe,)
class AVPromptsToParametersPipe:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"positive": ("STRING", {"multiline": True, "default": "Positive"}),
"negative": ("STRING", {"multiline": True, "default": "Negative"}),
},
"optional": {
"pipe": ("PIPE",),
"image": ("IMAGE",),
"mask": ("MASK",),
},
}
RETURN_TYPES = ("PIPE",)
CATEGORY = "Art Venture/Parameters"
FUNCTION = "prompt_to_parameter_pipe"
def prompt_to_parameter_pipe(
self, positive, negative, pipe: Dict = {}, image=None, mask=None
):
pipe["positive"] = positive
pipe["negative"] = negative
pipe["image"] = image
pipe["mask"] = mask
return (pipe,)
class AVParametersPipeToCheckpointModels:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"pipe": ("PIPE",),
},
}
RETURN_TYPES = (
"PIPE",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
)
RETURN_NAMES = (
"pipe",
"ckpt_name",
"secondary_ckpt_name",
"vae_name",
"upscaler_name",
"secondary_upscaler_name",
"lora_1_name",
"lora_2_name",
"lora_3_name",
)
CATEGORY = "Art Venture/Parameters"
FUNCTION = "parameter_pipe_to_checkpoint_models"
def parameter_pipe_to_checkpoint_models(self, pipe: Dict = {}):
ckpt_name = pipe.get("ckpt_name", None)
secondary_ckpt_name = pipe.get("secondary_ckpt_name", None)
vae_name = pipe.get("vae_name", None)
upscaler_name = pipe.get("upscaler_name", None)
secondary_upscaler_name = pipe.get("secondary_upscaler_name", None)
lora_1_name = pipe.get("lora_1_name", None)
lora_2_name = pipe.get("lora_2_name", None)
lora_3_name = pipe.get("lora_3_name", None)
return (
pipe,
ckpt_name,
secondary_ckpt_name,
vae_name,
upscaler_name,
secondary_upscaler_name,
lora_1_name,
lora_2_name,
lora_3_name,
)
class AVParametersPipeToPrompts:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"pipe": ("PIPE",),
},
}
RETURN_TYPES = (
"PIPE",
"STRING",
"STRING",
"IMAGE",
"MASK",
)
RETURN_NAMES = (
"pipe",
"positive",
"negative",
"image",
"mask",
)
CATEGORY = "Art Venture/Parameters"
FUNCTION = "parameter_pipe_to_prompt"
def parameter_pipe_to_prompt(self, pipe: Dict = {}):
positive = pipe.get("positive", None)
negative = pipe.get("negative", None)
image = pipe.get("image", None)
mask = pipe.get("mask", None)
return (
pipe,
positive,
negative,
image,
mask,
)
NODE_CLASS_MAPPINGS = {
"LoadImageFromUrl": UtilLoadImageFromUrl,
"AV_UploadImage": AVOutputUploadImage,
"AV_CheckpointModelsToParametersPipe": AVCheckpointModelsToParametersPipe,
"AV_PromptsToParametersPipe": AVPromptsToParametersPipe,
"AV_ParametersPipeToCheckpointModels": AVParametersPipeToCheckpointModels,
"AV_ParametersPipeToPrompts": AVParametersPipeToPrompts,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LoadImageFromUrl": "Load Image From URL",
"AV_UploadImage": "Upload to Art Venture",
"AV_CheckpointModelsToParametersPipe": "Checkpoint Models to Pipe",
"AV_PromptsToParametersPipe": "Prompts to Pipe",
"AV_ParametersPipeToCheckpointModels": "Pipe to Checkpoint Models",
"AV_ParametersPipeToPrompts": "Pipe to Prompts",
}