Monday 6 January 2014

Creating an Animation Studio with a Raspberry Pi and Python

When I was younger I used to love the character Morph. For those of you too young to remember, he was an animated plasticine model who appeared alongside Tony Hart.



Image courtesy of Giles Farrington via Flickr


He was created by Aardman Animations who went on to create other animated films such as Wallace and Gromit and Chicken Run. I often thought that it would have been great fun to create my own plasticine models and capture them on film. There was one small problem... making plasticine models was one thing, filming them and turning them into a film was something else!!

After finishing my blog which turned a Raspberry Pi and camera into a time-lapse camera I started to think about this. It's not a great leap to go from making time-lapse movies to animation movies. I also thought while I am at it, why not wrap it up with a nice user interface?

What a great idea for a blog post I thought ...

... so here is the Python code for your very own Raspberry Pi animation studio! All you need is a Raspberry Pi, a camera for your Pi and the contents of this blog.

This is written in Python 2.7 and not Python 3. To get started you will need to use the IDLE icon on your Raspberry Pi rather than the IDLE 3 icon. For those of you new to the Python world, don't spend too much time worrying if you should using Python 2 or 3. Its easy to change from one to another. I am using Python 2.7 for this program as we will need access to some libraries, which are not available yet in Python 3.

Before we dive into the code lets think about what we want to achieve.

  • We probably want a Graphical User Interface (GUI) to make things look nice.
  • We definitely want to be able to take pictures, and save them to add into our film.
  • We all make mistakes, so we want to delete a frame if we are not happy with it.
  • We probably want to be able to scroll through our frames to see what they look like.
  • We want to turn our images into a film, and we may want to adjust some inputs to tweak that process.


Sounds as though we have the basic structure of our program defined.

Before we embark on this, if you have not already done so please read my blog post explaining how to get started with Tkinter (For the GUI) and my time-lapse blog. I will borrow heavily from both of these blogs, as if you have some code written which works... why would you write it again from scratch? In fact I keep all my code I have written and am always referring back to it, which I think is good practice, and one you should adopt!

One final word before you get started, I would recommend you save your work very often, just in case!

Lets get started!

The first thing we need to do is to install a few libraries which we need to run this program. We will be using a tool called avconv. This can be installed by typing the following into a command line.

sudo apt-get install -y libav-tools

You will also need to install python-imaging-tk

sudo apt-get install -y python-imaging-tk

First of all here it the complete program. Have a read through it and see which parts you understand. If you have followed my previous blogs it should all be quite straight forward. We will then break it down into more detail in a minute.

from Tkinter import *
from ttk import *
import os
import glob
import Image, ImageTk

## Functions to select frames
def firstFrame():
    if countFrames()>0:
        frameNumber.set(1) #If there are any frames
    else:
        frameNumber.set(0) #Set to 0 if there are none
    updateGUI()
    return None

def lastFrame():
    frameNumber.set(countFrames())
    updateGUI()
    return None

def nextFrame():
    if frameNumber.get() < (countFrames()):
        frameNumber.set(frameNumber.get()+1)
    updateGUI()
    return None

def prevFrame():
    if frameNumber.get() > 1:
        frameNumber.set(frameNumber.get()-1)
    updateGUI()
    return None

def takeFrame():
    frameNumber.set(countFrames()+1)   
    os.system("raspistill -o image%s.jpg"%(setZero()))
    updateGUI()
    return None

##Deletes the last stored frame
def deleteFrame():    
    frameNumber.set(countFrames())
    if countFrames() > 0:
        os.remove('image%s.jpg'%setZero())
        frameNumber.set(countFrames())
        updateGUI()
    return None

##Returns the number of jpg files in the directory.
def countFrames():
    openFiles = glob.glob('image*.jpg')
    return len(openFiles)

## sets the right number of zeros for the frameNumber
def setZero():
    return str(frameNumber.get()).zfill(7)

##Updates the image in the GUI and refreshes
def updateGUI():
        
    if countFrames()>0: # Checks to see if there are any existing frames
        fileName = "image%s.jpg"%setZero()
        inFile = Image.open(fileName)        
        ## Resize image to suit GUI
        ## Was 2592 x 1944 - Divide both of these by 6
        newSizeFile = inFile.resize((400,300))
        
        displayFile =  ImageTk.PhotoImage(newSizeFile)#Convert to Tkinter compatible
        Label(mainframe, image=displayFile).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)
        root.update()#Refreshes
                
    else:
        
        frameImage = PhotoImage(file = "blankScreen.gif")
        Label(mainframe, image=frameImage).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)
        root.update()

