Python Movies


Topics

  1. What is a Movie?
  2. Creating Animated Text
  3. Animating Through Color Change
  4. Moving a Cropped Picture Around
  5. Modifying "Movie" Frames
  6. References
  7. Exercise

Please get the code examples and image used in this lab, by clicking here


1. What is a Movie?

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".

frame000 frame003 frame006 frame009 frame012

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?

1.1 Making and Playing Movies in JES

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/Monarch/frame000")
>>> playMovie()

The following is a screen capture of the playMovie tool:

playMovie

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.

1.2 Extracting Frames Using Avidemux

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? I used a tool called Avidemux. There are other tools on the Mac, but this is a very straight forward method. If you would like to follow along, click here for the movie file.

The steps are as follows:

  1. Use the Spotlight tool to find Avidemux2. Click on the application

  2. Under the File menu, choose Open... and select your movie file

  3. Find an interesting place in the movie from where you would like to extract frames. Write down the frame numbers that appear in the bottom left-hand side. If you are following along in the butterfly movie, a good range is from 390 to 590.

  4. When the circle is at 390 in the movie progress bar, click on the A

    Alternatively, type 390 in the Frame box and hit enter. Then, click on the A

  5. Move the circle in the movie progress bar to 590. Then, click on the B

    Alternatively, type 590 in the Frame box and hit enter. Then, click on B

  6. Under the File menu, choose Save > Save Selection as JPEG Images...

  7. In the dialog box that pops up, choose a directory and type a name in the Save As: box. All the files will have that name with frame numbers appended.

If you would see a video of this instead, watch: http://www.youtube.com/watch?v=nbEafZUCujc


2. Creating Animated Text

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.


3. Animating Through Color Change

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:

3.1 writeFrame()

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.


4. Moving a Cropped Picture Around

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.

4.1 moveButterfly()

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:

4.2 clip()

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:

4.3 copy()

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.


5. Modifying "Movie" Frames

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.


6. References


7. Exercise

For this exercise, you will be given the option to complete one of two things:

  1. Modify the Python code in section 5 to work with your own movie. You should clip a portion of your movie frame out, reduce the size, and change the color in some way (that might mean grayscale, negate, or selective color change).

  2. Create a "slideshow" with three images. To implement this, you will create at least two transitions (picture entering and picture exiting) to use for each picture. You can be creative such as blending, sliding in, expanding, shrinking, etc. Titles are optional. The following is an example of a slideshow: SlideShowFrames.mov. This slideshow has 20 frames for each image and 20 frames for each transition (140 frames in total). Four transition functions were used: a fade in (or out), an entry from the bottom corner, an exit out of top corner, and a middle expand (or shrink).

For the movie that you create, you should have at least 100 frames.