| 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 |