## Creates the video file.
def createFilm():
    setfpsIn = fpsIn.get()
    setfpsOut = fpsOut.get()
    os.system("avconv -r %s -i image%s.jpg -r %s -vcodec libx264 -crf 20 -g 15 -vf crop=2592:1458,scale=1280:720 timelapse.mp4"%(setfpsIn,'%7d',setfpsOut))
    return None


##Setup of main GUI
root = Tk()
root.title ('Animation Studio')
root.geometry("410x450")

mainframe = Frame(root, padding = '3 3 12 12')
mainframe.grid(column=0, row = 0, sticky=(N,W,E,S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0,weight=1)

##Variables
frameNumber = IntVar()
fpsIn = IntVar()
fpsOut = IntVar()

##Set Variables
frameNumber.set(countFrames())
fpsIn.set(10)
fpsOut.set(24)

##Frame Select
Button(mainframe, text="<<", command = firstFrame).grid(column = 1, row = 2, sticky = 'WE')
Button(mainframe, text="<", command = prevFrame).grid(column = 2, row = 2, sticky = 'WE')
Button(mainframe, text=">", command = nextFrame).grid(column = 4, row = 2, sticky = 'WE')
Button(mainframe, text=">>", command = lastFrame).grid(column = 5, row = 2, sticky = 'WE')
Entry(mainframe, textvariable = frameNumber, width = 7).grid(column = 3, row = 2, sticky = 'WE')


##Take Frame
Button(mainframe, text="Take Frame", command = takeFrame).grid(column = 1, row = 3, sticky = "WE", columnspan = 5)

##Delete Frame
Button(mainframe, text="Delete Last Frame", command = deleteFrame).grid(column = 1, row = 4, sticky = "WE", columnspan = 5)

##Set FPS in
Label(mainframe, text='Set FPS In').grid(column=1, row = 5, sticky = W)
Entry(mainframe, textvariable = fpsIn, width = 7).grid(column = 2, row = 5, sticky = 'WE')

##Set FPS out
Label(mainframe, text='Set FPS Out').grid(column=4, row = 5, sticky = W)
Entry(mainframe, textvariable = fpsOut, width = 7).grid(column = 5, row = 5, sticky = 'WE')

##Create Film
Button(mainframe, text="Create Film", command = createFilm).grid(column = 1, row = 7, sticky = "WE", columnspan = 5)

updateGUI()

root.mainloop()

To start with there are a few libraries which you will need to access. These are always imported first into your program, as you cannot use them until they have been imported.

from Tkinter import *
from ttk import *
import os
import glob
import Image, ImageTk

Next we have all the function which the program calls. Before we write these, let us jump down the code a little and set out our GUI. Once the GUI is set up we can write the required functions later.

##Setup of main GUI
root = Tk()
root.title ('Animation Studio')
root.geometry("410x450")

These lines of text set up the layout of the GUI. They help us define the name and the size of the window. The values stated as 410x450 are the width and height of our GUI. You can change these if you would like to, but I think these are good values.

Now we define a frame within our GUI.

mainframe = Frame(root, padding = '3 3 12 12')
mainframe.grid(column=0, row = 0, sticky=(N,W,E,S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0,weight=1)

Within our window we need to have one frame. Frames can split the window into different areas. These lines of code define how this frame fits within our window.

As you can see we now define a few variables.

##Variables
frameNumber = IntVar()
fpsIn = IntVar()
fpsOut = IntVar()

I know from my time-lapse program that I might want to change a few parameters when I am creating my film, for example I might want to change how many of my frames I want to display every second (fpsIn), and I might want to change the frame rate the video is created at (fpsOut).

The other variable I will need is to know which of the frames we are currently looking at on the GUI. We will call this variable frameNumber.

Once I have created the variables, I want to assign them values.

##Set Variables
frameNumber.set(countFrames())
fpsIn.set(10)
fpsOut.set(24)

I set the value of fpsIn to be 10, and fpsOut to be 24. The software will allow you to change them later, but these are good default values for you to start with.

Setting the frameNumber looks a little different. I am not setting it with a number, but a function. What is that all about? Well I think when you load the program it would be nice to set the variable frameNumber to the last frame you have taken. You can then carry on from where you left off. Therefore I will write a function called countFrames() which returns the number of frames I have taken. We will get onto writing the function for this later.

Lets now look at laying out the actual GUI.

Firstly we want to be able to scroll through our images. I think it would be nice to scroll through them one at a time and also jump to the first and the last frame. We have created a variable called frameNumber which holds the information about the current frame we are displaying. We should also display that number in our GUI, so we know what we are looking at.

My proposal is for the buttons to use greater than or less than symbols like this < or this > to move forward or back one frame. We will also use two of them together to jump to the first or last frame like so << >>. We will also want to display a number showing which of our images we are looking at.

So our buttons should look this this

<<   <   frame number   >   >>

The code to create these 4 buttons and text entry field is as follows:

##Frame Select
Button(mainframe, text="<<", command = firstFrame).grid(column = 1, row = 2, sticky = 'WE')
Button(mainframe, text="<", command = prevFrame).grid(column = 2, row = 2, sticky = 'WE')
Button(mainframe, text=">", command = nextFrame).grid(column = 4, row = 2, sticky = 'WE')
Button(mainframe, text=">>", command = lastFrame).grid(column = 5, row = 2, sticky = 'WE')
Entry(mainframe, textvariable = frameNumber, width = 7).grid(column = 3, row = 2, sticky = 'WE')

Here we have created 4 buttons and a text entry field. Each of the four buttons calls a different function to allow us to jump to the first/previous/next/last frame, depending upon which button we click.
The last line is an entry field, which we are using to display the value stored in our variable frameNumber.
As we are using the grid layout system, each of these items is positioned using the row and column values. You can see they are all on the same row, but in different columns.

Below these buttons we want a button which allows us to take a frame or picture.

##Take Frame
Button(mainframe, text="Take Frame", command = takeFrame).grid(column = 1, row = 3, sticky = "WE", columnspan = 5)

This is a button we will want to click on to take a new frame. The button is laid out as the buttons above, but has a different label "Take Frame" and calls a different function called takeFrame when clicked. The other difference is we have a columnspan value in there. This is telling our program do not constrain the button to one column. Otherwise it would be the same width as the << button, but allow it to span 5 columns. This should make the button the same size as the size of the <<   <   frame number   >   >>   buttons combined.

We will now create a button to allow us to delete a frame.

##Delete Frame
Button(mainframe, text="Delete Last Frame", command = deleteFrame).grid(column = 1, row = 4, sticky = "WE", columnspan = 5)

This follows the same format as the Take Frame button. Obviously it has a different title and calls a different function.

Now for the values of fpsIn and fpsOut we create a text entry field to define them, and a label to explain the text entry field.

##Set FPS in
Label(mainframe, text='Set FPS In').grid(column=1, row = 5, sticky = W)
Entry(mainframe, textvariable = fpsIn, width = 7).grid(column = 2, row = 5, sticky = 'WE')

##Set FPS out
Label(mainframe, text='Set FPS Out').grid(column=4, row = 5, sticky = W)
Entry(mainframe, textvariable = fpsOut, width = 7).grid(column = 5, row = 5, sticky = 'WE')

Our final button is the button to create the film. This is what we will click on when all the images are taken and we want the film to be made.

##Create Film
Button(mainframe, text="Create Film", command = createFilm).grid(column = 1, row = 7, sticky = "WE", columnspan = 5)

Again this is similar to the other buttons we have created.

The next thing we need is to show the image we have taken. To show images in Tkinter we need to put them into what is called a label. However the other thing we want to do is to update the label each time we change the image. This happens quite often in our program, therefore to save us writing the same code over and over again, we will put the label command into a function which allows us to update it when we need to. We shall create the call to the function now, but we will come back to writing the function in a minute.

updateGUI()

Although this function displays the image at the top of the GUI, as this function refreshes the Tkinter screen it is important that this function is called after all the other widgets, such as buttons / labels, created in the GUI.

root.mainloop()

Finally we finish this section with a root.mainloop() which is important to get our Tkinter program running.

OK we should now have defined out GUI. However we skipped over any functions it called, so we need to go back and write those now.

The functions in my program appear in an order I think they make sense to be in. However they are not in order of what was written first. I will not go through them from top to bottom, but will jump around a little. As some functions call other functions I think the order I am explaining them in will start to make some sense.

First of all we have talked about a function called countFrames(). This is a function that will count the number of frames and return the total number back to us. We will use this function throughout the program, and have already called it while setting the value in our variable frameNumber.

Lets look at writing a function called countFrames()

##Returns the number of jpg files in the directory.
def countFrames():
    openFiles = glob.glob('image*.jpg')
    return len(openFiles)

The first line defines the name of the function and the fact it takes no inputs.

In the timelapse blog we saved our files with the file-name in the format imageXXXXXXX.jpg. Where XXXXXXX was an incremental number. We will keep the naming convention the same in this program.


The next line uses a module called glob, which we imported at the top of our program. This line creates a list called openFiles. Then it stores all the file-names which start with "image" and end with ".jpg" into this list. Remember a * denotes a wildcard, so the program finds all options which fit with the * substituted for real text. This means whatever number our file has in it, this line will add it into the list.

Finally we can return the length of the list, which is counting how many items are in the list. This will be the number of frames we have.

We could have written this function on one line, however I think think using two lines makes it more readable.

This function we have just written returns a number. This is very convenient, as when we are doing anything with the frames, such as scrolling through them it makes sense to refer to each frame by a number. A number is also very easy for Python to increase or decrease. However for each frame we also save a picture file. We spoke earlier that this file name would be in the format imageXXXXXXX.jpg. This ensures that the file names are all the same size and stay in the correct order within our folder. To turn our frame number into this format we will have to add some leading zeroes onto it, and turn it into a string (text).

As an example image 1 would become image0000001.jpg, image 100 becomes image0000100.jpg etc. Seven digits in total should be more than enough for your film.

We will call a function which will find the number of the frame we are in, and return the number, as a string, with the right amount of zeros in front of it.

This will help us determine our file-name.

## sets the right number of zeros for the frameNumber
def setZero():
    return str(frameNumber.get()).zfill(7)

The first line defines the function name, as the brackets are empty there are no parameters being passed into it.
The second line does 4 things.

  • It gets the current frame number - frameNumber.get()
  • It converts the number into a string - str(frameNumber.get())
  • It adds some leading zeros so there are 7 digits. It does this with the zfill command - str(frameNumber.get()).zfill(7)
  • It returns this string back to where it was called - return str(frameNumber.get()).zfill(7)

Very easy. The key to this is using the .zfill function to add in the leading zeros.

We talked previously about displaying our current image in Tkinter. Tkinter uses a Label to display images. Although we defined all other parts of the GUI earlier we said as we would have to refresh this label quite often, so it made sense to write it in a function, so we can simply call the function every time we want to refresh the image.

Let us write that function now.

##Updates the image in the GUI and refreshes
def updateGUI():
        
    if countFrames()>0: # Checks to see if there are any existing frames
        fileName = "image%s.jpg"%setZero()
        inFile = Image.open(fileName)        
        ## Resize image to suit GUI
        ## Was 2592 x 1944 - Divide both of these by 6
        newSizeFile = inFile.resize((400,300))
        
        displayFile =  ImageTk.PhotoImage(newSizeFile)#Convert to Tkinter compatible
        Label(mainframe, image=displayFile).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)
        root.update()#Refreshes
                
    else:
        
        frameImage = PhotoImage(file = "blankScreen.gif")
        Label(mainframe, image=frameImage).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)
        root.update()

