""" ComfyUI Pixel Sort Node Integrates ASDFPixelSort Processing script with ComfyUI workflows """ import subprocess import os import tempfile import torch import numpy as np from PIL import Image class PixelSortNode: """ A ComfyUI node that applies pixel sorting effects using the ASDFPixelSort Processing script. """ @classmethod def INPUT_TYPES(cls): return { "required": { "image": ("IMAGE",), "mode": (["white", "black", "bright", "dark"], {"default": "white"}), "white_threshold": ("INT", {"default": -12345678, "min": -16777216, "max": 0, "step": 1000}), "black_threshold": ("INT", {"default": -3456789, "min": -16777216, "max": 0, "step": 1000}), "bright_threshold": ("INT", {"default": 127, "min": 0, "max": 255, "step": 1}), "dark_threshold": ("INT", {"default": 223, "min": 0, "max": 255, "step": 1}), } } RETURN_TYPES = ("IMAGE",) FUNCTION = "apply_pixel_sort" CATEGORY = "image/effects" def __init__(self): # Path to Processing installation self.processing_path = "/root/Processing/processing-4.3/processing-java" self.sketch_path = "/root/Processing/ASDFPixelSort" def apply_pixel_sort(self, image, mode, white_threshold, black_threshold, bright_threshold, dark_threshold): """ Apply pixel sorting to the input image using the Processing script. """ # Convert mode string to number mode_map = {"white": 0, "black": 1, "bright": 2, "dark": 3} mode_num = mode_map[mode] # Create temporary files with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as input_file: input_path = input_file.name with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as output_file: output_path = output_file.name try: # Convert tensor to PIL Image and save img_tensor = image[0] # Remove batch dimension img_array = (img_tensor.cpu().numpy() * 255).astype(np.uint8) img_pil = Image.fromarray(img_array) img_pil.save(input_path) # Prepare command arguments cmd = [ self.processing_path, f"--sketch={self.sketch_path}", "--run", input_path, output_path, str(mode_num), str(white_threshold), str(black_threshold), str(bright_threshold), str(dark_threshold) ] # Set up environment for headless operation env = os.environ.copy() env["DISPLAY"] = ":99" # Run the Processing script print(f"Running pixel sort with mode: {mode}") result = subprocess.run(cmd, env=env, capture_output=True, text=True, timeout=120) if result.returncode != 0: print(f"Processing script error: {result.stderr}") raise RuntimeError(f"Pixel sort failed: {result.stderr}") # Load the processed image if not os.path.exists(output_path): raise RuntimeError("Output image was not created") img_sorted = Image.open(output_path).convert("RGB") img_array = np.array(img_sorted).astype(np.float32) / 255.0 img_tensor = torch.from_numpy(img_array).unsqueeze(0) # Add batch dimension print(f"Pixel sort completed successfully") return (img_tensor,) except subprocess.TimeoutExpired: raise RuntimeError("Pixel sort operation timed out") except Exception as e: raise RuntimeError(f"Pixel sort failed: {str(e)}") finally: # Clean up temporary files try: if os.path.exists(input_path): os.unlink(input_path) if os.path.exists(output_path): os.unlink(output_path) except: pass class PixelSortAdvancedNode: """ Advanced pixel sort node with more control options. """ @classmethod def INPUT_TYPES(cls): return { "required": { "image": ("IMAGE",), "mode": (["white", "black", "bright", "dark"], {"default": "white"}), }, "optional": { "white_threshold": ("INT", {"default": -12345678, "min": -16777216, "max": 0, "step": 1000}), "black_threshold": ("INT", {"default": -3456789, "min": -16777216, "max": 0, "step": 1000}), "bright_threshold": ("INT", {"default": 127, "min": 0, "max": 255, "step": 1}), "dark_threshold": ("INT", {"default": 223, "min": 0, "max": 255, "step": 1}), "custom_sketch_path": ("STRING", {"default": ""}), } } RETURN_TYPES = ("IMAGE",) FUNCTION = "apply_pixel_sort_advanced" CATEGORY = "image/effects" def apply_pixel_sort_advanced(self, image, mode, white_threshold=-12345678, black_threshold=-3456789, bright_threshold=127, dark_threshold=223, custom_sketch_path=""): """ Advanced pixel sorting with optional custom sketch path. """ node = PixelSortNode() if custom_sketch_path: node.sketch_path = custom_sketch_path return node.apply_pixel_sort(image, mode, white_threshold, black_threshold, bright_threshold, dark_threshold) # Node registration NODE_CLASS_MAPPINGS = { "PixelSortNode": PixelSortNode, "PixelSortAdvancedNode": PixelSortAdvancedNode } NODE_DISPLAY_NAME_MAPPINGS = { "PixelSortNode": "Pixel Sort (ASDF)", "PixelSortAdvancedNode": "Pixel Sort Advanced (ASDF)" }