Overview

The goal of this assignment is to allow you to explore processing and manipulating images in the Python programming language.

To record your solutions and answers, create a new Jupyter Notebook titled Notebook10.ipynb and use this notebook to complete the following activities and answer the corresponding questions.

Make sure you label your activities appropriately. That is for Activity 1, have a header cell that is titled Activity 1. Likewise, use the Markdown cells to answer the questions (include the questions above the answer).

This Notebook assignment is due Midnight Friday, December 04, 2015 and is to be done individually or in pairs.

Partners

Both members of each pair should submit a Notebook. Be sure to identify your partner at the top of the Notebook.

Activity 0: Utilities

To help you complete this Notebook, here are some utility functions we discussed in class:

# Display figures and images inline
%matplotlib inline

# Imports
from matplotlib.pyplot import imread, imshow, imsave, figure
import numpy
import requests
import StringIO

# Functions
def display_image(image, enlarge=False):
    ''' Display image (enlarge if specified) '''
    if enlarge:
        figure(figsize=(10, 8))
    imshow(numpy.asarray(image).astype('uint8'))

def read_image(path, format='JPG'):
    ''' Read image from path or URL '''
    if path.startswith('http'):
        data  = StringIO.StringIO(requests.get(path).content)
        image = imread(data, format=format)
    else:
        image = imread(path)
    return image

def save_image(path, image):
    ''' Save image to specified path '''
    imsave(path, numpy.asarray(image).astype('uint8'))

def create_image(width, height, default_color=(0, 0, 0)):
    ''' Create image with specified width, height, and default color '''
    image = []

    for row in range(height):
        image_row = []
        for column in range(width):
            image_row.append(default_color)
        image.append(image_row)

    return image

def copy_image(source):
    ''' Return copy of source image '''
    height = len(source)
    width  = len(source[0])
    target = []

    for row in range(height):
        target_row = []
        for column in range(width):
            target_row.append(source[row][column])
        target.append(target_row)

    return target

Feel free to use these functions in the activities below.

Actvity 1: Image Creation

For the first activity, you are to implement two of any of the following four options. Each function involves creating or generating an image.

To help you out, each option is labeled with a difficulty rating and includes some hints.

Option A: Checkered

Easy

For this option, you are to produce a checkered image:

def make_checkered_image(width, height, square_size, colors):

Given the width and height of the target image, this function produces a checkered image consisting of squares of square_size and with alternating colors.

COLORS = [
  (255, 0, 0),
  (0, 255, 0),
]
WIDTH  = 400
HEIGHT = 300

display_image(make_checkered_image(WIDTH, HEIGHT, 100, COLORS), True)

Hints

Option B. Gradient

Easy

For this option, you are to produce a gradient image:

def make_gradient_image(width, height, colors):

Given the width and height of the target image, this function produces a gradient image that goes from colors[0] to colors[1].

COLORS = [
  (255, 0, 0),
  (0, 255, 0),
]
WIDTH  = 400
HEIGHT = 300

display_image(make_gradient_image(WIDTH, HEIGHT, COLORS), True)

Hints

A gradient is basically a weighted average of the two colors. For each pixel, you will want to use the following formula:

new_color = color_0(1 - progress) + color_1(progress)

Where progress is percentage of how far we are from the left-hand side of the image.

If we are at column 25 and the image has a width of 100, then our progress is 25/100 = 0.25. Given the formula above, this means that the first color (e.g. color_0) will be given the most weight (1 - 0.25 = 0.75), while the second color will give the least weight (0.25).

As we move from left to right, the first color becomes less dominant until the right-most pixels are basically the second color.

Option C. Tiled

Medium

For this option, you are to produce a tiled image:

def make_tiled_image(source, rows, columns):

Given a source image, this function creates a tiled image consisting of rows by columns copies of the source image.

source = read_image('http://www3.nd.edu/~pbui/static/img/pbui_and_children_uwec_office.jpg')
display_image(make_tiled_image(source, 3, 3), True)