## Creates the video file.
def createFilm():
    setfpsIn = fpsIn.get()
    setfpsOut = fpsOut.get()
    os.system("avconv -r %s -i image%s.jpg -r %s -vcodec libx264 -crf 20 -g 15 -vf crop=2592:1458,scale=1280:720 timelapse.mp4"%(setfpsIn,'%7d',setfpsOut))
    return None

This is quite a large function, so I will repeat each line below so you know which line I am referring to to help save any confusion!

The first line as always defines the name of the function. Nothing is being passed into this function.

def updateGUI():

As we are putting one of our images into the GUI we need to first of all do a check. What if there are no images already? Not having an image there will ruin our layout a little. Therefore we need a contingency for when we have not taken any images.

Lets first deal with the case when there are some images. We can use our countFrames function to determine if any images have been taken. If this reveals a number greater then 0 there are some images. That countFrames() function is becoming quite useful!

    if countFrames()>0: # Checks to see if there are any existing frames

Now we want to open up the file which relates to the frame we are currently viewing. Previously we showed we can transform the number of the current frame into a string with some leading zeros already attached by running our function setZero() well we do that in the next line.

        fileName = "image%s.jpg"%setZero()

This creates a variable called fileName. It populates it with the string "image%s.jpg", but the clever part of this line is it replaces the %s after image with whatever is returned from our setZero function. %s is an easy way to get different information into strings.

