Skip to content
Permalink
Browse files
[pfab17] Add option to use precomputed color map to generate ASCII art
  • Loading branch information
robert committed Jun 25, 2020
1 parent 691cfaf commit fd08b6737b0f8cffab4877aa9ea042f1043cc55b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 5 deletions.
@@ -6,6 +6,7 @@
from PIL import Image
from blessings import Terminal
import math
import json

def init_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
@@ -161,6 +162,42 @@ def snap_value(val):
rgb = [snap_value(v) for v in rgb]
return inverted_color_dict[str(rgb)]

class PrecomputedRgbToAnsiConverter(object):

"""
We wrap this functionality in a simple class to allow us
to tightly associate the way in which color maps are
read from and written to disk, and the way in which they
are accessed. We could also consider moving the entire
precomputation code into this class, or at least the portion
of it that if responsible for choosing the format of the
file that is written to disk.
You could argue that this is all unnecessarily fancy, but
I think it's worthwhile.
"""

@staticmethod
def write_color_map(fname, color_map):
with open(fname, 'w') as f:
json.dump(color_map, f)

@staticmethod
def read_color_map(fname):
with open(fname, 'r') as f:
return json.load(f)

@staticmethod
def from_file(fname):
color_map = PrecomputedRgbToAnsiConverter.read_color_map(fname)
return PrecomputedRgbToAnsiConverter(color_map)

def __init__(self, color_map):
self.color_map = color_map

def rgb_to_ansi(self, rgb):
return self.color_map[str(tuple(rgb))]

def main():
parser = init_argparse()
args = parser.parse_args()
@@ -174,7 +211,9 @@ def main():
invert = not args.uninvert
character_array = character_map(imarray, args.method, invert)
if args.color == True:
color_array = color_mask(imarray, rgb_to_ansi)
converter = PrecomputedRgbToAnsiConverter.from_file('./color_map.json')
color_array = color_mask(imarray, converter.rgb_to_ansi)

colors_print_to_terminal(character_array, color_array, args.scale)
else:
print_to_terminal(character_array, args.scale)
@@ -0,0 +1,18 @@
import asciiart
import json

if __name__ == "__main__":
color_map = {}
for r in range(256):
print(r)
for g in range(256):
for b in range(256):
rgb = (r, g, b)
col = asciiart.rgb_to_ansi(rgb)
color_map[str(rgb)] = col

asciiart.PrecomputedRgbToAnsiConverter.write_color_map(
"./color_map.json",
color_map,
)

@@ -12,13 +12,20 @@

imarray = asciiart.initial_process(filename, width)

n = 5
n = 2

normal_t = timeit.timeit(lambda: asciiart.color_mask(imarray, asciiart.closest_ANSI_color), number=5)
# ~6.2s/run
# ~16.5s/run
print(f"normal: {normal_t/n} per run")

snapper_t = timeit.timeit(lambda: asciiart.color_mask(imarray, snap), number=n)
snapper_t = timeit.timeit(lambda: asciiart.color_mask(imarray, asciiart.rgb_to_ansi), number=n)

# ~0.18s/run - >30x speedup!
# ~0.5s/run -> 30x speedup!
print(f"snapper: {snapper_t/n} per run")

# NOTE: we don't count the time it takes to load the color map from disk
converter = asciiart.PrecomputedRgbToAnsiConverter.from_file('./color_map.json')

# ~0.05s/run -> a further 10x speedup, and a 300x speedup over the original approach!
precomputed_t = timeit.timeit(lambda: asciiart.color_mask(imarray, converter.rgb_to_ansi), number=n)
print(f"precomputed: {precomputed_t/n} per run")

0 comments on commit fd08b67

Please sign in to comment.