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

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 iMovie

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:

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

    Spotlight Picture

  2. Under the File menu, choose Import | Movies... and select your movie file.

  3. It takes a little time to process the movie. When it done, your movie should appear in the bottom with a preview in the upper right.

  4. Select a range of frames by adjusting the yellow "markers". Click and drag the selected frames into upper left panel as shown below.

    Drag Frames

  5. Now you can export that selection as jpg images. Choose from the main menu Share | Export Using QuickTime...

    Export Using QuickTime

  6. In the dialog box, choose a directory and provide a file name to "Save As". Change the "Export" option to Movie to Image Sequence. Then, click on the Options button.

    Export Dialog Box

  7. You can change the "Format" to JPEG and the "Frames per second" to 16. If you want the filenames without spaces, make sure to uncheck "Insert space before number"
    Image Sequence Setings

  8. Once you have clicked on OK and then Save, the frames will be extracted and saved in your choosen directory. All the files will have the name you chose to "Save As:" with frame numbers appended.

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 JES/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 using JES/Python. 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.