Jason M. Grant

Assistant Professor of Computer Science

Image Manipulation, Pt. 2

In this lecture, we will look at a few simple image transformations. So far, we have looked at isolating a single channel in a RGB image. Throughout the week, you should also be working on similar transformations, including converting an image to grayscale, black and white, and sepia. Today, we will look at functions to increase or decrease the brightness, contrast, and temperature of an image. Furthermore, we will investigate image rotations, and blurring.

Let’s begin with changing the brightness of an image. Naively, we could write one function that increases the brightness of an image and a section function that decreases the brightness. However, it is more beneficial to write a function that takes in an input argument to either scale up or scales down the values. This can be done through either multiplication or addition. In this instance, we will use multiplication. Therefore, a scale factor between 0 and 1 would make our image darker and a scale factor greater than 1 would make our image brighter.

def brightness(image, scalefactor):
"""
Given an input image, this function brightens the image by
multiplying each value by a scale factor. If the value is between
0 and 1, the image will be darkened. If it is greater than 1, the
image will be brightened.
"""

# gets the size of the source image
minX, minY, width, height = image.getbbox()

dstimage = Image.new('RGB',(width, height), color = (255,255,255))

# loads in the pixels from the original image

# loads in the pixels from the destination image

# The height and width refer to the source/original image
for y in range(height):
for x in range(width):
rgb = srcpixels[x,y]

newred = int(rgb * scalefactor)
newgreen = int(rgb * scalefactor)
newblue = int(rgb * scalefactor)

if newred > 255:
newred = 255
if newgreen > 255:
newgreen = 255
if newblue > 255:
newblue = 255

dstpixels[x,y] = newred, newgreen, newblue

return dstimage

By brightening the brighter pixels of an image and darkening the darker pixels, we can increase the contrast of the image. This operation is very similar to what we have done to increase the brightness; however, now we want to consider a threshold where part of the image gets brighter and part of the image gets darker. Practice writing a function contrast(image, threshold, scalefactor)that increases the contrast of the image. The argument scalefactorshould be a value between 0 and 1, where a small value (i.e. 0.15) increases the contrast by just a little and a larger value increases the contrast by a lot. Can you think of a way to adapt this function so that a negative value decreases the contrast?

Next, let’s consider image rotation. We will begin with a clockwise rotation, but similarly we could write a transformation for a counterclockwise rotation.

Original Image

 1 2 3 4 5 6

Transformation Image

 5 3 1 6 4 2

If this small example, the numbers in the image represent luminance. If we were to use a much larger image example, we could express a formula for the transformation of one pixel location to the next.

src: (x,y) –> dst: (height-1 – y, x)

Using this transformation, we can copy the pixel values from our original image into our rotated image.

def rotateClockwise(image):
"""
Given an input image, this function rotates the image
clockwise and returns the new image
"""

# gets the size of the source image
minX, minY, width, height = image.getbbox()

dstimage = Image.new('RGB',(height,width), color = (255,255,255))

# loads in the pixels from the original image

# loads in the pixels from the destination image

# The height and width refer to the source/original image
for y in range(height):
for x in range(width):
rgb = srcpixels[x,y]
dstpixels[(height-1) - y,x] = rgb

return dstimage

The approach for rotating an image counterclockwise would be just about the same. The only thing that would change would be our transformation function.

src: (x,y) –> dst: (y, width-1 – x)

Lastly, we can implement a function to blur an image. A blurred effect can be used to “soften” an image and also remove some noise. In this function, we want to take a region of pixel values and average them together, then replace the current value with the average of the four pixel values.

 [x,y] [x+1,y] [x,y+1] [x+1,y+1]

For each pixel value [x,y], we want to average the values of its neighboring pixels and use the average value in the new blurred image.

def blur(image):
"""
Given an input image, this function smoothes the image by averaging
neighboring pixels. The function returns a new image that contains
the output.
"""

# gets the size of the source image
minX, minY, width, height = image.getbbox()

dstimage = Image.new('RGB',(width, height), color = (255,255,255))

# loads in the pixels from the original image

# loads in the pixels from the destination image

# The height and width refer to the source/original image
for y in range(height - 1):
for x in range(width - 1):

# store totals for all of the RGB channels

red = 0
blue = 0
green = 0

# sum all of the pixels in the blur window
for i in range(2):
for j in range(2):
rgb = srcpixels[x+i,y+j]

red = red + rgb
green = green + rgb
blue = blue + rgb

# Set the average of all pixels to the dst image
dstpixels[x,y] = red//4, green//4, blue//4

return dstimage

This function only works if we want to use a neighboring window of 2×2, meaning that we take the average of four pixels. What if we wanted to average a larger set of pixels? Is there a way that we could modify the existing code to work for a neighborhood of n-size? Practice by writing a function blur(image, winsize)that works for a window of any size. 