0
|
1 """Transformations on images"""
|
|
2 from numpy import random
|
|
3 from numpy import array
|
|
4 from PIL import Image
|
|
5 from PIL import ImageDraw
|
|
6 from PIL import ImageOps
|
|
7
|
|
8 def tileify(image_data, num_blocks, max_shift):
|
|
9 """
|
|
10 Breaks image up into rectangles and shifts them a random distance
|
|
11 Areas not covered by the moved rectangles are the negative of
|
|
12 the original image
|
|
13 """
|
|
14 width = image_data.size[0]
|
|
15 height = image_data.size[1]
|
|
16 row_block_size = round(height / num_blocks)
|
|
17 col_block_size = round(width / num_blocks)
|
|
18 negative_image = ImageOps.invert(image_data)
|
|
19 pixels = array(image_data)
|
|
20 for row in xrange(num_blocks):
|
|
21 inner_row_val = int(row_block_size * row)
|
|
22 outer_row_val = int(row_block_size * row + row_block_size)
|
|
23 for col in xrange(num_blocks):
|
|
24 inner_col_val = int(col_block_size * col)
|
|
25 outer_col_val = int(col_block_size * col + col_block_size)
|
|
26 block = pixels[
|
|
27 inner_row_val:outer_row_val,
|
|
28 inner_col_val:outer_col_val
|
|
29 ]
|
|
30 location = calculate_random_location(
|
|
31 inner_row_val,
|
|
32 inner_col_val,
|
|
33 max_shift
|
|
34 )
|
|
35 negative_image.paste(Image.fromarray(block), location)
|
|
36 return negative_image
|
|
37
|
|
38 def calculate_random_location(x, y, max_shift):
|
|
39 """
|
|
40 Calculates random location near some starting point
|
|
41 Inputs
|
|
42 x - coordinate point where 0,0 is upper left corner
|
|
43 y - coordinate point where 0,0 is upper left corner
|
|
44 max_shift - the furthest distance from the point to shift
|
|
45 """
|
|
46 x_offset = random.random_integers(-max_shift, max_shift)
|
|
47 y_offset = random.random_integers(-max_shift, max_shift)
|
|
48 location = (
|
|
49 y + y_offset,
|
|
50 x + x_offset
|
|
51 )
|
|
52 return location
|
|
53
|
|
54 def filmify_image(image_data):
|
|
55 """
|
|
56 Makes the image look like 35mm film
|
|
57 Inputs:
|
|
58 PIL Image
|
|
59 Returns:
|
|
60 Modified image data
|
|
61 """
|
|
62 # print 'Filming'
|
|
63 # Size of photo in side the film
|
|
64 film_size = (400, 300)
|
|
65 border_size = 30
|
|
66 film_hole_size = (8, 10)
|
|
67
|
|
68 filmed_image = modify_photo(image_data, film_size)
|
|
69 filmed_image = add_film_border(filmed_image, film_size, border_size)
|
|
70
|
|
71 # Center strip in the border space
|
|
72 strip_upper_offset = int(round((border_size - film_hole_size[1])/2))
|
|
73 strip_lower_offset = film_size[1] + border_size + strip_upper_offset
|
|
74 # Bring strip in towards the photo
|
|
75 offset = int(round(film_hole_size[1]/2))
|
|
76 strip_upper_offset = strip_upper_offset + offset
|
|
77 strip_lower_offset = strip_lower_offset - offset
|
|
78
|
|
79 place_film_strip(
|
|
80 filmed_image,
|
|
81 strip_upper_offset,
|
|
82 strip_lower_offset,
|
|
83 film_hole_size
|
|
84 )
|
|
85
|
|
86 return filmed_image
|
|
87
|
|
88 def add_film_border(image_data, film_size, border_size):
|
|
89 """
|
|
90 Creates a black border around the image
|
|
91 Inputs
|
|
92 image_data - image to be manipulated
|
|
93 film_size - size of the internal picture
|
|
94 border_size - how wide you want the border on the top and bottom to be
|
|
95 Output
|
|
96 image with border
|
|
97 """
|
|
98 image_data = ImageOps.expand(
|
|
99 image_data,
|
|
100 border=border_size,
|
|
101 fill='black'
|
|
102 )
|
|
103 # Crop to cut half of border from right and left of image
|
|
104 crop_box = (
|
|
105 int(round(border_size/2)),
|
|
106 0,
|
|
107 film_size[0] + int(round(border_size * 1.5)),
|
|
108 film_size[1] + border_size * 2
|
|
109 )
|
|
110 image_data = image_data.crop(crop_box)
|
|
111 return image_data
|
|
112
|
|
113 def place_film_strip(image_data, y_offset_upper, y_offset_lower, film_hole_size):
|
|
114 """
|
|
115 Create strip of film_holes at y_offset
|
|
116 """
|
|
117 hole_distance = 15
|
|
118 left_film_buffer = 2
|
|
119 for x_offset in range(left_film_buffer, image_data.size[0], hole_distance):
|
|
120 place_film_hole(image_data, (x_offset, y_offset_upper), film_hole_size)
|
|
121 place_film_hole(image_data, (x_offset, y_offset_lower), film_hole_size)
|
|
122
|
|
123 def place_film_hole(image_data, offset, film_hole_size):
|
|
124 """
|
|
125 Puts film hole at offset into the image provided
|
|
126 Inputs:
|
|
127 image_data - image file to have film hole pasted
|
|
128 offset - 2 tuple with x and y of the top left corner of film hole
|
|
129 film_hole_size - size of the rectangle to be made
|
|
130 Outputs
|
|
131 Original image with rectangle in location based on the offset
|
|
132 """
|
|
133 corner_radius = 2
|
|
134 film_hole_color = 'white'
|
|
135 film_hole = round_rectangle(film_hole_size, corner_radius, film_hole_color)
|
|
136 image_data.paste(film_hole, offset)
|
|
137
|
|
138 def round_corner(radius, fill):
|
|
139 """
|
|
140 Draw a round corner
|
|
141 This code came from http://nadiana.com/pil-tutorial-basic-advanced-drawing
|
|
142 """
|
|
143 corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0))
|
|
144 draw = ImageDraw.Draw(corner)
|
|
145 draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill=fill)
|
|
146 return corner
|
|
147
|
|
148 def round_rectangle(size, radius, fill):
|
|
149 """
|
|
150 Draw a rounded rectangle
|
|
151 This code came from http://nadiana.com/pil-tutorial-basic-advanced-drawing
|
|
152 """
|
|
153 width, height = size
|
|
154 rectangle = Image.new('RGBA', size, fill)
|
|
155 corner = round_corner(radius, fill)
|
|
156 rectangle.paste(corner, (0, 0))
|
|
157 # Rotate the corner and paste it
|
|
158 rectangle.paste(corner.rotate(90), (0, height - radius))
|
|
159 rectangle.paste(corner.rotate(180), (width - radius, height - radius))
|
|
160 rectangle.paste(corner.rotate(270), (width - radius, 0))
|
|
161 return rectangle
|
|
162
|
|
163 def modify_photo(image_data, film_size):
|
|
164 """
|
|
165 Make the image grayscale and invert the colors
|
|
166 All manipulations for the original photo go here
|
|
167 """
|
|
168 modified_image = ImageOps.grayscale(image_data)
|
|
169 # Image.ANTIALIAS is best for down sizing
|
|
170 modified_image = modified_image.resize(film_size, Image.ANTIALIAS)
|
|
171 modified_image = ImageOps.invert(modified_image)
|
|
172 modified_image = ImageOps.flip(modified_image)
|
|
173 return modified_image
|