In this project, you will learn how a computer can modify images. You will be programming transformations similar to those built into image processing programs such as Instagram and Photos app.
Please find a partner for this project. Please only submit one copy of the code with both names written in project9.py as comments.
To expedite grade reporting (for graduating seniors), please also write your name and your partner's name (if any) in the text box on Moodle.
If the image is big, try to reduce the size before saving. You can go to Tools -> Adjust Size to change the size of the image.
newR = (R x 0.393 + G x 0.769 + B x 0.189) newG = (R x 0.349 + G x 0.686 + B x 0.168) newB = (R x 0.272 + G x 0.534 + B x 0.131)
sepiaTone
, and include a contract and a descriptive docstring.
For uniformity, convert the RGB values to be integers between 0 and 255 using the int
function.
>>> int(12.4) 12 >>> int(12.9) 12Then think about how you would make sure each value is no larger than 255, using the
min
function.
>>> help(min) >>> min(12.3, 4.5) 4.5Test your
sepiaTone
procedure against various pixel values:
>>> from cImage import * >>> from project9 import * >>> apixel = Pixel(10,200,100) >>> sepiaTone(apixel) (176, 157, 122) >>> apixel = Pixel(10,255,0) >>> sepiaTone(apixel) (200, 178, 138)
transformPixels
that takes an image object and a procedure that transforms a pixel and returns a new image (of data type EmptyImage
) with all the original pixels transformed by the given procedure.
Include a descriptive docstring.
transformPixels
procedure,
first copy the negativePixel
and greyPixel
procedures
from the file
General_Manipulate_Image.py
and paste them into your project9.py
file.
Now you can test your transformPixels
procedure procedure using your negativePixel
and greyPixel
procedures and a .gif file:
>>> from cImage import * >>> myimg = FileImage("hello_dear.gif") >>> myimg_negative = transformPixels(myimg, negativePixel) >>> myimg_grey = transformPixels(myimg, greyPixel) >>> isinstance(myimg_grey, EmptyImage) True
transformPixels
procedure using your sepiaTone
procedure and a .gif file:
>>> from cImage import * >>> myimg = FileImage("hello_dear.gif") >>> myimg_sepia = transformPixels(myimg, sepiaTone) >>> type(myimg_sepia) # this should say 'cImage.EmptyImage'
Instruction: To receive the extra credit (a few points) for this work, show your work and do a demo of it (that is, apply your gamma filters to a picture that is too bright/too dark) in front of your lab instructor on Tuesday, May 16. Both partners should be present during the demo.
When you take pictures, you sometimes find that the pictures are too bright or too dark to see anything in them. Gamma correction allows you to compress or expand the range of luminance in images. It changes the luminance following the power-law expression:
Ynew = (Yold)gamma
However, pixels in our images are in RGB, not in YUV. The following formulas will help you convert between RGB and YUV values.
Y = 0.00117 * R + 0.00230 * G + 0.000447 * B
U = -0.000577 * R - 0.00113 * G + 0.00171 * B
V = 0.00241 * R - 0.00202 * G - 0.00039 * B
Ynew = (Yold)gamma
R = 255 * (Y + 1.13983 * V)
G = 255 * (Y - 0.39465 * U - 0.58060 * V)
B = 255 * (Y + 2.03211 * U)
Modify the RGB values if necessary to make them no smaller than 0 and no larger than 255.
gammaN
that takes a pixel object and a number. It performs a gamma correction with the gamma value of the input number, and return the new pixel.
gammaTwo
that takes a pixel object and performs a gamma correction with the gamma value of 2.0 and return the new pixel.
gammaHalf
that takes a pixel object and performs a gamma correction with the gamma value of 0.5 and return the new pixel.
Note: You may not repeat code lines for the procedure gammaHalf
and gammaTwo
. What you need to do is create a third function called gammaN
(as described above) which takes in a pixel object and a number. This third function should then be called by both gammaHalf
and gammaTwo
.
transformPixels
procedure and a .gif file:
>>> from cImage import * >>> myimg = FileImage("hello_dear.gif") >>> myimg_gammaTwo = transformPixels(myimg, gammaTwo) >>> isinstance(myimg_gammaTwo, EmptyImage) True
rotated
that takes an image object (either a FileImage
object or an EmptyImage
object ) and returns an image of the original image rotated 90 degrees counterclockwise (the popular convention used in Calculus textbooks and popular imaging apps). The returned value should be in the form of an EmptyImage
object (which is no longer empty).
>>> from project9key import * >>> im = FileImage('dear_daniel.gif') >>> rotim = rotated(im) # nothing appears! >>> isinstance(rotim, EmptyImage) TrueNote that, in order for you to view the rotated image, you have to first create an
ImageWin
object and then use the method draw
.
You will do this in Task D below.
drawImages
that takes a title and a list of FileImage
or EmptyImage
objects and draws the images on a new canvas. The images will be drawn onto the canvas side-by-side horizontally with 20-pixel-wide gaps.
Make sure that the canvas is just wide enough and tall enough (not any wider or taller than necessary).
Make sure the title is shown correctly.
Include a descriptive docstring.
>>> img2 = FileImage("hello_dear.gif") >>> img2rot = rotated(img2) >>> img3 = FileImage("princess.bride.gif") >>> img3rottwice = rotated(rotated(img3)) >>> list_of_sample_gifs = [img2, img2rot, img3, img3rottwice] >>> drawImages("Rotated pictures", list_of_sample_gifs)should show the following window (after a few seconds)
The words "princess bride" look the same in the two images because this is an ambigram for the cover of the 20th anniversary edition of Princess Bride.
>>> img1 = FileImage("dear_daniel.gif") >>> img2 = FileImage("hello_dear.gif") >>> img3 = FileImage("princess.bride.gif") >>> img4 = FileImage("gus_winner.gif") >>> list_of_sample_gifs = [img1, img2, img3, img4] >>> drawImages("Sample GIF pictures", list_of_sample_gifs)should show the following window (almost instantly)
>>> img1 = transformPixels(FileImage("dear_daniel.gif"), sepiaTone) >>> img2 = transformPixels(FileImage("hello_dear.gif"), sepiaTone) >>> img3 = transformPixels(FileImage("princess.bride.gif"), sepiaTone) >>> img4 = transformPixels(FileImage("gus_winner.gif"), sepiaTone) >>> list_of_sample_gifs = [img1, img2, img3, img4] >>> drawImages("Sample GIF pictures", list_of_sample_gifs)would show the following window with images in sepia tone (after you wait several seconds for the sepia tone filter to be applied)
if __name__ == '__main__':
in your file, write a few Python statements (similar to the example statements given above) to show the original image and at least two manipulations of the image in the same window using the procedure drawImages
.
NOTE: Make sure you do not write these statements outside of the selection statement if __name__ == '__main__':
NOTE: Please re-download the template file project9.py. I have updated the test files to reflect the fact that you do not need to submit Task B!
for
loop in the statements at the bottom of the file.
EmptyImage
object while you go through the for
loop
instead of creating a brand new EmptyImage
object at each iteration.
Submit your code using Moodle; click on the following link for instructions on submitting code using Moodle.
In the text box on the Moodle submission page, please write your name (and the name of your partner, if you have one).
You're also required to submit one Python file called
which is a modification of the given template file and
contains all the Python procedures (Tasks A, C, and D) and your tests (under the selection statement if __name__ == '__main__':
) as described above.
if __name__ == '__main__':
statement.
cImage.py
.
Before you submit, make sure that your work runs perfectly on IDLE on the computer lab machines.
cImage
module using the following statement at the top of your project9.py
file:
from cImage import *
If you are using your own GIF images (that is, not the ones you downloaded from project9_gif_images.zip) in your tests, please upload them (no more than five).
if __name__ == '__main__':
statement.
Make sure your procedures work on images that are not squares!
Pixel
object with correct values for different inputs.
EmptyImage
object of correct values when for the downloaded images and filter function that is sepiaTone
, negativePixel
, and greyPixel
.