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