From 45251efebaa86e10ee1a9be2838bb54d1027e1a4 Mon Sep 17 00:00:00 2001 From: Fill <55672949+filliptm@users.noreply.github.com> Date: Wed, 15 May 2024 14:55:39 +0900 Subject: [PATCH] Add files via upload --- __init__.py | 18 ++++++++-- fl_halftone.py | 69 +++++++++++++++++++++++++++++++++++ fl_nftgenerator.py | 85 ++++++++++++++++++++++++++++++++++++++++++++ fl_promptselector.py | 36 +++++++++++++++++++ fl_randomrange.py | 38 ++++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 fl_halftone.py create mode 100644 fl_nftgenerator.py create mode 100644 fl_promptselector.py create mode 100644 fl_randomrange.py diff --git a/__init__.py b/__init__.py index aed2dd0..28303f4 100644 --- a/__init__.py +++ b/__init__.py @@ -13,6 +13,10 @@ from .fl_glitch import FL_Glitch from .fl_ripple import FL_Ripple from .fl_pixelsort import FL_PixelSort from .fl_hexagonalpattern import FL_HexagonalPattern +from .fl_nftgenerator import FL_NFTGenerator +from .fl_halftone import FL_HalftonePattern +from. fl_randomrange import FL_RandomNumber +from. fl_promptselector import FL_PromptSelector @@ -31,7 +35,11 @@ NODE_CLASS_MAPPINGS = { "FL_Glitch": FL_Glitch, "FL_Ripple": FL_Ripple, "FL_PixelSort": FL_PixelSort, - "FL_HexagonalPattern": FL_HexagonalPattern + "FL_HexagonalPattern": FL_HexagonalPattern, + "FL_NFTGenerator": FL_NFTGenerator, + "FL_HalftonePattern": FL_HalftonePattern, + "FL_RandomNumber": FL_RandomNumber, + "FL_PromptSelector": FL_PromptSelector } NODE_DISPLAY_NAME_MAPPINGS = { @@ -44,12 +52,16 @@ NODE_DISPLAY_NAME_MAPPINGS = { "FL_AudioFrameCalculator": "FL Audio Scanner", "FL_CodeNode": "FL Code Node", "FL_ImagePixelator": "FL Image Pixelator", - "FL_DirectoryCrawl": "FL DirectoryCrawl", + "FL_DirectoryCrawl": "FL Directory Crawl", "FL_Ascii": "FL Ascii", "FL_Glitch": "FL Glitch", "FL_Ripple": "FL Ripple", "FL_PixelSort": "FL PixelSort", - "FL_HexagonalPattern": "FL Hexagonal Pattern" + "FL_HexagonalPattern": "FL Hexagonal Pattern", + "FL_NFTGenerator": "FL NFT Generator", + "FL_HalftonePattern": "FL Halftone", + "FL_RandomNumber": "FL Random Number", + "FL_PromptSelector": "FL Prompt Selector" } diff --git a/fl_halftone.py b/fl_halftone.py new file mode 100644 index 0000000..a3568e4 --- /dev/null +++ b/fl_halftone.py @@ -0,0 +1,69 @@ +import torch +import numpy as np +import sys + +class FL_HalftonePattern: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + "optional": { + "dot_size": ("INT", {"default": 5, "min": 1, "max": 20, "step": 1}), + "dot_spacing": ("INT", {"default": 10, "min": 5, "max": 50, "step": 1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "halftone_pattern" + CATEGORY = "🏵️Fill Nodes" + + def halftone_pattern(self, images, dot_size=5, dot_spacing=10): + out = [] + total_images = len(images) + for i, img in enumerate(images, start=1): + img_np = img.cpu().numpy().squeeze() + grayscale_image = np.dot(img_np[..., :3], [0.299, 0.587, 0.114]) + height, width = grayscale_image.shape + + halftone_image = np.ones((height, width), dtype=np.float32) + + for y in range(0, height, dot_spacing): + for x in range(0, width, dot_spacing): + box = (x, y, x + dot_spacing, y + dot_spacing) + region_mean = np.mean(grayscale_image[box[1]:box[3], box[0]:box[2]]) + dot_radius = int((1 - region_mean) * dot_size / 2) + dot_position = (x + dot_spacing // 2, y + dot_spacing // 2) + + # Create a circular mask for the dot + y_grid, x_grid = np.ogrid[-dot_radius:dot_radius + 1, -dot_radius:dot_radius + 1] + mask = x_grid ** 2 + y_grid ** 2 <= dot_radius ** 2 + + # Apply the dot mask to the halftone image + y_start = max(0, dot_position[1] - dot_radius) + y_end = min(height, dot_position[1] + dot_radius + 1) + x_start = max(0, dot_position[0] - dot_radius) + x_end = min(width, dot_position[0] + dot_radius + 1) + + # Ensure the mask dimensions match the sliced halftone image dimensions + mask_height = y_end - y_start + mask_width = x_end - x_start + mask = mask[:mask_height, :mask_width] + + halftone_image[y_start:y_end, x_start:x_end][mask] = 0 + + o = np.stack((halftone_image,) * 3, axis=-1) + o = torch.from_numpy(o).unsqueeze(0) + out.append(o) + + # Print progress update + progress = i / total_images * 100 + sys.stdout.write(f"\rProcessing images: {progress:.2f}%") + sys.stdout.flush() + + # Print a new line after the progress update + print() + + out = torch.cat(out, 0) + return (out,) \ No newline at end of file diff --git a/fl_nftgenerator.py b/fl_nftgenerator.py new file mode 100644 index 0000000..beffb4d --- /dev/null +++ b/fl_nftgenerator.py @@ -0,0 +1,85 @@ +import os +import random +from PIL import Image +import torch +import numpy as np + +class FL_NFTGenerator: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "folder_path": ("STRING", {"default": ""}), + "dummy_seed": ("INT", {"default": 0, "min": 0, "max": 1000000}), + } + } + + RETURN_TYPES = ("IMAGE", "IMAGE") + FUNCTION = "generate_nft" + CATEGORY = "🏵️Fill Nodes" + + def t2p(self, t): + if t is not None: + i = 255.0 * t.cpu().numpy().squeeze() + p = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return p + + def generate_nft(self, folder_path, dummy_seed): + if not os.path.exists(folder_path): + raise ValueError(f"Folder path does not exist: {folder_path}") + + image_files = [f for f in os.listdir(folder_path) if not f.lower().endswith("-mask.png") and not f.lower().endswith("-mask.jpg") and not f.lower().endswith("-mask.jpeg")] + if not image_files: + raise ValueError(f"No image files found in the folder: {folder_path}") + + # Extract rarity percentages from image filenames + rarities = [] + for image_file in image_files: + if "-" in image_file: + rarity_str = image_file.split("-")[1].split("per")[0] + rarity = int(rarity_str) + rarities.append(rarity) + else: + raise ValueError(f"Invalid image filename format: {image_file}") + + # Calculate cumulative probabilities + total_rarity = sum(rarities) + probabilities = [rarity / total_rarity for rarity in rarities] + cumulative_probabilities = [sum(probabilities[:i+1]) for i in range(len(probabilities))] + + # Generate a random number between 0 and 1 using the dummy seed + random.seed(dummy_seed) + random_number = random.random() + + # Find the index of the selected image based on the random number and cumulative probabilities + selected_index = None + for i, prob in enumerate(cumulative_probabilities): + if random_number <= prob: + selected_index = i + break + + if selected_index is None: + raise ValueError("Failed to select an image based on rarity.") + + # Get the selected image and its corresponding mask + selected_image_file = image_files[selected_index] + selected_image_path = os.path.join(folder_path, selected_image_file) + selected_image = Image.open(selected_image_path) + + # Get the file extension of the selected image + _, extension = os.path.splitext(selected_image_file) + + # Generate the mask filename based on the selected image filename + mask_file = selected_image_file.rsplit(".", 1)[0] + "-mask" + extension + mask_path = os.path.join(folder_path, mask_file) + + if os.path.exists(mask_path): + mask_image = Image.open(mask_path) + else: + # Create a blank mask image if the corresponding mask is not found + mask_image = Image.new("RGB", selected_image.size, (0, 0, 0)) + + selected_image_tensor = torch.from_numpy(np.array(selected_image).astype(np.float32) / 255.0).unsqueeze(0) + mask_image_tensor = torch.from_numpy(np.array(mask_image).astype(np.float32) / 255.0).unsqueeze(0) + + return (selected_image_tensor, mask_image_tensor) \ No newline at end of file diff --git a/fl_promptselector.py b/fl_promptselector.py new file mode 100644 index 0000000..b9f8719 --- /dev/null +++ b/fl_promptselector.py @@ -0,0 +1,36 @@ +class FL_PromptSelector: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "prepend_text": ("STRING", {"multiline": True, "default": ""}), + "prompts": ("STRING", {"multiline": True}), + "append_text": ("STRING", {"multiline": True, "default": ""}), + "index": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}), + }, + "optional": {}, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "select_prompt" + CATEGORY = "🏵️Fill Nodes" + + def select_prompt(self, prepend_text, prompts, append_text, index): + prepend_text = prepend_text.strip() + prompt_lines = prompts.split("\n") + append_text = append_text.strip() + + num_prompts = len(prompt_lines) + + if index < 0 or index >= num_prompts: + raise ValueError(f"Index {index} is out of range. Please provide an index between 0 and {num_prompts - 1}.") + + selected_prompt = prompt_lines[index].strip() + + if prepend_text: + selected_prompt = prepend_text + " " + selected_prompt + + if append_text: + selected_prompt = selected_prompt + " " + append_text + + return (selected_prompt,) \ No newline at end of file diff --git a/fl_randomrange.py b/fl_randomrange.py new file mode 100644 index 0000000..cba1a61 --- /dev/null +++ b/fl_randomrange.py @@ -0,0 +1,38 @@ +import random +import torch + +class FL_RandomNumber: + @classmethod + def INPUT_TYPES(cls): + return { + "required": {}, + "optional": { + "min_value": ("FLOAT", {"default": 0.0, "min": -1000000.0, "max": 1000000.0, "step": 0.1}), + "max_value": ("FLOAT", {"default": 1.0, "min": -1000000.0, "max": 1000000.0, "step": 0.1}), + "seed": ("INT", {"default": 0, "min": 0, "max": 1000000}), + }, + } + + RETURN_TYPES = ("INT", "FLOAT") + FUNCTION = "generate_random_number" + CATEGORY = "🏵️Fill Nodes" + + def generate_random_number(self, min_value=0.0, max_value=1.0, seed=0): + if min_value > max_value: + raise ValueError("min_value should be less than or equal to max_value") + + # Generate a random seed if seed is 0 + if seed == 0: + seed = random.randint(1, 1000000) + + # Set the random seed for reproducibility + random.seed(seed) + torch.manual_seed(seed) + + # Generate a random float value within the specified range + random_float = min_value + (max_value - min_value) * random.random() + + # Generate a random integer value within the specified range + random_int = int(min_value + (max_value - min_value) * random.random()) + + return (random_int, random_float) \ No newline at end of file