ansel

What's ansel?

ansel is a module that gives users tools to manipulate images - crop, rotate, change color composition, etc. It's a thin wrapper for a small subset of the functions in the pygame library of functions. It's intended as a way for students to visualize certain control structures. Loops mainly. It's also intended to acquaint students with classes and their associated syntax.

We'll use ansel for the Image Filter Project. I've begun the project for you here.

What's an Image

Before we get down into the weeds and begin to write code, we should talk for a minute about digital images. Look closely at your screen. Very closely. Do you see that the image it displays is composed of tiny little dots? Those dots are pixels.

A pixel is defined by its position and its color. How is position given? By a pair of integers, both 0 or greater. The first is how far over a pixel is from the left; the second is how far down a pixel is from the top. Thus the first tells us what column we're in, and the second what row. (Remember that columns run up-down and rows run left-right.) So a pixel at (0, 0) is at column 0, row 0 (that's the top left corner); and a pixel at (100, 200) is at column 100, row 200 (that's over 100 from the left and down 200 from the top).

How is color given? By a triple of integers each in the range 0- 255. The first of the three is the amount of red, the second the amount of green and the third the amount of blue. So for instance (255, 105, 180) is max red, intensity 105 of green and intensity 180 of blue; and that results in a hot pink.

So then a pixel is defined by five integers in total. The first two give position (column, row); the last three give color (red, green, blue).

Fire It Up

So now let's talk about how you'll use ansel to manipulate an image. (I'm about to step you through the piece of the Image Filter project code I so generously provided.)

We begin with import ansel. We thereby give ourselves access to all its functions.

For your convenience (and amusement), I've provided a lengthy list of color definitions. (Look in the "colors.py" file.) I suggest that you import them with from colors import *. This will give you access to those definitions without the need to use the colors. prefix.

After those two imports, we call ansel's initialize functions. This makes sure pygame starts up properly.

So our code begins in this way:

import ansel

from colors import *

ansel.initialize()

How Do We Filter?

What's next? Well, now we filter! How we do that might come as a bit of a surprise. We don't actually alter a given image. Instead we build a new one that will replace the old. Here are the steps:

  1. Load an image. Let's say we'll name it img.

  2. Create a new, empty image. It's name will be new_img.

  3. Scan through img, pixel by pixel, and use what we find to construct new pixels. Place those new pixels in new_img.

  4. Assign new_img to img. Display new_img.

Let's look at the code for each step.

Step 1: Load an Image

To apply a filter to an image, we must first load it.

img = ansel.Image(name = "image_name.jpg", load = True)

img.draw()

The call to ansel.Image creates an object of the Image type. With load = True, we say that the Image object is to load from a file of the given name; and the string after name = gives that file name. We name that image img. (Make sure to use the complete file name; notice the .jpg at the end of the name. Make sure too that the image resides in the same directory as your program.)

The second line - the img.draw() - draws the image to a window. If we don't do that, we won't see it!

Step 2: Create a New, Empty Image

The code here is very simple:

new_img = ansel.Image(name = "choose_a_name", load = False, width = w, height = h, color = BLACK)

As before, the call to ansel.Image creates a new object of the Image type. We give it a name; and that's a string. We say whether the image should load in from a file; since the image is initially empty, we don't load. We give a width and height. We specify the color of the empty image. (Peruse "colors.py" for color choices. The color can also be given by a tuple (r, g, b).)

Step 3: Scan Old Image, Create New Pixels, Place New Pixels in New Image

We're now ready to write code that pulls pixels from a given image, uses their red, green and blue values to create new pixels and then places those new pixels in a new image. I think it's best here to have a chuck of code that does that.

1. def remove_red(img):

2. w, h = img.get_width(), img.get_height()

3. new_img = ansel.Image(name = img.get_name(), load = False, width = w, height = h, color = BLACK)

4. for col in range(w):

5. for row in range(h):

6. pix = img.get_pixel(col, row)

7. new_pix = ansel.Pixel((0, pix.get_green(), pix.get_blue()))

8. new_img.set_pixel(new_pix, col, row)

9. return new_img

The function name is remove_red. It has the single parameter img, which will name an object of the Image type.

On line 2, we get the width and height of img and assign them to w and h repsectively. (get_width and get_height are Image methods. See below for a complete list of Image methods.)

One line 3, we create an object of the Image type and name it new_img. Later we will fill new_img with pixels; and at the end we will return it. The parameters here are name, load, width, height and color. Let me say a bit about each.

name The name must be a string. We wish to keep the name of the image passed to the function, so we pulled that name with the get_name method.

load If we set load to False, a new empty image is created.

width, height Self-explanatory, but do note that new_img has the same width and height as img. (This won't always be the case. For instance, I'll have your write a filter that doubles size.)

color Set the color of the pixels (all of them) in an empty image. The color name BLACK is defined in the "colors.py" file. You could if you wished specify the color by a triple of values all between 0 and 255. For instance, color = (255, 255, 255) would make the new image white.

One lines 4 and 5 we begin to iterate through the pixels in img. for col in range(w) takes us through all the columns; those range from 0 to w - 1; for col in range(h) takes us through all the rows; those range from 0 to h - 1. So for instance if w = 640 and h = 480, col will take values from 0 to 639 and row will take values from 0 to 479.

On line 6 we pull a pixel from img and name it pix; its location is (col, row). (0, 0) is top left; (w - 1, h - 1), where w and h are width and height respectively, is bottom right.

On line 7 we create a new pixel named new_pix; to do so, we give it red, green and blue values. Since this is the remove red filter, we zero out red. Green and blue however remain the same as in pix. This is why we use pix.get_green() and pix.get_blue(); these return the amounts of green and blue respectively in pix.

On line 8 we insert new_pix into new_img at location (row, col).

Once we reach line 9, we will have placed a pixel at every position in new_img; and so we now return new_img.

Official Docs

Let's have a list of all functions available in ansel.


Pixel Constructor

Construct an object of the pixel type. Parameter: a rgb_tuple. In the examples below, we create a pixel of the given color on the right and assign that pixel to the variable on the left.

pix = ansel.Pixel((0, 0, 0)) # makes a black pixel

pix = ansel.Pixel(HOTPINK) # HOTPINK is defined in colors.py


Pixel Methods

The pixel methods are get_red, get_green and get_blue. When called on a pixel, each returns a value between 0 and 255 inclusive. Examples:

red, green, blue = pix.get_red(),pix.get_green(), pix.get_blue()


Image Constructor

Construct an object of the image type. Parameters: name, load, width, height, color. Examples:

# create an empty image with name "image_name.jpg", width w and height h, composed of BLACK pixels

# assign the imaged created to empty_image

empty_img = ansel.Image(name = "image_name.jpg", load = False, width = w, height = h, color = BLACK)

# load the image name "file_name.jpg" and assign it to loaded_img

loaded_img = ansel.Image(name = "file_name.jpg", load = True)


Image Methods

get_name, change_name, get_width, get_height, get_pixel, set_pixel, draw, save, save_as

Examples (where img is an object of Image type and pix is an object of Pixel type):

image_name = img.get_name() # return the name of img and assign it to image_name

img.change_name("new_name.jpg") # change the name of img to "new_name.jpg"

width = img.get_width() # return the width of img and assign it to width

height = img.get_height() # return the height of img and assign it to height

a_pixel = img.get_pixel(col, row) # return the pixel at (col, row) and assign it to a_pixel

img.set_pixel(a_pixel, col, row) # place the pixel named a_pixel at (col, row) in img

img.draw() # draw img to the screen

img.save() # save img; will replace the previous save

img.save_as("name.jpg") # save a new copy of img with the name "name.jpg"