-
Notifications
You must be signed in to change notification settings - Fork 0
/
img2bricks
executable file
·136 lines (117 loc) · 4.08 KB
/
img2bricks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#! /usr/bin/env python
###########################################
# Render a bitmapped image as a brick #
# wall through the use of TikZbricks #
# #
# By Scott Pakin <[email protected]> #
###########################################
import argparse
import random
import sys
from PIL import Image
# Parse the command line.
parser = argparse.ArgumentParser(description='Render an image as a brick wall.')
parser.add_argument('image', metavar='IMAGE-FILE',
help='bitmapped image to draw as a wall')
parser.add_argument('--output', '-o', type=argparse.FileType('w'),
metavar='LATEX-FILE', default=sys.stdout,
help='name of output file')
parser.add_argument('--depth', type=int, default=1,
help='depth of each brick')
parser.add_argument('--widths', metavar='NUM[,NUM]...',
help='comma-separated list of allowable brick widths')
cl_args = parser.parse_args()
out = cl_args.output
depth = cl_args.depth
widths = cl_args.widths
if widths != None:
# Ensure width-1 bricks are included then sort the widths from
# largest to smallest.
widths = [int(w) for w in widths.split(',')]
if 1 not in widths:
widths.append(1)
widths.sort(reverse=True)
# Read the input image.
img = Image.open(cl_args.image)
img = img.convert('RGBA')
wd, ht = img.size
def remove_transparency(clr):
'Map pixels less than 50% transparent to fully transparent and the rest to fully opaque.'
if clr[3] < 128:
return (0, 0, 0, 0)
else:
return (clr[0], clr[1], clr[2], 255)
def find_run(c, r):
'''Return a run length and pixel color for a brick beginning at (c, r)
and continuing to the left.'''
clr = remove_transparency(img.getpixel((c, r)))
tally = 0
for cc in range(c, -1, -1):
if remove_transparency(img.getpixel((cc, r))) == clr:
tally += 1
else:
break
return (tally, clr)
def run_to_bricks(run, clr):
'Return a list of lines of LaTeX that draw a run of color.'
# Handle transparent bricks.
if clr[3] == 0:
return [r'\addtocounter{brickx}{%d}' % run]
# Handle opaque bricks.
code = []
code.append(r'\definecolor{bcolor}{rgb}{%.5f,%.5f,%.5f}' % (clr[0]/255.0, clr[1]/255.0, clr[2]/255.0))
if widths == None:
# Any brick width is allowed. Produce a single brick for the entire run.
code.append(r'\wallbrick[color=bcolor,brickheight=1.0]{%d}{%d}' % (run, depth))
else:
# Split the run into as few bricks as possible.
bricks = []
sizes = [w for w in widths]
remaining = run
while remaining > 0:
# Find the largest eligible brick width.
while sizes[0] > remaining:
sizes.pop(0)
# Append another brick of the current size.
bricks.append(sizes[0])
remaining -= sizes[0]
# Shuffle the list of brick sizes for variety.
random.shuffle(bricks)
# Convert bricks to LaTeX code.
for br in bricks:
code.append(r'\wallbrick[color=bcolor,brickheight=1.0]{%d}{%d}' % (br, depth))
return code
def convert_row(r):
'Convert a row of pixels to a list of LaTeX code.'
code = []
c = wd - 1
while c >= 0:
run, clr = find_run(c, r)
if run == c + 1 and clr[3] == 0:
# Optimize away trailing runs of transparency.
break
code.extend(run_to_bricks(run, clr))
c -= run
return code
def convert_image():
'Convert the entire image to a list of LaTeX code.'
code = []
for r in range(ht - 1, -1, -1):
code.extend(convert_row(r))
if r > 0:
code.append(r'\newrow')
return code
# Write some header boilerplate.
out.write(r'''% This file was generated by img2bricks.
\documentclass{standalone}
\usepackage{tikzbricks}
\begin{document}
\begin{wall}
''')
# Write the entire image.
code = convert_image()
out.write(' %s\n' % '\n '.join(code))
# Write some trailer boilerplate.
out.write(r'''\end{wall}
\end{document}
''')