Now we have our file name we can use Image.open to open that file.

        inFile = Image.open(fileName) 

 I know that the images the camera takes are 2592 x 1944 pixels. These are quite large. To make it easier to view on the screen I will resize these. By dividing 2592 by 6 and 1944 by 6 I get 400 x 300. Apart from being nice numbers and about the right size it ensures the aspect ration is kept the same i.e. the images are not stretched either horizontally or vertically.

        newSizeFile = inFile.resize((400,300))

 Tkinter cannot display .jpg images. So we have to convert our image to allow Tkinter to display it. We do this by the following command.

        displayFile =  ImageTk.PhotoImage(newSizeFile)

 Finally we get around to creating the label which we use to display files in Tkinter.

        Label(mainframe, image=displayFile).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)

 We would normally have placed this line with all the other Tkinter buttons and labels. However as we want to decide when we want to update the label it was better off as a function. If we were not modifying the image we may have left it in the usual place, with all the other Tkinter widgets.

 Finally we refresh the window

        root.update()#Refreshes

 So that covers the times when we have already taken frames. We now need to look into the times when there are no existing frames. This is likely when we are using the project for the first time and do not have existing .jpg images.

If we did not load an image the positioning of our GUI would all be thrown out. Therefore if there are no images we will load one which we have pre-created.

 We need to start with an else statement.

    else:

 Now we load our image, which this time is a .gif image. We don't need to do any conversion on this as Tkinter handles .gif images quite nicely. We load the file and store it in a variable called frameImage

        frameImage = PhotoImage(file = "blankScreen.gif")

 Now as before we create a Label to display this blank image, and then refresh the screen.

        Label(mainframe, image=frameImage).grid(column=1, row = 1, sticky = 'WE',columnspan = 5)
        root.update()

