Skip to content

Commit de7a29b

Browse files
committed
Adds AsciiArt effect
makes image composed of ascii characters
1 parent 7dfa597 commit de7a29b

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Both images have the workflow attached, and are included with the repo. Feel fre
1919
<br/>
2020

2121
- ArithmeticBlend: Blends two images using arithmetic operations like addition, subtraction, and difference.
22+
- AsciiArt: Transforms an image into being composed of ASCII characters
2223
- Blend: Blends two images together with a variety of different modes
2324
- Blur: Applies a Gaussian blur to the input image, softening the details
2425
- CannyEdgeMask: Creates a mask using canny edge detection

post_processing/ascii_art.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from PIL import Image, ImageDraw, ImageFont
2+
import numpy as np
3+
import torch
4+
5+
class AsciiArt:
6+
def __init__(self):
7+
pass
8+
9+
@classmethod
10+
def INPUT_TYPES(s):
11+
return {
12+
"required": {
13+
"image": ("IMAGE",),
14+
"char_size": ("INT", {
15+
"default": 12,
16+
"min": 0,
17+
"max": 64,
18+
"step": 2,
19+
}),
20+
"font_size": ("INT", {
21+
"default": 12,
22+
"min": 0,
23+
"max": 64,
24+
"step": 2,
25+
}),
26+
},
27+
}
28+
29+
RETURN_TYPES = ("IMAGE",)
30+
FUNCTION = "apply_ascii_art_effect"
31+
32+
CATEGORY = "postprocessing/Effects"
33+
34+
def apply_ascii_art_effect(self, image: torch.Tensor, char_size: int, font_size: int):
35+
batch_size, height, width, channels = image.shape
36+
result = torch.zeros_like(image)
37+
38+
for b in range(batch_size):
39+
img_b = image[b] * 255.0
40+
img_b = Image.fromarray(img_b.numpy().astype('uint8'), 'RGB')
41+
result_b = ascii_art_effect(img_b, char_size, font_size)
42+
result_b = torch.tensor(np.array(result_b)) / 255.0
43+
result[b] = result_b
44+
45+
return (result,)
46+
47+
48+
def ascii_art_effect(image: torch.Tensor, char_size: int, font_size: int):
49+
chars = " .'`^\",:;I1!i><-+_-?][}{1)(|\/tfjrxnuvczXYUCLQ0OZmwqpbdkhao*#MW&8%B@$"
50+
small_image = image.resize((image.size[0] // char_size, image.size[1] // char_size), Image.Resampling.NEAREST)
51+
52+
def get_char(value):
53+
return chars[value * len(chars) // 256]
54+
55+
ascii_image = Image.new('RGB', image.size, (0, 0, 0))
56+
font = ImageFont.truetype("arial.ttf", font_size)
57+
draw_image = ImageDraw.Draw(ascii_image)
58+
59+
for i in range(small_image.height):
60+
for j in range(small_image.width):
61+
r, g, b = small_image.getpixel((j, i))
62+
k = (r + g + b) // 3
63+
draw_image.text(
64+
(j * char_size, i * char_size),
65+
get_char(k),
66+
font=font,
67+
fill=(r, g, b)
68+
)
69+
70+
return ascii_image
71+
72+
NODE_CLASS_MAPPINGS = {
73+
"AsciiArt": AsciiArt,
74+
}

post_processing_nodes.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import torch
2+
from PIL import Image, ImageDraw, ImageFont
3+
import numpy as np
24
import torch.nn.functional as F
35
import cv2
4-
import numpy as np
56
from PIL import Image, ImageEnhance
67
from PIL import Image
78

@@ -47,6 +48,73 @@ def subtract(self, img1, img2):
4748
def difference(self, img1, img2):
4849
return torch.abs(img1 - img2)
4950

51+
class AsciiArt:
52+
def __init__(self):
53+
pass
54+
55+
@classmethod
56+
def INPUT_TYPES(s):
57+
return {
58+
"required": {
59+
"image": ("IMAGE",),
60+
"char_size": ("INT", {
61+
"default": 12,
62+
"min": 0,
63+
"max": 64,
64+
"step": 2,
65+
}),
66+
"font_size": ("INT", {
67+
"default": 12,
68+
"min": 0,
69+
"max": 64,
70+
"step": 2,
71+
}),
72+
},
73+
}
74+
75+
RETURN_TYPES = ("IMAGE",)
76+
FUNCTION = "apply_ascii_art_effect"
77+
78+
CATEGORY = "postprocessing/Effects"
79+
80+
def apply_ascii_art_effect(self, image: torch.Tensor, char_size: int, font_size: int):
81+
batch_size, height, width, channels = image.shape
82+
result = torch.zeros_like(image)
83+
84+
for b in range(batch_size):
85+
img_b = image[b] * 255.0
86+
img_b = Image.fromarray(img_b.numpy().astype('uint8'), 'RGB')
87+
result_b = ascii_art_effect(img_b, char_size, font_size)
88+
result_b = torch.tensor(np.array(result_b)) / 255.0
89+
result[b] = result_b
90+
91+
return (result,)
92+
93+
94+
def ascii_art_effect(image: torch.Tensor, char_size: int, font_size: int):
95+
chars = " .'`^\",:;I1!i><-+_-?][}{1)(|\/tfjrxnuvczXYUCLQ0OZmwqpbdkhao*#MW&8%B@$"
96+
small_image = image.resize((image.size[0] // char_size, image.size[1] // char_size), Image.Resampling.NEAREST)
97+
98+
def get_char(value):
99+
return chars[value * len(chars) // 256]
100+
101+
ascii_image = Image.new('RGB', image.size, (0, 0, 0))
102+
font = ImageFont.truetype("arial.ttf", font_size)
103+
draw_image = ImageDraw.Draw(ascii_image)
104+
105+
for i in range(small_image.height):
106+
for j in range(small_image.width):
107+
r, g, b = small_image.getpixel((j, i))
108+
k = (r + g + b) // 3
109+
draw_image.text(
110+
(j * char_size, i * char_size),
111+
get_char(k),
112+
font=font,
113+
fill=(r, g, b)
114+
)
115+
116+
return ascii_image
117+
50118
class Blend:
51119
def __init__(self):
52120
pass
@@ -1372,6 +1440,7 @@ def pixel_sort(img, mask, horizontal_sort=False, span_limit=None, sort_by='H', r
13721440

13731441
NODE_CLASS_MAPPINGS = {
13741442
"ArithmeticBlend": ArithmeticBlend,
1443+
"AsciiArt": AsciiArt,
13751444
"Blend": Blend,
13761445
"Blur": Blur,
13771446
"CannyEdgeMask": CannyEdgeMask,

0 commit comments

Comments
 (0)