Please get the code examples and image used in this lab, by clicking here
Have you ever noticed that when you mouse over the "movie progress bar" in YouTube you can see a series of expanding images: http://www.youtube.com/watch?v=gHn4-L2C3t0? Without knowing it, you may intuitively understand that a movie is a sequence of images that are displayed one after the other.
This summer I was lucky enough to have monarch butterfly caterpillars in my yard. I filmed one of them after it had emerged from the chrysalis. I then manipulated the images to change the color of the butterfly-something we will be doing the lab! The below is a sequence of the manipulated images. You can gain a sense of how the butterfly was moving. There is a reason why movies are called "motion pictures".
As always, when you are working with something new, terminology is very important. A couple of key definitions are:
Frame rate is important. What happens if you have a film that was recorded at 16 fps and you play it at 24 fps? or vice versa?
We are going to focus on making "silent" movies in this lab. All of the techniques that we learned for manipulating images (such as grayscale, blending with white, negating, etc) work with movies too. The difference is that instead of modifying one image, we will be modifying a sequence of images. There are a couple of JES specific functions that enable you to easily create and play movies:
To find some other functions related to movies, you can look in the JES menu under Help > Understanding Video. Click on "Video Functions in JES". You might find some cool stuff in there.
To make a movie, all we need is a directory with some frames. Conveniently enough, we have such a directory (called Monarch) included with this lab's sample code. Let us try it:
>>> myMovie=makeMovieFromInitialFile("/Users/yourUserName/Downloads/codeAndSamples/Monarch/frame000") >>> playMovie(myMovie)
The following is a screen capture of the playMovie tool:
Notice how you can click on the Prev and Next buttons to see the images change from frame to frame. When you "Play Movie", however, it looks like a movie of a butterfly twisting.
If you are working from home on a Windows machine, you can download and use Avidemux. More information is available by clicking here. Make sure that you download the older version of Avidemux because more recent versions are missing the functionality shown in the notes.
I told you that I created a video of the butterfly just emerging (saved on my camera as a .mov) file. How did I extract the frames from the .mov file? On the Mac, you can use iMovie. If you would like to follow along, right click here and select "Download Linked File" from the pop up menu.
The steps are as follows:
Let us create an animation without any pictures. The animation in this case, is created by moving text. This might be useful if, for example, you want to create a slideshow with animated titles. The code is below.
#program 120, page 316 def tickerTape(directory, string): for num in range(1,100): #99 frames canvas = makeEmptyPicture(300, 100) #Start at right, and move left addText(canvas, 300 - (num*10), 50, string) #Now, write out the frame #Have to deal with single digit vs. double digit frame numbers differently numStr = str(num) if num < 10: writePictureTo(canvas, directory + "//frame0" + numStr + ".jpg") if num >= 10: writePictureTo(canvas, directory + "//frame" + numStr + ".jpg") movie = makeMovieFromInitialFile(directory + "//frame01.jpg") return movie
Remember, an animation is a sequence of images. This code generates 99 frames (images of size 300 x 100 pixels) and stores them in directory. The algorithm is:
If we would like to see the results, we can run it with the following:
>>> myMovie=tickerTape("/Users/yourUserName/Desktop/Testing", "CS 325 is fun") >>> playMovie(myMovie)
Do not forget to create the directory called Testing on the Desktop and change yourUserName to your own user name.
To remove all of the frames in the directory before continuing, click on the Delete All Previous button.
As we have said before, movies are just sequences of images. If you have an image and gradually apply color changes to a series of frames, an animation will be created. The below code is an exaggerated "sunset" animation using a picture of a lab instructor driving into the sunset on a go-cart (the go-cart part is real, the sunset is a little fabricated).
#program 125, page 320 and 321 #call setMediaPath() before calling this function def slowSunset(directory): #outside the loop! canvas = makePicture(getMediaPath("mySunsetSmall.jpg")) for num in range(1, 100): #99 frames printNow("Frame number: " + str(num)) makeSunset(canvas) #Now, write out the frame writeFrame(num, directory, canvas) movie = makeMovieFromInitialFile(directory + "//frame001.jpg") return movie def makeSunset(picture): for p in getPixels(picture): value = getBlue(p) setBlue(p, value * 0.99) # Just 1% decrease! value = getGreen(p) setGreen(p, value * 0.99)
When you run this code, it may take more than a couple of minutes to generate all of the frames.
The algorithm is as follows:
The writeFrame() function is provided below:
#program 124, page 320 def writeFrame(num, dir, pict): #Have to deal with single digit vs. double digit numStr = str(num) if num < 10: writePictureTo(pict, dir + "//frame00" + numStr + ".jpg") if num >= 10 and num < 100: writePictureTo(pict, dir + "//frame0" + numStr + ".jpg") if num >= 100: writePictureTo(pict, dir + "//frame" + numStr + ".jpg")
The if statements determine if the frame number should be preceded with leading 00, 0, or just the frame number. Otherwise, the purpose of this code is to call the writePictureTo() function, which writes the image (with appropriate incrementing frame number) to directory.
To remove all of the frames in the directory before continuing, click on the Delete All Previous button.
We have an image of a butterfly; and we want to make it "fly" on the screen. We can do that by "clipping" the butterfly out of its original image and copying it to different locations on a "canvas". Before we can "clip" the butterfly, we use the explorer tool to find the coordinates for the upper left-hand corner (174, 137), and the lower right-hand corner (371, 312) of the boundaries of the butterfly. The below subsections examine the functions used to make this animation happen.
First, let us look at the moveButterfly() function that calls all of the other functions:
#modified from program 123, page 319 #call setMediaPath() before calling this function
def moveButterfly(directory):
butterfly = getMediaPath("butterflySmall.jpg")
butterflyPict = makePicture(butterfly)
butterF = clip (butterflyPict, 174, 137, 371,312)
for num in range(1, 30): #29 frames
printNow("Frame number: " + str(num))
canvas = makeEmptyPicture(640, 480)
#Now, do the actual copying
copy(butterF, canvas, num * 10, num * 5)
#Now, write out the frame
writeFrame(num,directory,canvas)
movie = makeMovieFromInitialFile(directory + "//frame001.jpg")
return movie
To summarize the code:
The clip() function is shown below:
# part of program 122, page 319 def clip(picture, startX, startY, endX, endY): width = endX - startX + 1 height = endY - startY + 1 resPict = makeEmptyPicture(width, height) resX = 0 for x in range(startX, endX): resY = 0 #reset result y index for y in range(startY, endY): origPixel = getPixel(picture, x, y) resPixel = getPixel(resPict, resX, resY) setColor(resPixel, (getColor(origPixel))) resY = resY + 1 resX = resX + 1 return resPict
Notice the following things:
The code for the copy() function is shown below:
#program 30, page 97 def copy(source, target, targX, targY): targetX = targX for sourceX in range(0, getWidth(source)): targetY = targY for sourceY in range(0, getHeight(source)): px = getPixel(source, sourceX, sourceY) tx = getPixel(target, targetX, targetY) setColor(tx, getColor(px)) targetY = targetY + 1 targetX = targetX + 1
To briefly describe this function:
To remove all of the frames in the directory before continuing, click on the Delete All Previous button.
This is the section that you all have been anticipating!!! This section answers the question of how to modify the movie of the butterfly so that the wings are magenta. Remember from section 1.2 that we extracted about 200 frames from monarch3.mov. The below code will go through every second extracted frame in that directory, and call a function called clipAndFilter(). clipAndFilter() does three things: 1) clips each frame to be the size of the twisting butterfly, 2) skips every second pixel (to reduce the size of the frame), and 3) changes colors that are approximately orange to magenta.
def cropFrames(directory): monarchDir=setMediaPath() num = 0 even = 0 for file in os.listdir(monarchDir): if file.endswith(".jpg") and even%2==0: #skip every second frame printNow("Frame number: " + str(num)) origFrame = makePicture(monarchDir + "//" + file) #Now crop newFrame = clipAndFilter(origFrame, 400, 110, 1095, 899) #Now write to new location writeFrame(num, directory, newFrame) num = num + 1 even = even + 1 movie = makeMovieFromInitialFile(directory + "//frame000.jpg") return movie
Some new things in the code above are:
Every second file that ends in ".jpg" is turned into a Picture object and then sent to the clipAndFilter() function to be processed. The below is the code for that function:
def clipAndFilter(picture, startX, startY, endX, endY): width = endX - startX + 1 height = endY - startY + 1 resPict = makeEmptyPicture(width/2, height/2) resX = 0 for x in range(startX, endX, 2): resY = 0 #reset result y index for y in range(startY, endY, 2): origPixel = getPixel(picture, x, y) origColor = getColor(origPixel) resPixel = getPixel(resPict, resX, resY) #the following line cannot be broken into multiple lines if (distance(origColor, makeColor(157, 128, 36)) <= 45) or (distance(origColor, makeColor(236,226,175)) <= 45) or (distance(origColor, makeColor(215,196,91)) <= 55): setColor(resPixel,makeColor(255,0,255)) else: setColor(resPixel, (origColor)) resY = resY + 1 resX = resX + 1 return resPict
Normally, the "clip" operation would be done in a separate function from the "color change" operation. As it is, this code takes a long time (two or three frames are written per minute). If we had to cycle through all the pixels again in another function, it would probably slow down the code even farther.
Some questions that we can answer about the code are:
As a note, the color such as (157, 128, 36) could be found by using the explore tool on one of the frames.
For this exercise, you will be given the option to complete one of two things:
For the movie that you create, you should have at least 100 frames.