And that finishes our updateGUI function. It is important that you have the blankScreen.gif image in the same folder as your Animation Studio folder, otherwise the software will not work.

 We should start now looking at the buttons for scrolling through the images.

## Functions to select frames
def firstFrame():
    if countFrames()>0:
        frameNumber.set(1) #If there are any frames
    else:
        frameNumber.set(0) #Set to 0 if there are none
    updateGUI()
    return None

The idea of this function is to go to the first frame or picture we have taken. As we have already created a variable called frameNumber, we should be able to set that to the right value, and then update our GUI to suit. Should be simple... but we have to cover all eventualities. What if we have not taken a picture? We need to ensure our program deals with that.

Using an if ... else statement should be just the trick.

If we have taken frames previously the number of frames will be greater than 0. Therefore frame 1 will be the first frame, so we should set the frame we want to look at as 1. If we have not taken any frames, then the first will be frame 0. We will set the variable frameNumber to suit.

After naming our function the first line makes use of our frameCount function we have written earlier.
def firstFrame():
    if countFrames()>0:
        frameNumber.set(1) #If there are any frames

This says if the value from countFrames is greater than 0, which should be the case if any frames have been taken, then we can set the frame number to 1. This should be the first frame we have taken.

If the else statement comes into play

    else:
        frameNumber.set(0) #Set to 0 if there are none

Then there are no frames which have been taken. We should set the frame number to 0.

Now we make use of our great updateGUI() function which refreshes the screen to reflect the frame number we are currently on.

    updateGUI()
    
Finally we return None.
    
    return None

The next function helps us navigate to the last frame we have taken.

def lastFrame():
    frameNumber.set(countFrames())
    updateGUI()
    return None

Firstly we define the function name.

def lastFrame():

Now we can set the frameNumber to the value returned from the function countFrames(). This should return the number of the last frame.

    frameNumber.set(countFrames())

We just need to update our GUI to reflect this change.

    updateGUI()
    return None

Finally we return None.

The next function is the next frame function. We need to think about this one a little more. If we are at the last frame and try to move along, our program will likely crash. Therefore we should only allow our program to move to the next frame if the frame we are currently on is less than the total number of frames.


def nextFrame():
    if frameNumber.get() < (countFrames()):
        frameNumber.set(frameNumber.get()+1)
    updateGUI()
    return None

So after defining the function name.

