Mercurial > repos > tomasz-bednarz > image_tools
changeset 0:64374d852e36
Uploaded
author | tomasz-bednarz |
---|---|
date | Mon, 25 Nov 2013 21:32:12 -0500 |
parents | |
children | 4f6d8ffe0a60 |
files | README README.md test-data/sydney.jpg tools/image_do.py tools/image_filmify.xml tools/image_resize.xml tools/image_rotate.xml tools/image_tileify.xml tools/image_transforms.py tools/image_transforms.pyc |
diffstat | 10 files changed, 476 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,12 @@ +Part of http://cloudimaging.net.au + +Image functions supported: + +* Filmify: Change image to funky film-frame looking image. +* Resize: Resize the image. +* Rotate: Rotate the image. +* Tileify: Change image to funky tiled looking image. + +Required: http://www.pythonware.com/products/pil/ + +For more info please contact tomasz.bednarz (at) csiro dot au.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,12 @@ +Part of http://cloudimaging.net.au + +Image functions supported: + +* Filmify: Change image to funky film-frame looking image. +* Resize: Resize the image. +* Rotate: Rotate the image. +* Tileify: Change image to funky tiled looking image. + +Required: http://www.pythonware.com/products/pil/ + +For more info please contact tomasz.bednarz (at) csiro dot au.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_do.py Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,146 @@ +import os +import optparse + +from PIL import Image +from PIL import ImageDraw +from image_transforms import tileify +from image_transforms import calculate_random_location +from image_transforms import filmify_image + + +def main(): + + # Parse options + parser = optparse.OptionParser() + parser.add_option( + "-i", + dest="inputImage", + help="Input Image", + metavar="FILE" + ) + parser.add_option( + "-o", + dest="outputImage", + help="Output Image", + metavar="FILE" + ) + parser.add_option( + "--command", + "-c", + dest="imageCommand", + help="Command", + metavar="string" + ) + parser.add_option( + "-s", + dest='custom_string', + help='String to be placed as decription' + ) + parser.add_option( + "-e", + dest='image_ext', + help='Image extension' + ) + (opts, args) = parser.parse_args() + + mandatory_options = ['inputImage', 'outputImage', 'image_ext'] + for option in mandatory_options: + if not opts.__dict__[option]: + print "One or more options are missing\n" + parser.print_help() + exit(-1) + + inputImage_fn = opts.inputImage + outputImage_fn =opts.outputImage + input_extension = opts.image_ext + if input_extension == "jpg": + input_extension="jpeg" + + listOfValues = opts.imageCommand.split() + + try: + imageIn = Image.open(inputImage_fn) + except IOError: + print "Cannot open file: " + inputImage_fn + + if listOfValues[0] == "filmify": + """ + Transform image into a signed movie frame. + Use: + python image_do.py -i input.jpg -o output.jpg -c "filmify" -s "hello world" -e "jpg" + """ + print "command: " + listOfValues[0] + new_image_data = filmify_image(imageIn) + add_text_header(new_image_data, text=opts.custom_string) + new_image_data.save(outputImage_fn, input_extension); + # new_image_data.show(); + elif listOfValues[0] == "tileify": + """ + Breaks image up into rectangles and shifts them a random distance. + Parameters: number of tiles, max shift + Use: + python image_do.py -i input.jpg -o output.jpg -c "tileify num_tiles max_shift" -e "jpg" + """ + print "command: " + listOfValues[0] + num_tiles = int(listOfValues[1]) + max_shift = int(listOfValues[2]) + new_image_data = tileify(imageIn, num_tiles, max_shift) + new_image_data.save(outputImage_fn, input_extension); + # new_image_data.show(); + elif listOfValues[0] == "rotate": + """ + Rotates an image the given number of degrees counter clockwise around its centre. + Parameters: deg, 1 - expand the image or 0 - leave same size, filter + Use: + python image_do.py -i input.jpg -o output.jpg -c "rotate 45 1" -e "jpg" + """ + print "command: " + listOfValues[0] + new_image_data = imageIn.rotate(int(listOfValues[1]), expand=int(listOfValues[2])) + new_image_data.save(outputImage_fn, input_extension); + # new_image_data.show(new_image_data) + elif listOfValues[0] == "resize": + """ + Resizes the image. + Parameters: width, height, filter: 0 = NEAREST, 1 = ANTIALIAS, 2 = BILINEAR, 3 = BICUBIC + Use: + python image_do.py -i sydney.jpg -o o.jpg -c "resize 140 140 0" + """ + print "command: " + listOfValues[0] + size = int(listOfValues[1]), int(listOfValues[2]) + filterr = int(listOfValues[3]) + new_image_data = imageIn.resize(size, filterr) + new_image_data.save(outputImage_fn, input_extension); + # new_image_data.show() + + elif listOfValues[0] == "crop": + # bbox + # returns a copy of a rectangular region from the current image. The box is a 4-tuple defining the left, upper, right, and lower pixel coordinate. + # example: python image_do.py -i sydney.jpg -o sydney_out.jpg -c "crop 0 0 10 10" + print "command: " + listOfValues[0] + new_image_data = imageIn.crop((int(listOfValues[1]), int(listOfValues[2]), int(listOfValues[3]), int(listOfValues[4]))) + new_image_data.save(outputImage_fn); + # new_image_data.show(new_image_data) + + +def add_text_header(image_data, text='cloudimaging.net.au'): + """ + Add text to the top of the document + Inputs + image_data - image to have text added + text - string to add + """ + # TODO add string wrapping for long strings + # TODO figure this out programatically + text_location = (300, 1) + # This is white + fill_color = 255 + # This happens if -s wasn't set + if text == None: + text = 'cloudimaging.net.au' + draw = ImageDraw.Draw(image_data) + draw.text(text_location, text, fill=fill_color) + + +if __name__ == "__main__": + main() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_filmify.xml Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,29 @@ +<tool id="image_filmify" name="Image Filmify" version="1.0.0"> + + <description>Change image to funky film-frame looking image.</description> + + <command interpreter="python" > + image_do.py -i $input -o $output -c "filmify" -s "$label_film" -e "$input.ext" + </command> + + <inputs> + <param name="input" type="data" format="png,jpg,tif,zip" label="Input image" help="Original RGB or grayscale image." /> + <param name="label_film" size="20" type="text" value="cloudimaging.net.au" label="Label for the movie frame." /> + </inputs> + + <outputs> + <data format="input" name="output" metadata_source="input" label="Filmify on ${input.display_name}"/> + </outputs> + + <help> + Makes the image look like 35mm film. + </help> + + <tests> + <test> + <param name="input" value="sydney.jpg" /> + <output name="output" file="sydney-filmify.jpg" /> + </test> + </tests> + +</tool> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_resize.xml Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,39 @@ +<tool id="image_resize" name="Image Resize" version="1.0.0"> + + <description>Resize the image.</description> + + <command interpreter="python" > + image_do.py -i $input -o $output -c "resize $xx $yy $filter" -e "$input.ext" + </command> + + <inputs> + <param name="input" type="data" format="png,jpg,tif,zip" label="Input image" help="Original RGB or grayscale image." /> + <param name="xx" size="5" type="integer" value="0" label="Width" help="Target image width" /> + <param name="yy" size="5" type="integer" value="0" label="Height" help="Target image height" /> + <param name="filter" type="select" label="Filter"> + <option value="0">NEAREST</option> + <option value="1" selected="true">ANTIALIAS</option> + <option value="2">BILINEAR</option> + <option value="3">BICUBIC</option> + </param> + </inputs> + + <outputs> + <data format="input" name="output" metadata_source="input" label="Resized ${input.display_name}"/> + </outputs> + + <help> + Resizes an image. + </help> + + <tests> + <test> + <param name="input" value="sydney.jpg" /> + <param name="xx" value="50" /> + <param name="yy" value="50" /> + <param name="filter" value="1" /> + <output name="output" file="sydney-resized.jpg" /> + </test> + </tests> + +</tool> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_rotate.xml Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,32 @@ +<tool id="image_rotate" name="Image Rotate" version="1.0.0"> + + <description>Rotate the image.</description> + + <command interpreter="python" > + image_do.py -i $input -o $output -c "rotate $rotDeg $expand" -e "$input.ext" + </command> + + <inputs> + <param name="input" type="data" format="png,jpg,tif,zip" label="Input image" help="Original RGB or grayscale image." /> + <param name="rotDeg" size="5" type="integer" value="0" label="Degree" help="Degree of rotation" /> + <param name="expand" type="boolean" value="True" checked="yes" truevalue="1" falsevalue="0" label="Expand?" help="Expand image size when rotated (true) or keep exisiting size (false)." /> + </inputs> + + <outputs> + <data format="input" name="output" metadata_source="input" label="Rotated ${input.display_name}"/> + </outputs> + + <help> + Rotates an image the given number of degrees counter clockwise around its centre. + </help> + + <tests> + <test> + <param name="input" value="sydney.jpg" /> + <param name="rotDeg" value="30" /> + <param name="expand" value="True" /> + <output name="output" file="sydney-rotated.jpg" /> + </test> + </tests> + +</tool> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_tileify.xml Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,33 @@ +<tool id="image_tileify" name="Image Tileify" version="1.0.0"> + + <description>Change image to funky tiled looking image.</description> + + <command interpreter="python" > + image_do.py -i $input -o $output -c "tileify $numTiles $maxShift" -e "$input.ext" + </command> + + <inputs> + <param name="input" type="data" format="png,jpg,tif,zip" label="Input image" help="Original RGB or grayscale image." /> + <param name="numTiles" size="5" type="integer" value="10" label="Number of tiles" help="Number of tiles." /> + <param name="maxShift" size="5" type="integer" value="5" label="Max shift" help="Max shift in pixels." /> + </inputs> + + <outputs> + <data format="input" name="output" metadata_source="input" label="Tileify on ${input.display_name}"/> + </outputs> + + <help> + Breaks image up into rectangles and shifts them a random distance. + Areas not covered by the moved rectangles are the negative of the original image. + </help> + + <tests> + <test> + <param name="input" value="sydney.jpg" /> + <param name="num_titles" value="10" /> + <param name="max_shift" value="5" /> + <output name="output" file="sydney-tiled.jpg" /> + </test> + </tests> + +</tool> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/image_transforms.py Mon Nov 25 21:32:12 2013 -0500 @@ -0,0 +1,173 @@ +"""Transformations on images""" +from numpy import random +from numpy import array +from PIL import Image +from PIL import ImageDraw +from PIL import ImageOps + +def tileify(image_data, num_blocks, max_shift): + """ + Breaks image up into rectangles and shifts them a random distance + Areas not covered by the moved rectangles are the negative of + the original image + """ + width = image_data.size[0] + height = image_data.size[1] + row_block_size = round(height / num_blocks) + col_block_size = round(width / num_blocks) + negative_image = ImageOps.invert(image_data) + pixels = array(image_data) + for row in xrange(num_blocks): + inner_row_val = int(row_block_size * row) + outer_row_val = int(row_block_size * row + row_block_size) + for col in xrange(num_blocks): + inner_col_val = int(col_block_size * col) + outer_col_val = int(col_block_size * col + col_block_size) + block = pixels[ + inner_row_val:outer_row_val, + inner_col_val:outer_col_val + ] + location = calculate_random_location( + inner_row_val, + inner_col_val, + max_shift + ) + negative_image.paste(Image.fromarray(block), location) + return negative_image + +def calculate_random_location(x, y, max_shift): + """ + Calculates random location near some starting point + Inputs + x - coordinate point where 0,0 is upper left corner + y - coordinate point where 0,0 is upper left corner + max_shift - the furthest distance from the point to shift + """ + x_offset = random.random_integers(-max_shift, max_shift) + y_offset = random.random_integers(-max_shift, max_shift) + location = ( + y + y_offset, + x + x_offset + ) + return location + +def filmify_image(image_data): + """ + Makes the image look like 35mm film + Inputs: + PIL Image + Returns: + Modified image data + """ + # print 'Filming' + # Size of photo in side the film + film_size = (400, 300) + border_size = 30 + film_hole_size = (8, 10) + + filmed_image = modify_photo(image_data, film_size) + filmed_image = add_film_border(filmed_image, film_size, border_size) + + # Center strip in the border space + strip_upper_offset = int(round((border_size - film_hole_size[1])/2)) + strip_lower_offset = film_size[1] + border_size + strip_upper_offset + # Bring strip in towards the photo + offset = int(round(film_hole_size[1]/2)) + strip_upper_offset = strip_upper_offset + offset + strip_lower_offset = strip_lower_offset - offset + + place_film_strip( + filmed_image, + strip_upper_offset, + strip_lower_offset, + film_hole_size + ) + + return filmed_image + +def add_film_border(image_data, film_size, border_size): + """ + Creates a black border around the image + Inputs + image_data - image to be manipulated + film_size - size of the internal picture + border_size - how wide you want the border on the top and bottom to be + Output + image with border + """ + image_data = ImageOps.expand( + image_data, + border=border_size, + fill='black' + ) + # Crop to cut half of border from right and left of image + crop_box = ( + int(round(border_size/2)), + 0, + film_size[0] + int(round(border_size * 1.5)), + film_size[1] + border_size * 2 + ) + image_data = image_data.crop(crop_box) + return image_data + +def place_film_strip(image_data, y_offset_upper, y_offset_lower, film_hole_size): + """ + Create strip of film_holes at y_offset + """ + hole_distance = 15 + left_film_buffer = 2 + for x_offset in range(left_film_buffer, image_data.size[0], hole_distance): + place_film_hole(image_data, (x_offset, y_offset_upper), film_hole_size) + place_film_hole(image_data, (x_offset, y_offset_lower), film_hole_size) + +def place_film_hole(image_data, offset, film_hole_size): + """ + Puts film hole at offset into the image provided + Inputs: + image_data - image file to have film hole pasted + offset - 2 tuple with x and y of the top left corner of film hole + film_hole_size - size of the rectangle to be made + Outputs + Original image with rectangle in location based on the offset + """ + corner_radius = 2 + film_hole_color = 'white' + film_hole = round_rectangle(film_hole_size, corner_radius, film_hole_color) + image_data.paste(film_hole, offset) + +def round_corner(radius, fill): + """ + Draw a round corner + This code came from http://nadiana.com/pil-tutorial-basic-advanced-drawing + """ + corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0)) + draw = ImageDraw.Draw(corner) + draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill=fill) + return corner + +def round_rectangle(size, radius, fill): + """ + Draw a rounded rectangle + This code came from http://nadiana.com/pil-tutorial-basic-advanced-drawing + """ + width, height = size + rectangle = Image.new('RGBA', size, fill) + corner = round_corner(radius, fill) + rectangle.paste(corner, (0, 0)) + # Rotate the corner and paste it + rectangle.paste(corner.rotate(90), (0, height - radius)) + rectangle.paste(corner.rotate(180), (width - radius, height - radius)) + rectangle.paste(corner.rotate(270), (width - radius, 0)) + return rectangle + +def modify_photo(image_data, film_size): + """ + Make the image grayscale and invert the colors + All manipulations for the original photo go here + """ + modified_image = ImageOps.grayscale(image_data) + # Image.ANTIALIAS is best for down sizing + modified_image = modified_image.resize(film_size, Image.ANTIALIAS) + modified_image = ImageOps.invert(modified_image) + modified_image = ImageOps.flip(modified_image) + return modified_image