From de7a29b339cd88586b54b171249b35785d20a23c Mon Sep 17 00:00:00 2001 From: EllangoK Date: Tue, 9 May 2023 23:25:37 -0400 Subject: [PATCH] Adds AsciiArt effect makes image composed of ascii characters --- README.md | 1 + post_processing/ascii_art.py | 74 ++++++++++++++++++++++++++++++++++++ post_processing_nodes.py | 71 +++++++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 post_processing/ascii_art.py diff --git a/README.md b/README.md index 9becc40..44f6343 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Both images have the workflow attached, and are included with the repo. Feel fre
- ArithmeticBlend: Blends two images using arithmetic operations like addition, subtraction, and difference. + - AsciiArt: Transforms an image into being composed of ASCII characters - Blend: Blends two images together with a variety of different modes - Blur: Applies a Gaussian blur to the input image, softening the details - CannyEdgeMask: Creates a mask using canny edge detection diff --git a/post_processing/ascii_art.py b/post_processing/ascii_art.py new file mode 100644 index 0000000..df96bd7 --- /dev/null +++ b/post_processing/ascii_art.py @@ -0,0 +1,74 @@ +from PIL import Image, ImageDraw, ImageFont +import numpy as np +import torch + +class AsciiArt: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "char_size": ("INT", { + "default": 12, + "min": 0, + "max": 64, + "step": 2, + }), + "font_size": ("INT", { + "default": 12, + "min": 0, + "max": 64, + "step": 2, + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_ascii_art_effect" + + CATEGORY = "postprocessing/Effects" + + def apply_ascii_art_effect(self, image: torch.Tensor, char_size: int, font_size: int): + batch_size, height, width, channels = image.shape + result = torch.zeros_like(image) + + for b in range(batch_size): + img_b = image[b] * 255.0 + img_b = Image.fromarray(img_b.numpy().astype('uint8'), 'RGB') + result_b = ascii_art_effect(img_b, char_size, font_size) + result_b = torch.tensor(np.array(result_b)) / 255.0 + result[b] = result_b + + return (result,) + + +def ascii_art_effect(image: torch.Tensor, char_size: int, font_size: int): + chars = " .'`^\",:;I1!i><-+_-?][}{1)(|\/tfjrxnuvczXYUCLQ0OZmwqpbdkhao*#MW&8%B@$" + small_image = image.resize((image.size[0] // char_size, image.size[1] // char_size), Image.Resampling.NEAREST) + + def get_char(value): + return chars[value * len(chars) // 256] + + ascii_image = Image.new('RGB', image.size, (0, 0, 0)) + font = ImageFont.truetype("arial.ttf", font_size) + draw_image = ImageDraw.Draw(ascii_image) + + for i in range(small_image.height): + for j in range(small_image.width): + r, g, b = small_image.getpixel((j, i)) + k = (r + g + b) // 3 + draw_image.text( + (j * char_size, i * char_size), + get_char(k), + font=font, + fill=(r, g, b) + ) + + return ascii_image + +NODE_CLASS_MAPPINGS = { + "AsciiArt": AsciiArt, +} \ No newline at end of file diff --git a/post_processing_nodes.py b/post_processing_nodes.py index 7ff5234..4b33877 100644 --- a/post_processing_nodes.py +++ b/post_processing_nodes.py @@ -1,7 +1,8 @@ import torch +from PIL import Image, ImageDraw, ImageFont +import numpy as np import torch.nn.functional as F import cv2 -import numpy as np from PIL import Image, ImageEnhance from PIL import Image @@ -47,6 +48,73 @@ class ArithmeticBlend: def difference(self, img1, img2): return torch.abs(img1 - img2) +class AsciiArt: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "char_size": ("INT", { + "default": 12, + "min": 0, + "max": 64, + "step": 2, + }), + "font_size": ("INT", { + "default": 12, + "min": 0, + "max": 64, + "step": 2, + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_ascii_art_effect" + + CATEGORY = "postprocessing/Effects" + + def apply_ascii_art_effect(self, image: torch.Tensor, char_size: int, font_size: int): + batch_size, height, width, channels = image.shape + result = torch.zeros_like(image) + + for b in range(batch_size): + img_b = image[b] * 255.0 + img_b = Image.fromarray(img_b.numpy().astype('uint8'), 'RGB') + result_b = ascii_art_effect(img_b, char_size, font_size) + result_b = torch.tensor(np.array(result_b)) / 255.0 + result[b] = result_b + + return (result,) + + +def ascii_art_effect(image: torch.Tensor, char_size: int, font_size: int): + chars = " .'`^\",:;I1!i><-+_-?][}{1)(|\/tfjrxnuvczXYUCLQ0OZmwqpbdkhao*#MW&8%B@$" + small_image = image.resize((image.size[0] // char_size, image.size[1] // char_size), Image.Resampling.NEAREST) + + def get_char(value): + return chars[value * len(chars) // 256] + + ascii_image = Image.new('RGB', image.size, (0, 0, 0)) + font = ImageFont.truetype("arial.ttf", font_size) + draw_image = ImageDraw.Draw(ascii_image) + + for i in range(small_image.height): + for j in range(small_image.width): + r, g, b = small_image.getpixel((j, i)) + k = (r + g + b) // 3 + draw_image.text( + (j * char_size, i * char_size), + get_char(k), + font=font, + fill=(r, g, b) + ) + + return ascii_image + class Blend: def __init__(self): pass @@ -1372,6 +1440,7 @@ def pixel_sort(img, mask, horizontal_sort=False, span_limit=None, sort_by='H', r NODE_CLASS_MAPPINGS = { "ArithmeticBlend": ArithmeticBlend, + "AsciiArt": AsciiArt, "Blend": Blend, "Blur": Blur, "CannyEdgeMask": CannyEdgeMask,