def nextFrame():

We can check to see if the current frame is not more than the total frames we have taken.

    if frameNumber.get() < (countFrames()):

If we have not gone over the total number of frames, then we can increase our current frame number by one. We do this by getting the current frame number, adding one to it and then setting this as our frameNumber. This is all done in a single line.

        frameNumber.set(frameNumber.get()+1)

Again we update the GUI and return None

    updateGUI()
    return None

The previous frame function is similar to the next frame function.

def prevFrame():
    if frameNumber.get() > 1:
        frameNumber.set(frameNumber.get()-1)
    updateGUI()
    return None

However this time we check to ensure our current frame is greater than 1. We should only allow the user to reduce the frame count if this is the case.

We do that check by using frameNumber.get()

    if frameNumber.get() > 1:

We then use the same method we used to increase our frame number by 1 by reducing it by one.

        frameNumber.set(frameNumber.get()-1)

Finally we again updateGUI() and then return None.

    updateGUI()
    return None

The next thing we need to do is create the function which allows us to take frames.

def takeFrame():
    frameNumber.set(countFrames()+1)   
    os.system("raspistill -o image%s.jpg"%(setZero()))
    updateGUI()
    return None

No matter which frame we are looking at we don't want to overwrite a frame. We want to save the frame we take as the number AFTER the total number of frames, which is a number not currently used.

Therefore the first thing we do is to set the variable frameNumber to one after the total number of frames.

    frameNumber.set(countFrames()+1) 

We take the image in a very similar way we did in the timelapse program we created.

    os.system("raspistill -o image%s.jpg"%(setZero()))

The only difference is we use the setZero function to convert the frame number to text to be inserted in the file name.

Finally we update the GUI

    updateGUI()
and return None

    return None

The next function which we need to write is to delete frames. We could program it to delete any frame but there are pitfalls to this, as we would have to replace these deleted frames, either taking a new frame or by reducing the number on all the remaining frames after the one we have deleted. This could get complicated. An easier option would be to allow us to delete the last frame. For the purpose of this project that's what we shall do.

##Deletes the last stored frame
def deleteFrame():    
    frameNumber.set(countFrames())
    if countFrames() > 0:
        os.remove('image%s.jpg'%setZero())
        frameNumber.set(countFrames())
        updateGUI()
    return None

First we will define the function name.

def deleteFrame():   

We will set the frame to the last frame, by calling the countFrames function.

    frameNumber.set(countFrames())

Next we check there are some frames. We don't want to try to delete any frames which are not there.

    if countFrames() > 0:

We will use the os.remove function to remove the frame we are currently on, which is of course the last frame, we have taken.

        os.remove('image%s.jpg'%setZero())

As we were looking at the last frame, which we just deleted, we should recount the frames and set the current frame to the last frame. This will avoid the program trying to update to a frame which no longer exists! Always likely to cause the software to crash!

        frameNumber.set(countFrames())

Finally update the GUI then return None

        updateGUI()
    return None

The final function we have to write is the function to create the film we have been taking.

## Creates the video file.
def createFilm():
    setfpsIn = fpsIn.get()
    setfpsOut = fpsOut.get()
    os.system("avconv -r %s -i image%s.jpg -r %s -vcodec libx264 -crf 20 -g 15 -vf crop=2592:1458,scale=1280:720 timelapse.mp4"%(setfpsIn,'%7d',setfpsOut))
    return None

The first two lines ensures we get the values from our variables for fpsIn and fpsOut.

    setfpsIn = fpsIn.get()
    setfpsOut = fpsOut.get()

Now we create the video using the avconv tool.

    os.system("avconv -r %s -i image%s.jpg -r %s -vcodec libx264 -crf 20 -g 15 -vf crop=2592:1458,scale=1280:720 timelapse.mp4"%(setfpsIn,'%7d',setfpsOut))

There is a little more about this on my timelapse blog. This takes all the images you have created and turns them into a video.

Finally we return None

    return None

And that should finalise the program.

All that is required is to now run the program by pressing F5. As a reminder remember to have the blankScreen.gif image in the same location as your program, as this is where your program will look for it.

blankScreen.gif

If you would like to download the source code, you can do so by clicking on the link below.

Animation Studio Source Code

When you run the program you should see a user interface like this.



I hope you have enjoyed this blog post, I certainly had fun writing it! I would very much like to see some of your animation movies you create with this!