10.8. Image Processing

Two dimensional tables have both rows and columns. You have probably seen many tables like this if you have used a spreadsheet program. Another object that is organized in rows and columns is a digital image. In this section we will explore how iteration allows us to manipulate these images.

A digital image is a finite collection of small, discrete picture elements called pixels. These pixels are organized in a two-dimensional grid. Each pixel represents the smallest amount of picture information that is available. Sometimes these pixels appear as small “dots”.

Each image (grid of pixels) has its own width and its own height. The width is the number of columns and the height is the number of rows. We can name the pixels in the grid by using the column number and row number. However, it is very important to remember that computer scientists like to start counting with 0! This means that if there are 20 rows, they will be named 0,1,2, and so on through 19. This will be very useful later when we iterate using range.

In the figure below, the pixel of interest is found at column c and row r.

../_images/image.png

The RGB Color Model

Each pixel of the image will represent a single color. The specific color depends on a formula that mixes various amounts of three basic colors: red, green, and blue. This technique for creating color is known as the RGB Color Model. The amount of each color, sometimes called the intensity of the color, allows us to have very fine control over the resulting color.

The minimum intensity value for a basic color is 0. For example if the red intensity is 0, then there is no red in the pixel. The maximum intensity is 255. This means that there are actually 256 different amounts of intensity for each basic color. Since there are three basic colors, that means that you can create 2563 distinct colors using the RGB Color Model.

Here are the red, green and blue intensities for some common colors. Note that “Black” is represented by a pixel having no basic color. On the other hand, “White” has maximum values for all three basic color components.

Color

Red

Green

Blue

Red

255

0

0

Green

0

255

0

Blue

0

0

255

White

255

255

255

Black

0

0

0

Yellow

255

255

0

Magenta

255

0

255

In order to manipulate an image, we need to be able to access individual pixels. This capability is provided by a module called image, provided in ActiveCode.

The image module has a function image.read that reads an image file and returns the RGB values for every pixel in a 2-dimensional list. Since a pixel itself is a list of 3 values, the function actually returns a 3-dimensional list of ints.

import image

row: int
col: int
pixels: [[[int]]]
pixel: [int]
red: int
green: int
blue: int

row = 35                             # row of the pixel to get
col = 86                             # column of the pixel to get
pixels = image.read("rooney.jpg")    # get array of pixel data
pixel = pixels[row][col]             # get a single pixel
red = pixel[0]                       # get the pixel's red value
green = pixel[1]                     # get the pixel's green value
blue = pixel[2]                      # get the pixel's blue value
print("the pixel at row " + str(row) + " and column " + str(col))
print("has a red value of " + str(red))
print("has a green value of " + str(green))
print("has a blue value of " + str(blue))

The three values of a pixel are in the order red, green, then blue. So the value at index 0 is the red value, the value at index 1 is the green value, and the value at index 2 is the blue value.

Check your understanding

    iter-9-1: If you have a pixel whose RGB value is (50, 0, 0), what color will this pixel appear to be?

  • Dark red
  • Because all three values are close to 0, the color will be dark. But because the red value is higher than the other two, the color will appear red.
  • Light red
  • The closer the values are to 0, the darker the color will appear.
  • Dark green
  • The first value in RGB is the red value. The second is the green. This color has no green in it.
  • Light green
  • The first value in RGB is the red value. The second is the green. This color has no green in it.

Image Processing

Image processing refers to the ability to manipulate the individual pixels in a digital image. In order to process all of the pixels, we need to be able to systematically visit all of the rows and columns in the image. The best way to do this is to use nested iteration.

Our goal with image processing is to visit each pixel. We will use an iteration to process each row. Within that iteration, we will use a nested iteration to process each column. The result is a nested iteration, similar to the one seen above, where the outer for loop processes the rows, from 0 up to but not including the height of the image. The inner for loop will process each column of a row, again from 0 up to but not including the width of the image.

The resulting code will look like the following. We are now free to do anything we wish to each pixel in the image.

for row in range(image_height):
    for col in range(image_width):
        # do something with the pixel at position (col,row)

