Home> Blog> Introducing the Image Novice Module

Introducing the Image Novice Module

This post originally appeared on the Software Carpentry website.

The image novice module provides a simple image manipulation interface for beginners that allows for easy loading, manipulating, and saving of image files. It has been developed collaboratively with Greg Wilson, Tony Yu, and Stephan van der Walt. Below, I show how to use the module for some basic image manipulation.

Getting the Module

The novice module comes as a standalone package via pip (image_novice), and the source code is available on Github. We are working with the folks over at scikit-image to get novice incorporated into the next release (it's currently in their Github repo).

Basic image manipulation

Loading a picture is easy with the novice module. After importing, the open method returns a Picture instance that is visible in an IPython notebook.

from image_novice import novice
picture = novice.open("sample.png")
picture

A picture knows its image format, where it came from, and its size.

print "Format:", picture.format
print "Path:", picture.path
print "Size:", picture.size
print "Width:", picture.width

Format: png
Path: /home/hansenm/Projects/novice/sample.png
Size: (665, 500)
Width: 665

We can easily resize the picture so that it's taller than it is wide.

picture.size = (200, 250)
picture

A Picture is a collection of Pixel instances. We can iterate over the pixels and modify their color components individually. Below, we halve the red value of all pixels that are more than 50% red.

for pixel in picture:              
    if ((pixel.red > 127) and      
        (pixel.x < picture.width)):
        pixel.red /= 2
picture

A picture knows that it's been modified, which also resets its path to None.

print "Modified:", picture.modified
print "Path:", picture.path
Modified: True
Path: None

Modifying a group of pixels is easy using slices. Below, we put a black box in the lower-left corner of the picture. Note that the novice module uses Cartesian coordinates (i.e., pixel 0, 0 is at the lower left).

picture[0:20, 0:20] = "black"
picture

A Picture can be saved with a different format, and its corresponding properties are updated accordingly.

picture.save('sample-bluegreen.jpg')
print "Path:", picture.path
print "Format:", picture.format
print "Modified:", picture.modified
Path: /home/hansenm/Projects/novice/sample-bluegreen.jpg
Format: jpeg
Modified: False

The Block Example

Manipulating small images pixel by pixel can be difficult.

blocks = novice.open("block.png")
print "Size:", blocks.size
blocks
Size: (10, 10)

We can change how an image is displayed by increasing its inflation factor. An inflation factor of 10 means that a 10x10 square will be displayed for every real pixel in the image.

blocks.inflation = 10
blocks

Note that this does not modify the underlying image. The block is still the same size underneath (10x10 pixels).

blocks.size
(10, 10)

Using slices, let's recolor the lower left square with yellow.

blocks[1:5, 1:5] = "yellow"
blocks

We can change the inflation factor at any time:

blocks.inflation = 25
blocks

By using slices and the image's size, we can change the border to violet:

blocks[:, 0]                 = "violet"  # Bottom
blocks[:, blocks.height - 1] = "violet"  # Top
blocks[0, :]                 = "violet"  # Left
blocks[blocks.width - 1, :]  = "violet"  # Right
blocks

Finally, let's swap the left and right halves of the image. First, we copy the left half. Next, we overwrite the left half with the right half. Without the copy(), this would cause the temp variable to change as well. Finally, we overwrite the right half with the copied left half.

w = blocks.width / 2
temp = blocks[:w, :].copy()  # Have to make a copy here explicitly!
blocks[:w, :] = blocks[w:, :]
blocks[w:, :] = temp
blocks