初回コミット
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# ComfyUI-Load-DirectoryFiles
|
||||
|
||||
## 概要
|
||||
|
||||
[ComfyUI](https://github.com/comfyanonymous/ComfyUI)のCustomNode
|
||||
|
||||
指定したディレクトリにあるプロンプト(txt)と画像(png)を読み込みます。
|
||||
indexを指定することで、指定したファイルを出力します。
|
||||
|
||||
これは、計画した画像生成をすることを想定して作りました。
|
||||
プロンプトと画像を合わせて参照することで、プロンプトだけでは描きにくい問題を緩和します。
|
||||
例えば、ControlNetやi2iを使用することでプロンプトを補うことができるため、試行回数を減らすことに繋がります。
|
||||
|
||||
## ファイル形式について
|
||||
|
||||
以下のように、同じ番号で名前を付けたテキストと画像の組み合わせを1つのフォルダに格納します。なお、ネガティブプロンプトも対応させたので、名前に"_n"を付けています
|
||||
|
||||
```
|
||||
# プロンプト
|
||||
001.txt
|
||||
002.txt
|
||||
003.txt
|
||||
|
||||
# 画像
|
||||
001.png
|
||||
002.png
|
||||
003.png
|
||||
|
||||
# ネガティブプロンプト
|
||||
001_n.txt
|
||||
002_n.txt
|
||||
003_n.txt
|
||||
```
|
||||
|
||||
## 前提
|
||||
|
||||
- ComfyUIのインストールを完了していること
|
||||
|
||||
## 導入
|
||||
|
||||
```
|
||||
git clone educator-art/ComfyUI-Load-DirectoryFiles ./custom_node
|
||||
```
|
||||
|
||||
## 応用
|
||||
|
||||
ComfyUIの[basic_api_example.py](https://github.com/comfyanonymous/ComfyUI/blob/master/script_examples/basic_api_example.py)を参考にしてComfyUIのAPIを活用したプログラムを作成し、指定した枚数の画像生成を自動化しました。 これにより、当初の目的を実現できました。
|
||||
|
||||
※ 応用は拡張機能の範囲外となりますので、詳しい方法は割愛します。
|
||||
|
||||
## ライセンス
|
||||
|
||||
- GPL-3.0 License
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
from .nodes.nodes import *
|
||||
from .nodes.nodes_advanced import *
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Load Images and Prompts from Directory": LoadImageAndPromptsFromDirectory,
|
||||
"Load Images and Prompts from Directory(Advanced)": LoadImageAndPromptsFromDirectory_Advanced,
|
||||
}
|
||||
|
||||
print("Load Class: LoadImageAndPromptsFromDirectory")
|
||||
print("Load Class: LoadImageAndPromptsFromDirectory_Advanced")
|
||||
@@ -0,0 +1,84 @@
|
||||
import numpy as np
|
||||
import torch
|
||||
from PIL import Image
|
||||
import os
|
||||
import glob
|
||||
|
||||
class LoadImageAndPromptsFromDirectory:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.max_len = 0
|
||||
self.text_files_path = []
|
||||
self.text_negative_files_path = []
|
||||
self.image_files_path = []
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"dir": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "Please input Directory Path"},
|
||||
),
|
||||
"files_index": (
|
||||
"INT",
|
||||
{"default": 0, "min": 0, "max": 4096},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "IMAGE")
|
||||
FUNCTION = "load_data"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "Load Images and Prompts from Directory"
|
||||
|
||||
def check_directory(self, dir):
|
||||
|
||||
if not os.path.isdir(dir):
|
||||
raise ValueError(f"指定されたディレクトリが存在しません: {dir}")
|
||||
|
||||
image_files = sorted(glob.glob(os.path.join(dir, "*.png")))
|
||||
text_files = sorted(glob.glob(os.path.join(dir, "*.txt")))
|
||||
text_files = [f for f in text_files if not f.endswith('_n.txt')]
|
||||
text_files_negative=sorted(glob.glob(os.path.join(dir, "*_n.txt")))
|
||||
|
||||
if image_files == 0 or text_files == 0:
|
||||
raise RuntimeError("テキストファイルか画像ファイルが存在しません。")
|
||||
|
||||
if len(image_files) != len(text_files):
|
||||
raise ValueError("テキストファイルと画像ファイルの数が一致しません。")
|
||||
|
||||
self.text_files_path = text_files
|
||||
self.text_negative_files_path=text_files_negative
|
||||
self.image_files_path = image_files
|
||||
self.max_len = len(image_files)
|
||||
|
||||
|
||||
def pil_to_tensor(self, image: Image.Image) -> torch.Tensor:
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
def load_data(self, dir, files_index):
|
||||
if self.max_len == 0:
|
||||
self.check_directory(dir)
|
||||
|
||||
text_name = os.path.splitext(
|
||||
os.path.basename(self.text_files_path[files_index])
|
||||
)[0]
|
||||
image_name = os.path.splitext(
|
||||
os.path.basename(self.image_files_path[files_index])
|
||||
)[0]
|
||||
|
||||
if text_name != image_name:
|
||||
raise ValueError("プロンプトと画像のファイル名が一致しません。")
|
||||
|
||||
with open(self.text_files_path[files_index], "r", encoding="utf-8") as file:
|
||||
prompt_string = file.read()
|
||||
|
||||
with open(self.text_negative_files_path[files_index], "r", encoding="utf-8") as file:
|
||||
prompt_negative_string = file.read()
|
||||
|
||||
image = Image.open(self.image_files_path[files_index]).convert("RGB")
|
||||
image_tensor = self.pil_to_tensor(image)
|
||||
|
||||
return (prompt_string, prompt_negative_string, image_tensor)
|
||||
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
2024.10.02 Created
|
||||
Automatic1111 WebUIのBreak文、ComfyUIへPromptをSplitして出力する対応
|
||||
"""
|
||||
import numpy as np
|
||||
import torch
|
||||
from PIL import Image
|
||||
import os
|
||||
import glob
|
||||
|
||||
class LoadImageAndPromptsFromDirectory_Advanced:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.max_len = 0
|
||||
self.text_files_path = []
|
||||
self.text_negative_files_path = []
|
||||
self.image_files_path = []
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"dir": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "Please input Directory Path"},
|
||||
),
|
||||
"files_index": (
|
||||
"INT",
|
||||
{"default": 0, "min": 0, "max": 4096},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
# Automatic 1111 Web UIのPromptのBreak文に対応(Break文:5件対応なので最大、6件のプロンプト文)
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "IMAGE")
|
||||
RETURN_NAMES = ("1st prompt", "2nd prompt", "3rd prompt","4th prompt","5th prompt" , "6th prompt", "negative prompt", "image")
|
||||
FUNCTION = "load_data"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "Load Images and Prompts from Directory"
|
||||
|
||||
def check_directory(self, dir):
|
||||
|
||||
if not os.path.isdir(dir):
|
||||
raise ValueError(f"指定されたディレクトリが存在しません: {dir}")
|
||||
|
||||
image_files = sorted(glob.glob(os.path.join(dir, "*.png")))
|
||||
text_files = sorted(glob.glob(os.path.join(dir, "*.txt")))
|
||||
text_files = [f for f in text_files if not f.endswith('_n.txt')]
|
||||
text_files_negative=sorted(glob.glob(os.path.join(dir, "*_n.txt")))
|
||||
|
||||
if image_files == 0 or text_files == 0:
|
||||
raise RuntimeError("テキストファイルか画像ファイルが存在しません。")
|
||||
|
||||
if len(image_files) != len(text_files):
|
||||
raise ValueError("テキストファイルと画像ファイルの数が一致しません。")
|
||||
|
||||
self.text_files_path = text_files
|
||||
self.text_negative_files_path=text_files_negative
|
||||
self.image_files_path = image_files
|
||||
self.max_len = len(image_files)
|
||||
|
||||
|
||||
def pil_to_tensor(self, image: Image.Image) -> torch.Tensor:
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
def load_data(self, dir, files_index):
|
||||
if self.max_len == 0:
|
||||
self.check_directory(dir)
|
||||
|
||||
text_name = os.path.splitext(
|
||||
os.path.basename(self.text_files_path[files_index])
|
||||
)[0]
|
||||
image_name = os.path.splitext(
|
||||
os.path.basename(self.image_files_path[files_index])
|
||||
)[0]
|
||||
|
||||
if text_name != image_name:
|
||||
raise ValueError("プロンプトと画像のファイル名が一致しません。")
|
||||
|
||||
with open(self.text_files_path[files_index], "r", encoding="utf-8") as file:
|
||||
prompt_string = file.read()
|
||||
|
||||
# 追記
|
||||
prompt_split_string = ["", "", "", "", "", ""]
|
||||
prompt_string = prompt_string.replace("\n", "").strip()
|
||||
segments = [segment for segment in prompt_string.split("BREAK,")]
|
||||
for i in range(min(len(segments), len(prompt_split_string))):
|
||||
prompt_split_string[i] = segments[i]
|
||||
|
||||
with open(self.text_negative_files_path[files_index], "r", encoding="utf-8") as file:
|
||||
prompt_negative_string = file.read()
|
||||
|
||||
image = Image.open(self.image_files_path[files_index]).convert("RGB")
|
||||
image_tensor = self.pil_to_tensor(image)
|
||||
|
||||
# 修正
|
||||
return (prompt_split_string[0],prompt_split_string[1],prompt_split_string[2],prompt_split_string[3],prompt_split_string[4],prompt_split_string[5], prompt_negative_string, image_tensor)
|
||||
|
||||
Reference in New Issue
Block a user