From 6fa31720c2143210eb0368d4e3e8d31c22ba9421 Mon Sep 17 00:00:00 2001 From: JISAUAY Date: Wed, 11 Sep 2024 17:56:43 -0500 Subject: [PATCH] CITF named --- .gitignore | 3 +- sobel.py => asciify.py | 452 ++++++++++++++++++++--------------------- ctif.py | 165 +++++++++++++++ main.py | 18 ++ 4 files changed, 405 insertions(+), 233 deletions(-) rename sobel.py => asciify.py (90%) create mode 100644 ctif.py create mode 100644 main.py diff --git a/.gitignore b/.gitignore index f399bde..8781435 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.png -*.jpg \ No newline at end of file +*.jpg +__pycache__ \ No newline at end of file diff --git a/sobel.py b/asciify.py similarity index 90% rename from sobel.py rename to asciify.py index 8f459f6..b4ca3f8 100644 --- a/sobel.py +++ b/asciify.py @@ -1,232 +1,220 @@ -# import scipy.ndimage as nd -# import imageio.v3 as iio -# import numpy as np -import logging -import math -import os - -from PIL import Image -import pygame as pg - -import filters - -FORMAT = "%(levelname)s %(filename)s - %(message)s" -logging.basicConfig(level=logging.DEBUG, format=FORMAT) - -IMAGE_PATH = 'sample-images/engine.PNG' -FONT_PATH = 'fonts/scientifica.ttf' - - -WHITE = (255,255,255) - -TEXT_WIDTH = 6 # on font size 12 monocraft medium -TEXT_HEIGHT = 11 - -SCALE_FACTOR = 2 -RESIZE_FACTOR = 2 - -SCALED_WIDTH = TEXT_WIDTH // SCALE_FACTOR -SCALED_HEIGHT = TEXT_HEIGHT // SCALE_FACTOR - -DEBUG = True - -if not os.path.exists('./debug/') and DEBUG: - os.makedirs('./debug') - -# '█@?OPoci. ' -CHARS = list(reversed([ - u'█', - '@', - '?', - 'O', - 'P', - 'o', - 'c', - 'i', - '.', - ' ' -])) - -def main(): - - image = Image.open(IMAGE_PATH) - image = image.resize((image.width * RESIZE_FACTOR, image.height * RESIZE_FACTOR)) - logging.debug(f'Image Size: {image.size}') - - L_image = image.convert('L') - logging.debug(f'L_image Size: {L_image.size}') - - dog = filters.difference_of_gaussians(image) - logging.debug(f'Dog Size: {dog.size}') - - dog.save('dog.png') - - sb, gradient = filters.sobel(dog) - - w, h = sb.size - - sb.save('sobel.png') - - text_grid_width = w / SCALED_WIDTH - text_grind_height = h / SCALED_HEIGHT - - pg.init() - font = pg.font.Font(FONT_PATH, 12) - window = pg.display.set_mode((text_grid_width * TEXT_WIDTH, text_grind_height * TEXT_HEIGHT)) - - y_offset = 0 - - for y in range(math.floor(h / SCALED_HEIGHT)): - x_offset = 0 - - for x in range(math.floor(w / SCALED_WIDTH)): - histogram = {} - - # Collect the most common char for a group of pixels - colors = [] - - for y2 in range(SCALED_HEIGHT): - for x2 in range(SCALED_WIDTH): - - real_x = x2 + x_offset - real_y = y2 + y_offset - - gradient_v = gradient[real_y * w + real_x] - - char = ' ' - - if gradient_v: - char = match_gradient(gradient_v) - else: - char = CHARS[round(L_image.getpixel((real_x, real_y))/(255/len(CHARS))) - 1] - - if char in histogram: - histogram[char] += 1 - else: - histogram[char] = 1 - - colors.append(image.getpixel((real_x, real_y))) - - color_avg = average_colors(colors) - - # get most common - most_common = None - score = float('-inf') - for char in histogram: - if histogram[char] > score: - score = histogram[char] - most_common = char - - rendered_char = font.render(str(most_common), True, color_avg) - window.blit(rendered_char, (x * TEXT_WIDTH, y * TEXT_HEIGHT)) - - x_offset += SCALED_WIDTH - - y_offset += SCALED_HEIGHT - - pg.display.update() - pg.image.save(window, "render.png") - pg.quit() - - if DEBUG: - sb.save('debug/sobel-dog.png') - dog.save('debug/dog.png') - - # Draw only the ASCII edges - pg.init() - font = pg.font.Font(FONT_PATH, 12) - window = pg.display.set_mode((text_grid_width * TEXT_WIDTH, text_grind_height * TEXT_HEIGHT)) - - y_offset = 0 - - for y in range(math.floor(h / SCALED_HEIGHT)): - x_offset = 0 - - for x in range(math.floor(w / SCALED_WIDTH)): - histogram = {} - - # Collect the most common char for a group of pixels - colors = [] - - for y2 in range(SCALED_HEIGHT): - for x2 in range(SCALED_WIDTH): - - real_x = x2 + x_offset - real_y = y2 + y_offset - - gradient_v = gradient[real_y * w + real_x] - - char = ' ' - - if gradient_v: - char = match_gradient(gradient_v) - else: - char = ' ' - - if char in histogram: - histogram[char] += 1 - else: - histogram[char] = 1 - - colors.append(image.getpixel((real_x, real_y))) - - color_avg = average_colors(colors) - - # get most common - most_common = None - score = float('-inf') - for char in histogram: - if histogram[char] > score: - score = histogram[char] - most_common = char - - rendered_char = font.render(most_common, True, color_avg) - window.blit(rendered_char, (x * TEXT_WIDTH, y * TEXT_HEIGHT)) - - x_offset += SCALED_WIDTH - - y_offset += SCALED_HEIGHT - - pg.display.update() - pg.image.save(window, "debug/edges-render.png") - pg.quit() - -# TODO @0x01FE : refactor plz increment by 30 & 60 -def match_gradient(n: float) -> str: - - n = abs(n) - - if n < math.radians(30): - return '|' - elif n < math.radians(60): - return '/' - elif n < math.radians(120): - return '-' - elif n < math.radians(150): - return '\\' - elif n < math.radians(210): - return '|' - elif n < math.radians(240): - return '/' - elif n < math.radians(300): - return '-' - elif n < math.radians(330): - return '\\' - else: - return '|' - -def average_colors(colors: list): - avg = [0 for x in range(len(colors[0]))] - - for color in colors: - for i, num in enumerate(color): - avg[i] += num - - for i, n in enumerate(avg): - avg[i] /= len(colors) - - return avg - -if __name__ == '__main__': - main() - +import logging +import math +import os + +from PIL import Image +import pygame as pg + +import filters + +logging.getLogger(__name__) + +FONT_PATH = 'fonts/scientifica.ttf' + +TEXT_WIDTH = 6 # on font size 12 monocraft medium +TEXT_HEIGHT = 11 + +SCALE_FACTOR = 2 +RESIZE_FACTOR = 2 + +SCALED_WIDTH = TEXT_WIDTH // SCALE_FACTOR +SCALED_HEIGHT = TEXT_HEIGHT // SCALE_FACTOR + +DEBUG = True + +if not os.path.exists('./debug/') and DEBUG: + os.makedirs('./debug') + +# '█@?OPoci. ' +CHARS = list(reversed([ + u'█', + '@', + '?', + 'O', + 'P', + 'o', + 'c', + 'i', + '.', + ' ' +])) + + +def asciify(image: Image.Image) -> str: + + image = image.resize((image.width * RESIZE_FACTOR, image.height * RESIZE_FACTOR)) + logging.debug(f'Image Size: {image.size}') + + L_image = image.convert('L') + logging.debug(f'L_image Size: {L_image.size}') + + dog = filters.difference_of_gaussians(image) + logging.debug(f'Dog Size: {dog.size}') + + dog.save('dog.png') + + sb, gradient = filters.sobel(dog) + + w, h = sb.size + + sb.save('sobel.png') + + text_grid_width = w / SCALED_WIDTH + text_grind_height = h / SCALED_HEIGHT + + pg.init() + font = pg.font.Font(FONT_PATH, 12) + window = pg.display.set_mode((text_grid_width * TEXT_WIDTH, text_grind_height * TEXT_HEIGHT)) + + y_offset = 0 + + for y in range(math.floor(h / SCALED_HEIGHT)): + x_offset = 0 + + for x in range(math.floor(w / SCALED_WIDTH)): + histogram = {} + + # Collect the most common char for a group of pixels + colors = [] + + for y2 in range(SCALED_HEIGHT): + for x2 in range(SCALED_WIDTH): + + real_x = x2 + x_offset + real_y = y2 + y_offset + + gradient_v = gradient[real_y * w + real_x] + + char = ' ' + + if gradient_v: + char = match_gradient(gradient_v) + else: + char = CHARS[round(L_image.getpixel((real_x, real_y))/(255/len(CHARS))) - 1] + + if char in histogram: + histogram[char] += 1 + else: + histogram[char] = 1 + + colors.append(image.getpixel((real_x, real_y))) + + color_avg = average_colors(colors) + + # get most common + most_common = None + score = float('-inf') + for char in histogram: + if histogram[char] > score: + score = histogram[char] + most_common = char + + rendered_char = font.render(str(most_common), True, color_avg) + window.blit(rendered_char, (x * TEXT_WIDTH, y * TEXT_HEIGHT)) + + x_offset += SCALED_WIDTH + + y_offset += SCALED_HEIGHT + + pg.display.update() + pg.image.save(window, "render.png") + pg.quit() + + if DEBUG: + sb.save('debug/sobel-dog.png') + dog.save('debug/dog.png') + + # Draw only the ASCII edges + pg.init() + font = pg.font.Font(FONT_PATH, 12) + window = pg.display.set_mode((text_grid_width * TEXT_WIDTH, text_grind_height * TEXT_HEIGHT)) + + y_offset = 0 + + for y in range(math.floor(h / SCALED_HEIGHT)): + x_offset = 0 + + for x in range(math.floor(w / SCALED_WIDTH)): + histogram = {} + + # Collect the most common char for a group of pixels + colors = [] + + for y2 in range(SCALED_HEIGHT): + for x2 in range(SCALED_WIDTH): + + real_x = x2 + x_offset + real_y = y2 + y_offset + + gradient_v = gradient[real_y * w + real_x] + + char = ' ' + + if gradient_v: + char = match_gradient(gradient_v) + else: + char = ' ' + + if char in histogram: + histogram[char] += 1 + else: + histogram[char] = 1 + + colors.append(image.getpixel((real_x, real_y))) + + color_avg = average_colors(colors) + + # get most common + most_common = None + score = float('-inf') + for char in histogram: + if histogram[char] > score: + score = histogram[char] + most_common = char + + rendered_char = font.render(most_common, True, color_avg) + window.blit(rendered_char, (x * TEXT_WIDTH, y * TEXT_HEIGHT)) + + x_offset += SCALED_WIDTH + + y_offset += SCALED_HEIGHT + + pg.display.update() + pg.image.save(window, "debug/edges-render.png") + pg.quit() + +# TODO @0x01FE : refactor plz increment by 30 & 60 +def match_gradient(n: float) -> str: + + n = abs(n) + + if n < math.radians(30): + return '|' + elif n < math.radians(60): + return '/' + elif n < math.radians(120): + return '-' + elif n < math.radians(150): + return '\\' + elif n < math.radians(210): + return '|' + elif n < math.radians(240): + return '/' + elif n < math.radians(300): + return '-' + elif n < math.radians(330): + return '\\' + else: + return '|' + +def average_colors(colors: list): + avg = [0 for x in range(len(colors[0]))] + + for color in colors: + for i, num in enumerate(color): + avg[i] += num + + for i, n in enumerate(avg): + avg[i] /= len(colors) + + return avg diff --git a/ctif.py b/ctif.py new file mode 100644 index 0000000..0399613 --- /dev/null +++ b/ctif.py @@ -0,0 +1,165 @@ +import logging +import math +import os + +from PIL import Image +import pygame as pg + +import filters + +logging.getLogger(__name__) + +FONT_PATH = 'fonts/scientifica.ttf' + +TEXT_WIDTH = 6 # on font size 12 monocraft medium +TEXT_HEIGHT = 11 + +SCALE_FACTOR = 2 +RESIZE_FACTOR = 2 + +SCALED_WIDTH = TEXT_WIDTH // SCALE_FACTOR +SCALED_HEIGHT = TEXT_HEIGHT // SCALE_FACTOR + +DEBUG = True + +if not os.path.exists('./debug/') and DEBUG: + os.makedirs('./debug') + +# '█@?OPoci. ' +CHARS = list(reversed([ + u'█', + '@', + '?', + 'O', + 'P', + 'o', + 'c', + 'i', + '.', + ' ' +])) + +# Character Text Image Format +class CTIF: + data: list + + def __init__(self): + pass + + def convert(self, image: Image.Image) -> None: + + image = image.resize((image.width * RESIZE_FACTOR, image.height * RESIZE_FACTOR)) + logging.debug(f'Image Size: {image.size}') + + L_image = image.convert('L') + logging.debug(f'L_image Size: {L_image.size}') + + dog = filters.difference_of_gaussians(image) + logging.debug(f'Dog Size: {dog.size}') + + dog.save('debug/dog.png') + + sb, gradient = filters.sobel(dog) + + w, h = sb.size + + sb.save('debug/sobel.png') + + text_grid_width = w / SCALED_WIDTH + text_grind_height = h / SCALED_HEIGHT + + pg.init() + font = pg.font.Font(FONT_PATH, 12) + window = pg.display.set_mode((text_grid_width * TEXT_WIDTH, text_grind_height * TEXT_HEIGHT)) + + y_offset = 0 + + for y in range(math.floor(h / SCALED_HEIGHT)): + x_offset = 0 + + for x in range(math.floor(w / SCALED_WIDTH)): + histogram = {} + + # Collect the most common char for a group of pixels + colors = [] + + for y2 in range(SCALED_HEIGHT): + for x2 in range(SCALED_WIDTH): + + real_x = x2 + x_offset + real_y = y2 + y_offset + + gradient_v = gradient[real_y * w + real_x] + + char = ' ' + + if gradient_v: + char = self._match_gradient(gradient_v) + else: + char = CHARS[round(L_image.getpixel((real_x, real_y))/(255/len(CHARS))) - 1] + + if char in histogram: + histogram[char] += 1 + else: + histogram[char] = 1 + + colors.append(image.getpixel((real_x, real_y))) + + color_avg = self._average_colors(colors) + + # get most common + most_common = None + score = float('-inf') + for char in histogram: + if histogram[char] > score: + score = histogram[char] + most_common = char + + rendered_char = font.render(str(most_common), True, color_avg) + window.blit(rendered_char, (x * TEXT_WIDTH, y * TEXT_HEIGHT)) + + x_offset += SCALED_WIDTH + + y_offset += SCALED_HEIGHT + + pg.display.update() + pg.image.save(window, "render.png") + pg.quit() + + + # TODO @0x01FE : refactor plz increment by 30 & 60 + def _match_gradient(self, n: float) -> str: + + n = abs(n) + + if n < math.radians(30): + return '|' + elif n < math.radians(60): + return '/' + elif n < math.radians(120): + return '-' + elif n < math.radians(150): + return '\\' + elif n < math.radians(210): + return '|' + elif n < math.radians(240): + return '/' + elif n < math.radians(300): + return '-' + elif n < math.radians(330): + return '\\' + else: + return '|' + + def _average_colors(self, colors: list): + avg = [0 for x in range(len(colors[0]))] + + for color in colors: + for i, num in enumerate(color): + avg[i] += num + + for i, n in enumerate(avg): + avg[i] /= len(colors) + + return avg + diff --git a/main.py b/main.py new file mode 100644 index 0000000..fd2335e --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +import logging + +from PIL import Image +import asciify + +FORMAT = "%(levelname)s %(filename)s - %(message)s" +logging.basicConfig(level=logging.DEBUG, format=FORMAT) + +IMAGE_PATH = 'sample-images/engine.PNG' + +def main(): + + image = Image.open(IMAGE_PATH) + asciify.asciify(image) + +if __name__ == '__main__': + main() +