## 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    srcpixels = image.load()        # loads in the pixels from the destination image    dstpixels = dstimage.load()        # 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    srcpixels = image.load()        # loads in the pixels from the destination image    dstpixels = dstimage.load()        # 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    srcpixels = image.load()        # loads in the pixels from the destination image    dstpixels = dstimage.load()        # 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. 