Hints

Option D. Paint-By-Numbers

Easy

For this option, you are to produce a paint-by-numbers image:

def make_image_from_template(template, block_size, color_map):

Given a template, this function produces an image that replaces each letter in the template with a block of block_size and with the color specified in the color_map.

TEMPLATE = [
    'PPPPPPPPPPPP',
    'PPPPPPPPPPPP',
    'PPPPPPPPPPPP',
    'PPPP____PPPP',
    'PPPP____PPPP',
    'PPPPPPPPPPPP',
    'PPPPPPPPPPPP',
    'PPPPPPPPPPPP',
    'PPPP        ',
    'PPPP        ',
    'PPPP        ',
    'PPPP        ',
]

COLOR_MAP = {
    'P': (0, 0, 255),
    '_': (255, 0, 0),
    ' ': (0, 255, 0)
}

HEIGHT = 400
WIDTH  = 300

display_image(make_image_from_template(TEMPLATE, 10, COLOR_MAP), True)

Hints

Actvity 2: Image Transformation

For the second activity, you are to implement two of any of the following five options. Each function involves processing or transforming an existing image.

Once again, to help you out, each option is labeled with a difficulty rating and includes some hints.

Option E. Scramble

Hard

For this option, you are to scramble an image:

def scramble_image(source, blocks_per_side):

Given a source image, this function divides the image into blocks_per_side by blocks_per_side blocks and scrambles the blocks to produce a new image.

source = read_image('https://upload.wikimedia.org/wikipedia/en/thumb/3/3b/NDLeprechaun.svg/280px-NDLeprechaun.svg.png')
display_image(scramble_image(source, 4), True)

Hints

Option F. Pixelate

Medium

For this option, you are to pixelate an image:

def pixelate_image(source, block_size):

Given a source image, this function pixelates the image dividing the image into blocks of block_size and replacing each pixel in each block with the center pixel value.

source = read_image('https://upload.wikimedia.org/wikipedia/en/thumb/3/3b/NDLeprechaun.svg/280px-NDLeprechaun.svg.png')
display_image(pixelate_image(source, 6), True)

Hints

Option G. Blend

Easy

For this option, you are to blend two images:

def blend_images(image0, image1, weight):

Given images image0 and image1, this function blends the two sources by performing a weighted average of the two images based on the specified weight.

image0 = read_image('https://upload.wikimedia.org/wikipedia/en/e/e8/Taylor_Swift_-_Red.png')
image1 = read_image('https://upload.wikimedia.org/wikipedia/en/f/f6/Taylor_Swift_-_1989.png')

display_image(blend_images(image0, image1, 0.50), True)

Hints

Option H. Filter

Hard

For this option, you are to blur an image:

def filter_image(source, matrix):

Given a source image, this function performs a convolution to apply the matrix filter to the given image.

source = read_image('https://www3.nd.edu/~pbui/static/img/pbui_and_children_uwec_office.jpg')
display_image(filter_image(source, [
      ( 0.0, 0.2,  0.0),
      ( 0.2, 0.2,  0.2),
      ( 0.0, 0.2,  0.0),
]), True)

Hints

Option I. Replace

Easy

For this option, you are to replace the colors in an image:

def replace_colors(source, color_map):

Given a source image, this function replaces colors in the source image based on the mapping provided by the color_map.

COLORS = [
  (255, 0, 0),
  (0, 255, 0),
]

WIDTH  = 400
HEIGHT = 300

COLOR_MAP = {
    (255, 0, 0): (2, 43, 91),
    (0, 255, 0): (220, 180, 57),
}

checkered = make_checkered_image(WIDTH, HEIGHT, 100, COLORS)

display_image(replace_colors(checkered, COLOR_MAP), True)

Hints

Extra Credit

Any options you implement beyond the four required ones will be treated as additional 1 point of extra credit each.

Submission

To submit your notebook, follow the same directions for Notebook00, except store this notebook in the notebook10 folder.