One of the easiest image processing algorithms will create what is known as a negative image. A negative image simply means that each pixel will be the opposite of what it was originally. But what does opposite mean?

In the RGB color model, we can consider the opposite of the red component as the difference between the original red and 255. For example, if the original red component was 50, then the opposite, or negative red value would be 255-50 or 205. In other words, pixels with a lot of red will have negatives with little red and pixels with little red will have negatives with a lot. We do the same for the blue and green as well.

The program below implements this algorithm using the previous image (rooney.jpg). Run it to see the resulting negative image. Note that there is a lot of processing taking place and this may take a few seconds to complete.

import image

pixels: [[[int]]]
height: int
width: int
row: int
col: int
pixel: [int]
red: int
green: int
blue: int
new_pixels: [[[int]]]
new_row: [[int]]
new_pixel: [int]
new_red: int
new_green: int
new_blue: int

pixels = image.read("Rooney.jpg")         # get array of pixel data
height = len(pixels)                      # height = number of rows
width = len(pixels[0])                    # width = number of pixels in a row

new_pixels = []                           # new image, initially empty
for row in range(0, height, 1):           # for each row in image
    new_row = []                          # create new row, initially empty
    for col in range(0, width, 1):        # for each column in image
        pixel = pixels[row][col]          # get image's pixel
        red = pixel[0]                    # get pixel's red
        green = pixel[1]                  # get pixel's green
        blue = pixel[2]                   # get pixel's blue
        new_red = 255 - red               # compute new red
        new_green = 255 - green           # compute new green
        new_blue = 255 - blue             # compute new blue
        new_pixel = [new_red, new_green, new_blue] # create new pixel
        new_row = new_row + [new_pixel]   # add new pixel to new row
    new_pixels = new_pixels + [new_row]   # add new row to new image
image.draw(new_pixels)                    # draw the new image

Let’s take a closer look at the code. After importing the image module, we read the image pixels and get the the image’s width and height. The height of the image is the number of rows in the image, which is the length of the pixels list. The width of the image is the number of pixels in any of the rows.

Lines 26 and 28 create the nested iteration that we discussed earlier. This allows us to process each pixel in the image. Line 25 creates a new list empty list for the new inverted pixels, new_pixels. Line 27 creates a new empty list for a new row in the new image, new_row. Note that the creation of a new row is inside of the outer loop. This means that it will repeatedly create a new row list. This is good because the image consists of many rows. The inner loop appends pixels to the new_row at the end on line 37 and the outer loop appends rows to the new_pixels list at the end on line 38.

Lines 29-32 get the RGB values of a pixel. While lines 33-35 compute the RGB values for the inverted pixel by subtracting the values from 255. Once we have the new_red, new_green, and new_blue values, we can create a new pixel on line 36.

Finally, we draw the inverted image.

Other pixel manipulation

There are a number of different image processing algorithms that follow the same pattern as shown above.

For example, you can create a gray scale pixel by averaging the red, green and blue intensities and then using that value for all intensities.

From the gray scale you can create black white by setting a threshold and selecting to either insert a white pixel or a black pixel into the empty image.

You can also do some complex arithmetic and create interesting effects, such as Sepia Tone

    iter-9-2: What would the image produced from invert image code above look like if you replaced the lines:

    new_red = 255 - red
    new_green = 255 - green
    new_blue = 255 - blue
    

    with the lines:

    newred = red
    newgreen = 0
    newblue = 0
    
  • It would look like a red-washed version of the bell image
  • Because we are removing the green and the blue values, but keeping the variation of the red the same, you will get the same image, but it will look like it has been bathed in red.
  • It would be a solid red rectangle the same size as the original image
  • Because the red value varies from pixel to pixel, this will not look like a solid red rectangle. For it to look like a solid red rectangle each pixel would have to have exactly the same red value.
  • It would look the same as the original image
  • If you remove the blue and green values from the pixels, the image will look different, even though there does not appear to be any blue or green in the original image (remember that other colors are made of combinations of red, green and blue).
  • It would look the same as the negative image in the example code
  • Because we have changed the value of the pixels from what they were in the original ActiveCode box code, the image will not be the same.
You have attempted of activities on this page
Next Section - 10.9. Glossary