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)
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)
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)
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!
Had a wonderful time while reading your article. Thanks admin.
ReplyDeleteGerman Classes in Chennai
CCNA Training in Chennai
Python Training in Chennai
Best Python Training in Chennai
Python Training
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download Now
Delete>>>>> Download Full
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download LINK
>>>>> Download Now
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download Full
>>>>> Download LINK 55
This is the exact information I am been searching for, Thanks for sharing the required infos with the clear update and required points. To appreciate this I like to share some useful information regarding Microsoft Azure which is latest and newest,
ReplyDeleteRegards,
Ramya
Azure Training in Chennai
Azure Training Center in Chennai
Best Azure Training in Chennai
Azure Devops Training in Chenna
Azure Training Institute in Chennai
Azure Training in Chennai OMR
Azure Training in Chennai Velachery
Azure Online Training
Azure Training in Chennai Credo Systemz
DevOps Training in Chennai Credo Systemz
Best Cloud Computing Service Providers
This comment has been removed by the author.
ReplyDeleteThis is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to article very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
DedicatedHosting4u.com
ReplyDeleteNice infromation
Selenium Training In Chennai
Selenium course in chennai
Selenium Training
Selenium Training institute In Chennai
Best Selenium Training in chennai
Selenium Training In Chennai
ReplyDeleteRpa Training in Chennai
Rpa Course in Chennai
Rpa training institute in Chennai
Best Rpa Course in Chennai
uipath Training in Chennai
Blue prism training in Chennai
Data Science Training In Chennai
Data Science Course In Chennai
Data Science Training institute In Chennai
Best Data Science Training In Chennai
You need to take part in a contest for one of the greatest blogs on the internet. I will recommend this blog!
ReplyDeleteCLock here for information.
Out of these highly used apps, grocery apps are installed in the higher number by users nowadays. Double up your revenue by hiring dedicated grocery delivery app developer. putlocker.pictures
ReplyDeleteYou deserve thank you for maintaining your professional approach towards informative content. Awaiting to see more of your work. Web Designing Course Training in Chennai | Web Designing Course Training in annanagar | Web Designing Course Training in omr | Web Designing Course Training in porur | Web Designing Course Training in tambaram | Web Designing Course Training in velachery
ReplyDelete
ReplyDeleteAwesome blog. I enjoyed reading your articles. This is truly a great read for me. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work!
Best Data Science Courses in Bangalore
I’m excited to uncover this page. I need to to thank you for ones time for this particularly fantastic read !! I definitely really liked every part of it and i also have you saved to fav to look at new information in your site.
ReplyDeleteData Science Training in Bangalore
I am reading your post from the beginning, it was so interesting to read & I feel thanks to you for posting such a good blog,i want to share some Information about splunk tool tutorial . keep updates regularly..
ReplyDeleteRobotic Process Automation (RPA) Training in Chennai | Robotic Process Automation (RPA) Training in anna nagar | Robotic Process Automation (RPA) Training in omr | Robotic Process Automation (RPA) Training in porur | Robotic Process Automation (RPA) Training in tambaram | Robotic Process Automation (RPA) Training in velachery
Donker Media, also known as Donker Studios, is one of the leading animation studios in the world. This studio is based in Dubai and is one of the leading animation studios in the world. If you want to get more interesting details about animation studio visit this site right here.
ReplyDeleteThis blog was really good formatting, thanks for sharing with us. Visit Ogen Infosystem for professional website designing and digital marketing services at good price.
ReplyDeleteDigital Marketing Company in Delhi
This comment has been removed by the author.
ReplyDelete
ReplyDeleteReally nice and interesting post. I was looking for this kind of information and enjoyed reading this one. Keep posting. Thanks for sharing.
tally training in chennai
hadoop training in chennai
sap training in chennai
oracle training in chennai
angular js training in chennai
ReplyDeleteInteresting, Thanks for Sharing this useful information...
Data science training in chennai
Data science course in chennai
ReplyDeleteI feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.
Best Data Science courses in Hyderabad
Wow! Such an amazing and helpful post this is. I really really love it. It's so good and so awesome. I am just amazed. I hope that you continue to do your work like this in the future also.
ReplyDeletebest data science institute in hyderabad
We Give out personal loans for debt consolidation, bad credit loans, unsecured loans, loans for bad credit and instant secured loans with cheap rates Do you have a firm or company that need loan to startup a business or need, personal loan, Debt consolidation? For more information. We will provide you with loan to meet your needs. For more information contact Note: And many More:and many more at 2%interest rate; (Whats App) number: +919394133968 patialalegitimate515@gmail.com Mr Jeffery
ReplyDeleteJoin now for the intense Python Training in Hyderabad program at AI Patasala to become an early leader in this trending platform.
ReplyDeletePython Course in Hyderabad with Placements
Hello, I am one of the most impressed people in your article. sòng bạc I'm very curious about how you write such a good article. Are you an expert on this subject? I think so. Thank you again for allowing me to read these posts, and have a nice day today. Thank you.
ReplyDeleteMany thanks for the article, I have a lot of spray lining knowledge but always learn something new. Keep up the good work and thank you again. 온라인슬롯
ReplyDeleteGreat post, I have read the article. It is so good to read. It gives useful info.
ReplyDeletePsg grant Singapore
Peppol Invoicenow Singapore
Sage UBS accounting singapore
All things considered I read it yesterday yet I had a few musings about it and today I needed to peruse it again in light of the fact that it is very elegantly composed.
ReplyDelete360DigiTMG, the top-rated organisation among the most prestigious industries around the world, is an educational destination for those looking to pursue their dreams around the globe. The company is changing careers of many people through constant improvement, 360DigiTMG provides an outstanding learning experience and distinguishes itself from the pack. 360DigiTMG is a prominent global presence by offering world-class training. Its main office is in India and subsidiaries across Malaysia, USA, East Asia, Australia, Uk, Netherlands, and the Middle East.
ReplyDelete
ReplyDeleteExtremely overall quite fascinating post. I was searching for this sort of data and delighted in perusing this one. Continue posting. A debt of gratitude is in order for sharing.data science course in rohtak
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download Now
ReplyDelete>>>>> Download Full
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download LINK
>>>>> Download Now
Trevor Appleton: Creating An Animation Studio With A Raspberry Pi And Python >>>>> Download Full
>>>>> Download LINK
This is nice and informative, containing all information also has a great impact on the new technology. Thanks for sharing it,
ReplyDeletefull stack developer course
The increase in big data has led to a boom in the field of Data Science spiking ample career opportunities. Enroll in the Data Science training in Bangalore and invest in emerging skills and transform any business by wrangling, analyzing, and visualizing data. Give your career a makeover and gain in-depth knowledge on how to extract valuable insights from complex and large sets of data. Get to work on a live project which is designed to give hands-on experience to you along with career guidance and mentorship. data science course in chennai with placement
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteExtremely extraordinary elements you composed right here..fantastic stuff...I think you've made some in actuality entrancing points.keep occurring the five star do its stuff-battle. Bandicam Key Generator
ReplyDeleteI appreciate something you have acquainted with my skill base.Admiring the arise as outdated and exertion you put into your weblog and nitty gritty reference you pay for.thanks. https://cyberspc.com/farming-simulator-crack-download/
ReplyDeleteCongrats on the appearance of your delightful child. Inform us as to whether you want anything - we're here to help! The very best to your developing! Thums Up Babies Wishes Quotes
ReplyDeleteThe information you have posted is very useful. The sites you have referred was good. Thanks for sharing.
ReplyDeletefull stack web development course malaysia
the Article is very helpful
ReplyDeletepython fullstack online training in kukatpally kphb
This comment has been removed by the author.
ReplyDeleteAnkara
ReplyDeleteBolu
Sakarya
Mersin
Malatya
MPB
Nice Blog, thank you so much for sharing this blog.
ReplyDeleteJava full stack training in hyderabad
ağrı
ReplyDeletevan
elazığ
adıyaman
bingöl
V52CQX
görüntülüshow
ReplyDeleteücretli show
YE3W
antep evden eve nakliyat
ReplyDeletebolu evden eve nakliyat
afyon evden eve nakliyat
tekirdağ evden eve nakliyat
artvin evden eve nakliyat
V6SK
E2EDD
ReplyDeleteorder peptides
turinabol for sale
sarms
buy anapolon oxymetholone
sustanon
dianabol methandienone
order sustanon
order trenbolone enanthate
masteron
8461E
ReplyDeleteElazığ Lojistik
Artvin Şehirler Arası Nakliyat
Kütahya Şehir İçi Nakliyat
Çerkezköy Sineklik
Sinop Lojistik
Sinop Parça Eşya Taşıma
Bybit Güvenilir mi
Eskişehir Şehir İçi Nakliyat
Kripto Para Borsaları
D3BA1
ReplyDeleteKırıkkale Şehir İçi Nakliyat
Etimesgut Boya Ustası
Ünye Çatı Ustası
Kütahya Şehirler Arası Nakliyat
Trabzon Evden Eve Nakliyat
Bitranium Coin Hangi Borsada
Çerkezköy Oto Elektrik
Aksaray Şehir İçi Nakliyat
Kaspa Coin Hangi Borsada
8CF98
ReplyDeleteBinance Referans Kodu
Adana Evden Eve Nakliyat
Gümüşhane Evden Eve Nakliyat
order trenbolone enanthate
parabolan for sale
Uşak Evden Eve Nakliyat
boldenone
Tekirdağ Fayans Ustası
buy trenbolone enanthate
Nice blog! Good explanation about Python with coding, "I'm impressed with the quality of your posts." Keep posting more
ReplyDeletePython course training institute in KPHB
EFD54
ReplyDeleteChat Gpt Coin Hangi Borsada
Soundcloud Reposts Satın Al
Wabi Coin Hangi Borsada
Soundcloud Takipçi Satın Al
Twitch İzlenme Satın Al
Binance Borsası Güvenilir mi
Mexc Borsası Güvenilir mi
Nexa Coin Hangi Borsada
Osmo Coin Hangi Borsada
2EE0F13DF4
ReplyDeletetelegram sanal şov
Respect and I have a keen present: Who Repairs House Foundations whole house renovation
ReplyDelete