|
|
@ -5,15 +5,18 @@ from bresenham import bresenham |
|
|
|
from PIL import Image, ImageDraw |
|
|
|
from random import randint |
|
|
|
import sys |
|
|
|
from time import time |
|
|
|
|
|
|
|
|
|
|
|
rand = lambda: randint(0,200) |
|
|
|
quiet = False |
|
|
|
|
|
|
|
|
|
|
|
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))) |
|
|
|
if not quiet: |
|
|
|
print("{} black pixels over {} ({:.1%})".format(count, len(pixels), count/len(pixels))) |
|
|
|
|
|
|
|
|
|
|
|
def get_lines(img): |
|
|
@ -22,18 +25,26 @@ def get_lines(img): |
|
|
|
|
|
|
|
i, n = 0, len(pixels) * len(pixels) |
|
|
|
|
|
|
|
print("Get lines:", end = "") |
|
|
|
if not quiet: |
|
|
|
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: |
|
|
|
is_in = True |
|
|
|
for p in line: |
|
|
|
if p not in pixels: |
|
|
|
is_in = False |
|
|
|
break |
|
|
|
if is_in: |
|
|
|
lines.append(line) |
|
|
|
i += 1 |
|
|
|
print("\rGet lines: {:.0%}".format(i / n), end = "") |
|
|
|
print("\rGet lines: complete") |
|
|
|
if not quiet: |
|
|
|
print("\rGet lines: {:.1%}".format(i / n), end = "") |
|
|
|
if not quiet: |
|
|
|
print("\rGet lines: complete") |
|
|
|
|
|
|
|
print("{} lines found".format(len(lines))) |
|
|
|
if not quiet: |
|
|
|
print("{} lines found".format(len(lines))) |
|
|
|
return lines |
|
|
|
|
|
|
|
|
|
|
@ -41,7 +52,8 @@ def removing_doubles(lines): |
|
|
|
results = [] |
|
|
|
i, n = 0, len(lines) |
|
|
|
|
|
|
|
print("Remove duplicated lines:", end = "") |
|
|
|
if not quiet: |
|
|
|
print("Remove duplicated lines:", end = "") |
|
|
|
for l in lines: |
|
|
|
s = sorted(l) |
|
|
|
same = False |
|
|
@ -52,10 +64,13 @@ def removing_doubles(lines): |
|
|
|
if same == False: |
|
|
|
results.append(l) |
|
|
|
i += 1 |
|
|
|
print("\rRemove double lines: {:.0%}".format(i / n), end = "") |
|
|
|
print("\rRemove double lines: complete") |
|
|
|
if not quiet: |
|
|
|
print("\rRemove double lines: {:.0%}".format(i / n), end = "") |
|
|
|
if not quiet: |
|
|
|
print("\rRemove double lines: complete") |
|
|
|
|
|
|
|
print("{} lines kept".format(len(results))) |
|
|
|
if not quiet: |
|
|
|
print("{} lines kept".format(len(results))) |
|
|
|
return results |
|
|
|
|
|
|
|
|
|
|
@ -63,7 +78,8 @@ def removing_useless(lines): |
|
|
|
results = [] |
|
|
|
i, n = 0, len(lines) |
|
|
|
|
|
|
|
print("Remove useless lines:", end = "") |
|
|
|
if not quiet: |
|
|
|
print("Remove useless lines:", end = "") |
|
|
|
for l in lines: |
|
|
|
inclusions = 0 |
|
|
|
others = (x for x in lines if x != l) |
|
|
@ -74,10 +90,13 @@ def removing_useless(lines): |
|
|
|
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") |
|
|
|
if not quiet: |
|
|
|
print("\rRemove useless lines: {:.0%}".format(i / n), end = "") |
|
|
|
if not quiet: |
|
|
|
print("\rRemove useless lines: complete") |
|
|
|
|
|
|
|
print("{} lines kept".format(len(results))) |
|
|
|
if not quiet: |
|
|
|
print("{} lines kept".format(len(results))) |
|
|
|
return results |
|
|
|
|
|
|
|
|
|
|
@ -86,17 +105,21 @@ def get_best_solution(img, lines): |
|
|
|
results = [] |
|
|
|
n = len(px_left) |
|
|
|
|
|
|
|
print("Draw:", end = "") |
|
|
|
if not quiet: |
|
|
|
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") |
|
|
|
if not quiet: |
|
|
|
print("\rDraw: {:.0%}".format(1 - len(px_left)/n), end="") |
|
|
|
if not quiet: |
|
|
|
print("\rDraw: complete") |
|
|
|
|
|
|
|
print("Solution found in {} lines".format(len(results))) |
|
|
|
if not quiet: |
|
|
|
print("Solution found in {} lines".format(len(results))) |
|
|
|
return results |
|
|
|
|
|
|
|
|
|
|
@ -115,15 +138,21 @@ def generate_code(lines, args): |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
start = time() |
|
|
|
|
|
|
|
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('-c', '--codeonly', action='store_true', help='return code only (override -i)') |
|
|
|
parser.add_argument('-d', '--draw', action='store_true', help='draw the result into a new file') |
|
|
|
parser.add_argument('-f', '--flip', action='store_true', help='flip image vertically (for inverted ViewWindow)') |
|
|
|
parser.add_argument('-i', '--info', action='store_true', help='print informative stats') |
|
|
|
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') |
|
|
|
parser.add_argument('-s', '--show', action='store_true', help='show the result') |
|
|
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
quiet = args.codeonly |
|
|
|
|
|
|
|
try: |
|
|
|
image = Image.open(args.path) |
|
|
|
except: |
|
|
@ -134,24 +163,28 @@ if __name__ == "__main__": |
|
|
|
except: |
|
|
|
sys.exit("Error! Unable to convert to 1bit") |
|
|
|
|
|
|
|
print_stats(image) |
|
|
|
if args.flip: |
|
|
|
image = image.transpose(Image.FLIP_TOP_BOTTOM) |
|
|
|
|
|
|
|
if args.info and not quiet: |
|
|
|
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: |
|
|
|
if args.draw or args.show: |
|
|
|
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") |
|
|
|
if args.draw: |
|
|
|
export.save(args.path[:-4] + "_gen.png") |
|
|
|
if args.show: |
|
|
|
export.show() |
|
|
|
|
|
|
|
if args.info and not quiet: |
|
|
|
print("{} processed in {} lines ({:.3}s)".format(args.path, len(lines), time() - start)) |
|
|
|
print(code) |
|
|
|
|
|
|
|
print("\n\n=================================================\n\nDone!") |