#!/usr/bin/env python3 from argparse import ArgumentParser from bresenham import bresenham from PIL import Image, ImageDraw from random import randint import sys rand = lambda: randint(0,200) def print_stats(img): pixels = img.getdata() count = sum([1 for i in pixels if i == 0]) print("{} black pixels over {} ({:.1%})".format(count, len(pixels), count/len(pixels))) def get_lines(img): lines = [] pixels = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0] i, n = 0, len(pixels) * len(pixels) print("Get lines:", end = "") for a in pixels: for b in pixels: line = list(bresenham(a[0], a[1], b[0], b[1])) r = len([0 for p in line if p not in pixels]) if not r: lines.append(line) i += 1 print("\rGet lines: {:.0%}".format(i / n), end = "") print("\rGet lines: complete") print("{} lines found".format(len(lines))) return lines def removing_doubles(lines): results = [] i, n = 0, len(lines) print("Remove duplicated lines:", end = "") for l in lines: s = sorted(l) same = False for o in results: if sorted(o) == s: same = True break if same == False: results.append(l) i += 1 print("\rRemove double lines: {:.0%}".format(i / n), end = "") print("\rRemove double lines: complete") print("{} lines kept".format(len(results))) return results def removing_useless(lines): results = [] i, n = 0, len(lines) print("Remove useless lines:", end = "") for l in lines: inclusions = 0 others = (x for x in lines if x != l) for k in others: if len(list(set(l).intersection(set(k)))) == len(l): inclusions += 1 break if inclusions == 0 or len(l) == 1: results.append((len(l), l)) i += 1 print("\rRemove useless lines: {:.0%}".format(i / n), end = "") print("\rRemove useless lines: complete") print("{} lines kept".format(len(results))) return results def get_best_solution(img, lines): px_left = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0] results = [] n = len(px_left) print("Draw:", end = "") while len(px_left): lines = [(sum([1 for p in l if p in px_left]) - len(l)/(2*max(img.size)), l) for n, l in lines] lines = sorted(lines) (p, line) = lines.pop() px_left = [p for p in px_left if p not in line] results.append((line[0], line[-1])) print("\rDraw: {:.0%}".format(1 - len(px_left)/n), end="") print("\rDraw: complete") print("Solution found in {} lines".format(len(results))) return results def generate_code(lines, args): str_x, str_y = "{", "{" for (xa, yb), (x, y) in lines: x, y = x + int(args.offset[0]), y + int(args.offset[1]) a, b = xa - x + int(args.offset[0]), yb - y + int(args.offset[1]) str_x += "{}{:+}{}, ".format(x, a, "T" if a else "") str_y += "{}{:+}{}, ".format(y, b, "T" if b else "") str_x = str_x[:-2] + "}" str_y = str_y[:-2] + "}" return "Graph(X,Y)=({}, {})".format(str_x, str_y) if __name__ == "__main__": parser = ArgumentParser(description='Generate the Multi DrawStat code for an image.') parser.add_argument('path', type=str, help='path of the image to process') parser.add_argument('-d', '--draw', action='store_true', help='draw the result into a new file and display it') parser.add_argument('-o', '--offset', nargs=2, default=(0, 0), help='offset for viewwindow. Default: (0, 0)') # parser.add_argument('-q', '--quiet', action='store_true', help='return code only') # parser.add_argument('-r', '--reverse', action='store_true', help='reverse y axis') args = parser.parse_args() try: image = Image.open(args.path) except: sys.exit("Error! Unable to open file.") try: image = image.convert('1') except: sys.exit("Error! Unable to convert to 1bit") print_stats(image) lines = get_lines(image) lines = removing_doubles(lines) lines = removing_useless(lines) lines = get_best_solution(image, lines) code = generate_code(lines, args) if args.draw: export = Image.new('RGB', image.size, 'white') drawer = ImageDraw.Draw(export) for ((a, b), (c, d)) in reversed(lines): drawer.line((a, b, c, d), fill=(rand(), rand(), rand())) export = export.resize((export.width * 8, export.height * 8)) export.save(args.path[:-4] + "_gen.png") export.show() print("\n\n=================================================\n\n") print(code) print("\n\n=================================================\n\nDone!")