Improve performance by using more sets #1
|
@ -32,8 +32,9 @@ def print_stats(img):
|
|||
|
||||
def get_lines(img):
|
||||
"""Generate all potential lines the image contains"""
|
||||
lines = []
|
||||
pixels = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0]
|
||||
lines = set()
|
||||
lines_uniq = set()
|
||||
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)
|
||||
|
||||
|
@ -42,53 +43,21 @@ def get_lines(img):
|
|||
# for each pair of pixels, get the line
|
||||
for a in pixels:
|
||||
for b in pixels:
|
||||
line = list(bresenham(a[0], a[1], b[0], b[1]))
|
||||
is_in = True
|
||||
for p in line:
|
||||
if p not in pixels:
|
||||
is_in = False
|
||||
break
|
||||
line = tuple(bresenham(*a, *b))
|
||||
line_uniq = tuple(sorted(line))
|
||||
|
||||
# if image contains the line, put it on the array
|
||||
if is_in:
|
||||
lines.append(line)
|
||||
if set(line).issubset(pixels) and line_uniq not in lines_uniq:
|
||||
lines.add(line)
|
||||
lines_uniq.add(line_uniq)
|
||||
i += 1
|
||||
if progress:
|
||||
print("\rGet lines: {:.1%}".format(i / n), end = "")
|
||||
|
||||
if progress:
|
||||
print("\rGet lines: complete")
|
||||
|
||||
if progress:
|
||||
print("{} lines found".format(len(lines)))
|
||||
return lines
|
||||
|
||||
|
||||
def removing_doubles(lines):
|
||||
"""Remove lines that are symetric"""
|
||||
results = []
|
||||
n = len(lines)
|
||||
|
||||
if progress:
|
||||
print("Remove duplicated lines:", end = "")
|
||||
# for each line, see if it's already in the output array
|
||||
# beware not to change the orientation of the line (bresenham is not symetric)
|
||||
# TODO: optimize a bit this operation
|
||||
for i, l in enumerate(lines):
|
||||
s = sorted(l)
|
||||
same = False
|
||||
for o in results:
|
||||
if sorted(o) == s:
|
||||
same = True
|
||||
break
|
||||
if same == False:
|
||||
results.append(l)
|
||||
if progress:
|
||||
print("\rRemove double lines: {:.1%}".format(i / n), end = "")
|
||||
if progress:
|
||||
print("\rRemove double lines: complete")
|
||||
|
||||
if progress:
|
||||
print("{} lines kept".format(len(results)))
|
||||
return results
|
||||
return list(lines)
|
||||
|
||||
|
||||
def removing_useless(lines):
|
||||
|
@ -99,18 +68,20 @@ def removing_useless(lines):
|
|||
if progress:
|
||||
print("Remove useless lines:", end = "")
|
||||
# for each line, see if there is a line that contains every pixel of it
|
||||
for i, l in enumerate(lines):
|
||||
lines_set = [ set(l) for l in lines ]
|
||||
|
||||
for i, l in enumerate(lines_set):
|
||||
inclusions = 0
|
||||
# others are all lines that are not l
|
||||
others = (x for x in lines if x != l)
|
||||
for k in others:
|
||||
if len(list(set(l).intersection(set(k)))) == len(l):
|
||||
for j, k in enumerate(lines_set):
|
||||
if i == j: continue
|
||||
if l.issubset(k):
|
||||
inclusions += 1
|
||||
break
|
||||
# or len(l) == 1 : we keep single pixels to complete the image if necessary
|
||||
# TODO: do some tests to see if it's worth or not
|
||||
if inclusions == 0 or len(l) == 1:
|
||||
results.append((len(l), l))
|
||||
results.append((len(l), lines[i], l))
|
||||
if progress:
|
||||
print("\rRemove useless lines: {:.1%}".format(i / n), end = "")
|
||||
if progress:
|
||||
|
@ -123,22 +94,24 @@ def removing_useless(lines):
|
|||
|
||||
def get_best_solution(img, lines):
|
||||
"""Compute an optimized solution. The magic part of the algorithm"""
|
||||
px_left = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0]
|
||||
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)
|
||||
|
||||
if progress:
|
||||
print("Draw:", end = "")
|
||||
# while the entier image has not been drown
|
||||
while len(px_left):
|
||||
while px_left:
|
||||
# define the length of lines
|
||||
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 = [(len(l_set.intersection(px_left)) - len(l)/(2*max(img.size)),
|
||||
l, l_set) for n, l, l_set in lines]
|
||||
# sort them by length
|
||||
lines = sorted(lines)
|
||||
# pop the longest
|
||||
(p, line) = lines.pop()
|
||||
(p, line, line_set) = lines.pop()
|
||||
# define the pixels that are not covered by any lines
|
||||
px_left = [p for p in px_left if p not in line]
|
||||
px_left = px_left.difference(line_set)
|
||||
results.append((line[0], line[-1]))
|
||||
if progress:
|
||||
print("\rDraw: {:.0%}".format(1 - len(px_left)/n), end="")
|
||||
|
@ -222,7 +195,6 @@ if __name__ == "__main__":
|
|||
if args.info:
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue