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