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.
Binary file test-data/sydney.jpg has changed
--- /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
Binary file tools/image_transforms.pyc has changed