add combined_nodes file and generator
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
# ComfyUI-post-processing-nodes
|
# ComfyUI-post-processing-nodes
|
||||||
|
|
||||||
A collection of post processing nodes for [ComfyUI](https://github.com/comfyanonymous/ComfyUI), simply download this repo and drag the nodes into your `custom_nodes/` folder
|
A collection of post processing nodes for [ComfyUI](https://github.com/comfyanonymous/ComfyUI), simply download this repo and drag `combined_nodes.py` into your `custom_nodes/` folder
|
||||||
|
|
||||||
|
## Combine Nodes
|
||||||
|
|
||||||
|
By default `combined_nodes.py` should have all of the combined nodes. If you want a subset of nodes, you can run
|
||||||
|
|
||||||
|
python combine_files.py [--files FILES [FILES ...]] [--output OUTPUT]
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import ast
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_files(path):
|
||||||
|
for file in Path(path).glob("*.py"):
|
||||||
|
if file.is_file() and not file.name.startswith("combine"):
|
||||||
|
yield str(file)
|
||||||
|
|
||||||
|
def parse_files(files):
|
||||||
|
imports = set()
|
||||||
|
class_definitions = set()
|
||||||
|
node_class_mappings = set()
|
||||||
|
functions = set()
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
# read file as lines
|
||||||
|
with open(file, "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# remove comments
|
||||||
|
lines = [line for line in lines if not line.startswith("#")]
|
||||||
|
|
||||||
|
num_lines = len(lines)
|
||||||
|
i = 0
|
||||||
|
while i < num_lines:
|
||||||
|
line = lines[i]
|
||||||
|
if line.startswith("import") or line.startswith("from"):
|
||||||
|
imports.add(line.strip())
|
||||||
|
|
||||||
|
elif line.startswith("class"):
|
||||||
|
class_info = line
|
||||||
|
j = i + 1
|
||||||
|
while not lines[j].startswith("NODE_CLASS_MAPPINGS"):
|
||||||
|
class_info += lines[j]
|
||||||
|
j += 1
|
||||||
|
class_definitions.add(class_info)
|
||||||
|
i = j - 1
|
||||||
|
|
||||||
|
elif line.startswith("NODE_CLASS_MAPPINGS"):
|
||||||
|
node_class_mappings.add(lines[i+1])
|
||||||
|
|
||||||
|
elif line.startswith("def"):
|
||||||
|
function_info = line
|
||||||
|
j = i + 1
|
||||||
|
while j < num_lines and not lines[j].startswith("NODE_CLASS_MAPPINGS") and not lines[j].startswith("def"):
|
||||||
|
function_info += lines[j]
|
||||||
|
j += 1
|
||||||
|
functions.add(function_info)
|
||||||
|
i = j - 1
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return imports, class_definitions, node_class_mappings, functions
|
||||||
|
|
||||||
|
def write_combined(imports: list[str], class_definitions: list[str], node_class_mappings: list[str], functions: list[str], output_file: str):
|
||||||
|
with open(output_file, "w") as f:
|
||||||
|
# write imports
|
||||||
|
for line in imports:
|
||||||
|
f.write(line + "\n")
|
||||||
|
|
||||||
|
# write 2 blank lines
|
||||||
|
f.write("\n\n")
|
||||||
|
|
||||||
|
# write class definitions
|
||||||
|
for line in class_definitions:
|
||||||
|
f.write(line)
|
||||||
|
|
||||||
|
# write functions
|
||||||
|
for line in functions:
|
||||||
|
f.write(line + "\n")
|
||||||
|
|
||||||
|
# write node class mappings
|
||||||
|
f.write("NODE_CLASS_MAPPINGS = {\n")
|
||||||
|
for line in node_class_mappings:
|
||||||
|
if not line.endswith(",\n"):
|
||||||
|
if line.endswith("\n"):
|
||||||
|
line = line[:-1] + ",\n"
|
||||||
|
else:
|
||||||
|
line += ",\n"
|
||||||
|
|
||||||
|
f.write(line)
|
||||||
|
f.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Collect unique imports from Python files")
|
||||||
|
parser.add_argument("--all", action="store_true", help="Include all Python files in the specified directory")
|
||||||
|
parser.add_argument("--files", nargs="+", help="Specify Python files to parse")
|
||||||
|
parser.add_argument("--output", default="combined_nodes.py", help="Specify the output file name")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.all:
|
||||||
|
args.path = "."
|
||||||
|
files = get_python_files(args.path)
|
||||||
|
imports, class_definitions, node_class_mappings, functions = parse_files(files)
|
||||||
|
write_combined(imports, class_definitions, node_class_mappings, functions, args.output)
|
||||||
|
else:
|
||||||
|
if args.files is None:
|
||||||
|
print("No files specified")
|
||||||
|
return
|
||||||
|
|
||||||
|
imports, class_definitions, node_class_mappings, functions = parse_files(args.files)
|
||||||
|
write_combined(imports, class_definitions, node_class_mappings, functions, args.output)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
|
||||||
|
class CannyEdgeDetection:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
"lower_threshold": ("INT", {
|
||||||
|
"default": 100,
|
||||||
|
"min": 0,
|
||||||
|
"max": 500,
|
||||||
|
"step": 10
|
||||||
|
}),
|
||||||
|
"upper_threshold": ("INT", {
|
||||||
|
"default": 200,
|
||||||
|
"min": 0,
|
||||||
|
"max": 500,
|
||||||
|
"step": 10
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
FUNCTION = "canny"
|
||||||
|
|
||||||
|
CATEGORY = "postprocessing"
|
||||||
|
|
||||||
|
def canny(self, image: torch.Tensor, lower_threshold: int, upper_threshold: int):
|
||||||
|
batch_size, height, width, _ = image.shape
|
||||||
|
result = torch.zeros(batch_size, height, width)
|
||||||
|
|
||||||
|
for b in range(batch_size):
|
||||||
|
tensor_image = image[b].numpy().copy()
|
||||||
|
gray_image = (cv2.cvtColor(tensor_image, cv2.COLOR_RGB2GRAY) * 255).astype(np.uint8)
|
||||||
|
canny = cv2.Canny(gray_image, lower_threshold, upper_threshold)
|
||||||
|
tensor = torch.from_numpy(canny)
|
||||||
|
result[b] = tensor
|
||||||
|
|
||||||
|
return (result,)
|
||||||
|
|
||||||
|
class GaussianBlur:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
"kernel_size": ("INT", {
|
||||||
|
"default": 5,
|
||||||
|
"min": 1,
|
||||||
|
"max": 31,
|
||||||
|
"step": 1
|
||||||
|
}),
|
||||||
|
"sigma": ("FLOAT", {
|
||||||
|
"default": 1.0,
|
||||||
|
"min": 0.1,
|
||||||
|
"max": 10.0,
|
||||||
|
"step": 0.1
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
FUNCTION = "blur"
|
||||||
|
|
||||||
|
CATEGORY = "postprocessing"
|
||||||
|
|
||||||
|
def blur(self, image: torch.Tensor, kernel_size: int, sigma: float):
|
||||||
|
batch_size, height, width, _ = image.shape
|
||||||
|
result = torch.zeros_like(image)
|
||||||
|
|
||||||
|
for b in range(batch_size):
|
||||||
|
tensor_image = image[b].numpy()
|
||||||
|
blurred = cv2.GaussianBlur(tensor_image, (kernel_size, kernel_size), sigma)
|
||||||
|
tensor = torch.from_numpy(blurred).unsqueeze(0)
|
||||||
|
result[b] = tensor
|
||||||
|
|
||||||
|
return (result,)
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"CannyEdgeDetection": CannyEdgeDetection,
|
||||||
|
"GaussianBlur": GaussianBlur,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user