6
\$\begingroup\$

This program takes in an image, and saves an "arrowed" version of that image to /tmp/image.png. Example input and output is below the code.

from PIL import Image, ImageDraw
import operator
from pprint import pprint
import sys
base_width = 10
arrow_base_height = 15
arrow_point_height = 15
margin = (20,20)
do_outline = True
outline = (0, 0, 0) # black
background_color = (255, 255, 255) # white
arrow_width = base_width / 2
total_arrow_height = arrow_base_height + arrow_point_height
total_arrow_width = 2 * base_width
def drawArrow(coords, color):
 if do_outline:
 draw.polygon(coords, fill=color, outline=outline)
 else:
 draw.polygon(coords, fill=color)
def to_real_coordinates(coords):
 # translates the coords to pixels on the picture
 return translate(coords, margin)
def translate(coords, vector):
 # Translates a list of coordinate tuples over a vector tuple
 t_coords = []
 for cord in coords:
 t_coords.append(tuple(map(operator.add, cord, vector)))
 return t_coords
def mirror(coords):
 # Takes a list of coordinate tuples and mirrors it across the first element of
 # the first tuple
 # Formula: 2 * base - original
 m_coords = []
 base = coords[0]
 double_base = tuple(map(operator.mul, base, len(base)* (2,) ))
 for cord in coords:
 m_coords.append(tuple(map(operator.sub, double_base, cord)))
 return m_coords
def get_arrow_coords():
 coords = [
 (0, 0),
 (arrow_base_height, 0),
 (arrow_base_height, arrow_width),
 (arrow_base_height + arrow_point_height, - arrow_width),
 (arrow_base_height, -3 * arrow_width),
 (arrow_base_height, - base_width),
 (0, - base_width)
 ]
 return coords
if __name__ == "__main__":
 orig = Image.open(sys.argv[1]).transpose(Image.ROTATE_90)
 pix = orig.load()
 new_size = (1024,1024)
 actual_size = (new_size[0] + 2 * margin[0], new_size[1] + 2*margin[1])
 im = Image.new("RGB", actual_size, background_color)
 draw = ImageDraw.Draw(im)
 arrow = get_arrow_coords()
 m_arrow = mirror(arrow)
 for i in range(new_size[0] / total_arrow_height):
 for j in range((new_size[1] / total_arrow_width)):
 color = pix[
 i * total_arrow_height * orig.size[0] / new_size[0],
 j * total_arrow_width * orig.size[1] / new_size[1]
 ]
 # calculate and draw arrow
 coords = translate(arrow, (i * total_arrow_height, j * total_arrow_width))
 real_coords = to_real_coordinates(coords)
 drawArrow(real_coords, color)
 # calculate and draw mirrored arrow
 coords = translate(m_arrow, (arrow_base_height + i * total_arrow_height, j * total_arrow_width))
 real_coords = to_real_coordinates(coords)
 drawArrow(real_coords, color)
 im = im.transpose(Image.ROTATE_270)
 im.show()
 im.save("/tmp/image.png")

input enter image description here

asked May 14, 2017 at 12:01
\$\endgroup\$
2
  • \$\begingroup\$ Looks kind of cool, but might I ask why you want to convert images to "arrow images"? \$\endgroup\$ Commented May 14, 2017 at 13:15
  • 2
    \$\begingroup\$ @holroy: it's an art project I'm doing. It's basically a mix of Escher, Above and Invaders. \$\endgroup\$ Commented May 14, 2017 at 13:33

1 Answer 1

3
\$\begingroup\$

Be consistent and pythonic in naming functions.

draw_arrow instead of drawArrow

Use comprehensions instead of creating empty list and appending to it in a for loop. It's both faster and easier to read.

def translate(coords, vector):
 # Translates a list of coordinate tuples over a vector tuple
 return tuple(tuple(map(operator.add, c, vector)) for c in coords)
def mirror(coords):
 # Takes a list of coordinate tuples and mirrors it across the first element of
 # the first tuple
 # Formula: 2 * base - original
 base = coords[0]
 double_base = tuple(map(operator.mul, base, len(base) * (2,) ))
 return tuple(tuple(map(operator.sub, double_base, cord)) for c in coords)

Why have a function that returns a constant value. Why creating a variable when you can just return the value.

def get_arrow_coords():
 return [...]
# or if you worry about mutability - use tuples. They actually faster if you wouldn't try to modify them (creating new ones from old ones) a lot.
arrow_coords = ...

You can also use collections.namedtuple('Size', 'height width') instead of using plain tuples for sizes. This would improve readability a little.

And I'm not sure, but maybe you would benefit from using numpy as it seems that you're doing some matrices work.

200_success
145k22 gold badges190 silver badges478 bronze badges
answered May 14, 2017 at 17:20
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.