MP4 to GIF Converter

Help with space by converting MP4 into time elapse

Origin

The idea came from a very weird situation. My friend was at the issue of getting in a small crash and he wanted to see if it was possible to get his dash camera feed. Personally, I didn't actually end up using the program for that but that is beside the point. I used test data of phone footage as it was the only thing that I could find that would be worth the time elapsed at the time.

Setting up

First of all, I created this to be command-line based with everything in the same folder to limit the amount of confusion that will be going on. I would love to make GUI but for a small project like this is it not worth it. So to start out you will need to grab the FFmpeg file as that is needed to read most video formats. Just go to this website and download it putting it in the same folder as the project.

The final part of the setup is having python installed. I used python because of my familiarity with the image processing class and written programs with it in the past.

This project was a beast and took a day and a half to write because of processing time and the number of issues I ran into. I will try and keep it brief but I had 8 versions if that tells you how many issues I had.

Step 1: The Overview

So I know the process overview of what the code was going to look like. At first, it was going to be efficient and take a lot of writing but I wanted to guarantee the highest possibility of testing without wasting a lot of resources. Another huge factor was I couldn't find how to do input and output at the same time for video files. I would take apart the video by specific frame times and then put them together into a gif. From there I could turn it back into an mp4 if wanted. Sounds so simple but in so many places it went wrong.

Step 2: The Starting Code

The process of writing the first version of the code. It has a lot of mistakes and the thought process was a little long because after my research not much was out there for a project like this. I might not have done good enough research but I guess it was ok with me so far.

Turning mp4 into photos

This is the easiest version. The issue is the GIF was massive

images = []
filename = 'filename.mp4'
with imageio.get_writer('/path/to/movie.gif', mode='I') as writer:
    for filename in images:
        image = imageio.imread(filename)
        writer.append_data(image)

Turning Gif into mp4

Was hoping I could turn it back in hopes to be smaller but it was just as massive.

startGif = 'input.gif'
outputFile = 'm.mp4'
ff = ffmpy.FFmpeg(inputs={startGif:None},outputs={outputFile:None})
ff.run()

Creating Gif from photos

I got the photos and was hoping this would work. The timing was off and the gif was so slow.

duration = .1
images = []
for i in range(fileNumb):
    filename = "data6/frame" + str(i) + ".jpg"
    images.append(imageio.imread(filename));
output_file = 'Gif-%s.gif' % datetime.datetime.now().strftime('%Y-%M-%d-%H-%M-%S')
imageio.mimsave(output_file, images, duration=duration)

vidcap.release()
cv2.destroyAllWindows()

Changing the framerate

I was hopeful that if I changed the framerate the issues would go away. It caused even more issues

count = 0
framerate = vidcap.get(5)
print("framerate:", framerate)
framecount = vidcap.get(7)
print("framecount:", framecount)
vidcap.set(5,1)
newframerate = vidcap.get(5)
print("newframerate:", newframerate)
print("NUMBER OF FRAMES: ",vidcap.get(CAP_PROP_FRAME_COUNT))
framenumb = framecount * framerate

Additional Info

Also, I used Tinify to make smaller images because the size was an issue, well that backfired a lot. It created horrible images and made me even doubt a gif would work at that point because looking up more it was limited in colors. It was based on the fact it would be better but had to rethink that when this came up.

Overall

Overall view of the first attempt, the code would need a lot of tweaking but I was onto something. It was coming together just inefficiently.

Step 3: Modifications

So at this point, I had a lot of parts of code, but nothing was really solid. I cleaned up the code and got a great main class.

Initial Main Method

The main method I strung together after my first attempt

def main():
    ovfn = input("Enter in the file path:  ") # Original Video Filename
    vidcap = cv2.VideoCapture(ovfn);
    if (not vidcap.read()): # Is it able to read the file
        exit()
    # Inputting speed
    print("I need to collect some data about our new file")
    print("If you need any help just enter define and I will print some help")
    initd = input("Do you want more than one frame per second ('Yes' or 'No'): ")
    duration = 0
    if ("y" in initd): # If you want long videos
        print("Ok so that means its going to be long.")
        duration = input("You want a frame every how many seconds? ")
    else: # If you want Speed up videos
        print("This is going to be a compression, Fun. I love these")
        duration = input("How many frames do you want in a second? ")
        duration = 1 / duration
    duration = float(duration)
    print("Now we must consider the power of the software and Each Videos Properties")
    ''' Printing the properties of the video file '''
    framerate = vidcap.get(5)
    framecount = vidcap.get(7)
    print("Frame Rate: ", framerate)
    print("Frame Count: ", framecount)
    print("Frame Height: ", vidcap.get(CAP_PROP_FRAME_HEIGHT))
    print("Frame Width: ", vidcap.get(CAP_PROP_FRAME_WIDTH))
    print("Number of Frames: ", vidcap.get(CAP_PROP_FRAME_COUNT))
    print("Total Number of Frames: ", framecount * framerate)
    ''' Creating the output folder if it doesn't exist '''
    outFold = 'data6' # Outputting folder
    try:
        if not os.path.exists(outFold):
            os.makedirs(outFold)
    except OSError:
        print ('Error: Creating directory of data')
    
    
    
    speedRate = input("Input the number of frames you want")
    """ Need to add here  the speed rate checking to see if it is a number"""
    print(type(speedRate))
    speedRate = float(speedRate)
    ''' The Actual process of breaking up the MP4 '''
    con = 0 # Counter of the file
    fileNumb = 0 # The file Number
    while fileNumb < (con * secTime):
        if con > 200: # For the longer files cut off
            print("You trying to break this. I'm cutting you off")
            break
        success, image = vidcap.read()
        if success:
            cv2.imwrite(outFold+"/frame" + str(fileNumb) + ".jpg", image)  # save frame as JPEG file
        else: # At the end of the file
            break
        con+= 1/ speedRate # Adding time to next frame
        fileNumb += 1 # Next frame
    """ Creating the GIF with the obtained Files """
    images = []
    for i in range(fileNumb):
        filename = "data6/frame" + str(i) + ".jpg"
        images.append(imageio.imread(filename));
    output_file = 'Gif-%s.gif' % datetime.datetime.now().strftime('%Y-%M-%d-%H-%M-%S')
    imageio.mimsave(output_file, images, duration=duration)
    print("Time to clean up")
    print("Releasing all the lose ends")
    vidcap.release()
    print("Destroying all the evidence. MWahhhahha")
    cv2.destroyAllWindows()
    print("Welp, there we go. We are all done.")
    print("Continue with the rest of your day")

main()

Step 4: Cleaning Up

So I had a solid main class and all it took was modifications to this to get it how I wanted. I was going to do a better interface but after telling people about the program it was more of personal use than wide use. I modified it to work really well and the code was turning to be better

Second take of code

The code after my first day of starting the project

import os
import shutil
from cv2 import imwrite, CAP_PROP_FRAME_COUNT, CAP_PROP_POS_AVI_RATIO, \
    CAP_PROP_POS_MSEC, CAP_PROP_FRAME_HEIGHT, CAP_PROP_FRAME_WIDTH
import cv2
import ffmpy
import imageio
import sys

import moviepy.editor as mp
import numpy as np

def check(a):  # Checks to see if it is Yes or No
    return "y" in a or "Y" in a


def input_speed():
    print("I need to collect some data about our new file")
    print("If you need any help just enter define and I will print some help")
    initd = input("Do you want to compress The File ('Yes' or 'No') ")
#    initd = "Yes"
    duration = 0
    if not check(initd):  # If you want long videos
        print("Ok so that means its going to be long.")
        su = input("Are you sure about that??? ")
        if not check(su):
            print("Thank the lord. That is really not what this is used for")
            duration = input("How many frames do you want in a second? ")
        else:
            print("Fine be that way")
            duration = input("You want a frame every how many seconds? ")
    else:  # If you want Speed up videos
        print("This is going to be a compression, Fun. I love these")
        duration = input("How many frames do you want in a second? ")
#        duration = 4
    return int(duration)  # Return int for no problems


''' Add a preference input for this one '''


def output_exist(outFold):  # Make sure output folder exists
    try:
        if not os.path.exists(outFold):
            os.makedirs(outFold)
            print("It has been created. Your welcome")
        else:
            print("The output folder already existed. Your good to go")
    except OSError:
        print ('Error: Creating directory of data')
    return outFold


def main():
    secTime = 1000  # Number of MiliSeconds in Second
    videoFiles = []  # Files need to go through
    outFold = output_exist("data")  # The output folder
    print("WELCOME TO MY MP4 TO GIF TO MP4 PROGRAM")
    numbFold = input("Are you doing a folder ('Yes' or 'No'): ")
#    numbFold = "No"
    if check(numbFold):
        print("Yeah we get to have some fun")
        vidfol = input("Input the folder name: ")  # Folder of Videos
        print("PLOT TWIST. I Can't actually do that yet. Come back at another time and check again.")
        videoFiles.append(input("Enter in the file path of one file: "))
    else:
        print("What a shame not using to full potential. Ok so be it")
        videoFiles.append(input("Enter in the file path: "))
    for ovfn in videoFiles:
        ovfn = ovfn.strip("\"")  # Takes care of parentheses
        filename, file_extension = os.path.splitext(ovfn)
        print("The file name is: " + str(filename))
        print("With an extension of " + str(file_extension))
        if(os.path.isdir(ovfn)):  # Is a directory
            print("Well " + ovfn + " is a directory so I'll skip it")
            break
        elif not("mp4" in file_extension or "MP" in file_extension):
            print("That isn't a MP4 file silly. I'll let that slide though.")
            break
        else:  # is a file
            vidcap = cv2.VideoCapture(ovfn)
            if (not vidcap.read()[0]):  # Is it able to read the file
                print("Your a failure at life. That is not a readable file")
                print("Onward we go")
                vidcap.release()
            else:
                duration = 1 / input_speed()
                print("Now it is time for some properties")
                framerate = vidcap.get(5)
                framecount = vidcap.get(7)
                totFrame = framerate * framecount
                secLength = framecount / framerate
                print("Video Length (Seconds): ", secLength)
                print("Frame Rate: ", framerate)
                print("Frame Count: ", framecount)
                print("Frame Height: ", vidcap.get(CAP_PROP_FRAME_HEIGHT))
                print("Frame Width: ", vidcap.get(CAP_PROP_FRAME_WIDTH))
                print("Number of Frames: ", vidcap.get(CAP_PROP_FRAME_COUNT))
                print("Total Number of Frames: ", totFrame)
                print("Now I know videos can be long and only want 2 min of a 10 min video")
                capSec = secLength
                print("Here is your chance. The video is " + str(capSec) + " seconds Long")
                clipped = input("Do you want to make it shorter?\nEnter 'Yes' or 'No': ")
                if check(clipped):
                    print("Ok so shortening it. Good to hear")
                    newCapSec = float(input("How many Seconds long do you want it (up to " + str(capSec) + " seconds): "))
                    if(newCapSec > capSec):
                        print("That doesn't work. Your trying to break my program. I'll Let you have one more try")
                        newCapSec = input("How many Seconds long do you want it (up to " + str(capSec) + " seconds")
                        if(newCapSec > capSec):
                            print("Well that didn't work so I guess it will not be shortened")
                        else:
                            print("Good you came around and noticed your mistakes")
                            capSec = newCapSec
                    else:
                        print("Now just let me enter your new shortened length")
                        capSec = newCapSec    
                con = 1  # Seconds Counter of the file
                print("There are some benifits of this specific program.\nI can create it to be smoother")
                smo = input("Would you like it smoother ('Yes' or 'No'): ")
                smoothRate = 1  # Smoothness of the file
                if check(smo):
                    print("That is great. Number must be greater than one (I'm not checking)")
                    smoothRate = int(input("How much smoother? "))
                else:
                    print("You have a lot of faith in a second but have it your way.")
                fileNumb = 0  # The File Number
                while(con < capSec):
                    vidcap.set(cv2.CAP_PROP_POS_MSEC, (secTime * con))
                    success, image = vidcap.read()
                    if success:
                        cv2.imwrite(outFold + "/frame" + str(fileNumb) + ".jpg", image)  # Save frame as JPG
                    else:  # At the end of the file
                        break 
                    con += 1 / int(smoothRate)
                    fileNumb += 1
                    
                """ Creating the GIF with the obtained Files """
                images = []  # Files in the folder
                print("Putting together " + str(fileNumb) + " files")
                for i in range(fileNumb):
                    filenames = outFold + "/frame" + str(i) + ".jpg"
                    images.append(imageio.imread(filenames));
                output_file = filename + ".gif"
                ''' Checking to see that it is a valid File Number '''
                fileWorks = False
                fileCount = 0
                while not fileWorks:
                    try:
                        if(os.path.exists(output_file)):
                            output_file = filename + str(fileCount) + ".gif"
                            fileCount += 1
                        else:
                            fileWorks = True
                    except OSError:
                        print('Error when Creating the file name or something')
        
                imageio.mimsave(output_file, images, duration=duration)
                
                print("Now we have an option since the GIF is probably big")
                conback = input("Would you like to convert back to a MP4 File? ")
                if check(conback):  # Convert to MP4
                    print("Ok I can convert the GIF to MP4\nJust give me a second")
                    clip = mp.VideoFileClip(output_file)
                    clip.write_videofile(filename + "_Lapse.MP4")
                else:
                    print("OK. Was just trying to help out")
                print("Time to clean up")
                print("Releasing all the lose ends")
                vidcap.release()
                
                print("Destroying all the evidence. MWahhhahha")
                cv2.destroyAllWindows()
                
                print("WARNING: THIS IS GOING TO TAKE UP A LOT OF SPACE")
                cleanRemove = input("Do you want to delete the folder of all the images.")
                if check(cleanRemove):
                    print("Cleaning out the files")
                    shutil.rmtree(outFold)
                    print("They have been removed as requested")
                else:
                    print("It helps on space. Please reconsider")
                    cleanRemove = input("Do you want to delete the folder of all the images.")
                    if check(cleanRemove):
                        print("The hard drive thanks you\nCleaning out the files")
                        shutil.rmtree(outFold)
                        print("They have been removed as requested")
                    else:
                        print("That is a shame. I was trying to save you space.")
        
            print("Welp, there we go. We are all done.")
            print("Continue with the rest of your day")
        
    print("Finally we are done")


main()

"""
''' Other Ideas to add '''
Creating an input for output folder
Being able to do folders.
Simplifying for more modules even though that isn't that bad.
Make the software a little smaller
"""

Other ideas

I came up with a few ideas at this point Create an input for the output folder Being able to do multiple folders in 1 project Start program with already taken images Simplify the modules even though it isn't that bad Making the software smaller

Final Conclusion

The program seemed better in my head but when putting it all together it didn't really live up to my expectations. It did get it done and could add the compression algorithm I already had made a while ago. The really big issue with this project is putting it into gif I thought would make a bigger deal than it did on the size.

Final Code

Final code that is released

import os
import shutil
import cv2
import ffmpy
import imageio
import sys

import moviepy.editor as mp
#import numpy as np

def check(a):  # Checks to see if it is Yes or No
    return "y" in a or "Y" in a


def input_speed():
    print("I need to collect some data about our new file")
    print("If you need any help just enter define and I will print some help")
    initd = input("Do you want to compress The File ('Yes' or 'No') ")
#    initd = "Yes"
    duration = 0
    if not check(initd):  # If you want long videos
        print("Ok so that means its going to be long.")
        su = input("Are you sure about that??? ")
        if not check(su):
            print("Thank the lord. That is really not what this is used for")
            duration = input("How many frames do you want in a second? ")
        else:
            print("Fine be that way")
            duration = input("You want a frame every how many seconds? ")
    else:  # If you want Speed up videos
        print("This is going to be a compression, Fun. I love these")
        duration = input("How many frames do you want in a second? ")
#        duration = 4
    return int(duration)  # Return int for no problems


''' Add a preference input for this one '''


def output_exist(outFold):  # Make sure output folder exists
    try:
        if not os.path.exists(outFold):
            os.makedirs(outFold)
            print("It has been created. Your welcome")
        else:
            print("The output folder already existed. Your good to go")
    except OSError:
        print ('Error: Creating directory of data')
    return outFold


def main():
    secTime = 1000  # Number of MiliSeconds in Second
    videoFiles = []  # Files need to go through
    outFold = output_exist("data")  # The output folder
    print("WELCOME TO MY MP4 TO GIF TO MP4 PROGRAM")
    numbFold = input("Are you doing a folder ('Yes' or 'No'): ")
#    numbFold = "No"
    if check(numbFold):
        print("Yeah we get to have some fun")
        vidfol = input("Input the folder name: ")  # Folder of Videos
        print("PLOT TWIST. I Can't actually do that yet. Come back at another time and check again.")
#        videoFiles = os.listdir(vidfol)
        videoFiles.append(input("Enter in the file path of one file: "))
    else:
        print("What a shame not using to full potential. Ok so be it")
        videoFiles.append(input("Enter in the file path: "))
    for ovfn in videoFiles:
        ovfn = ovfn.strip("\"")  # Takes care of parentheses
        filename, file_extension = os.path.splitext(ovfn)
        print("The file name is: " + str(filename))
        print("With an extension of " + str(file_extension))
        if(os.path.isdir(ovfn)):  # Is a directory
            print("Well " + ovfn + " is a directory so I'll skip it")
            break
        elif not("mp4" in file_extension or "MP" in file_extension):
            print("That isn't a MP4 file silly. I'll let that slide though.")
            break
        else:  # is a file
            vidcap = cv2.VideoCapture(ovfn)
            if (not vidcap.read()[0]):  # Is it able to read the file
                print("Your a failure at life. That is not a readable file")
                print("Onward we go")
                vidcap.release()
            else:
                duration = 1 / input_speed()
                print("Now it is time for some properties")
                framerate = vidcap.get(5)
                framecount = vidcap.get(7)
                totFrame = framerate * framecount
                secLength = framecount / framerate
                print("Video Length (Seconds): ", secLength)
                print("Frame Rate: ", framerate)
                print("Frame Count: ", framecount)
                print("Frame Height: ", vidcap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                print("Frame Width: ", vidcap.get(cv2.CAP_PROP_FRAME_WIDTH))
                print("Number of Frames: ", vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
                print("Total Number of Frames: ", totFrame)
                print("Now I know videos can be long and only want 2 min of a 10 min video")
                capSec = secLength
                print("Here is your chance. The video is " + str(capSec) + " seconds Long")
                clipped = input("Do you want to make it shorter?\nEnter 'Yes' or 'No': ")
                if check(clipped):
                    print("Ok so shortening it. Good to hear")
                    newCapSec = float(input("How many Seconds long do you want it (up to " + str(capSec) + " seconds): "))
                    if(newCapSec > capSec):
                        print("That doesn't work. Your trying to break my program. I'll Let you have one more try")
                        newCapSec = input("How many Seconds long do you want it (up to " + str(capSec) + " seconds")
                        if(newCapSec > capSec):
                            print("Well that didn't work so I guess it will not be shortened")
                        else:
                            print("Good you came around and noticed your mistakes")
                            capSec = newCapSec
                    else:
                        print("Now just let me enter your new shortened length")
                        capSec = newCapSec    
                con = 1  # Seconds Counter of the file
                print("There are some benifits of this specific program.\nI can create it to be smoother")
                smo = input("Would you like it smoother ('Yes' or 'No'): ")
                smoothRate = 1  # Smoothness of the file
                if check(smo):
                    print("That is great. Number must be greater than one (I'm not checking)")
                    smoothRate = int(input("How much smoother? "))
                else:
                    print("You have a lot of faith in a second but have it your way.")
                fileNumb = 0  # The File Number
                while(con < capSec):
                    vidcap.set(cv2.CAP_PROP_POS_MSEC, (secTime * con))
                    success, image = vidcap.read()
                    if success:
                        cv2.cv2.imwrite(outFold + "/frame" + str(fileNumb) + ".jpg", image)  # Save frame as JPG
                    else:  # At the end of the file
                        break 
                    con += 1 / int(smoothRate)
                    fileNumb += 1
                    
                """ Creating the GIF with the obtained Files """
                images = []  # Files in the folder
                print("Putting together " + str(fileNumb) + " files")
                for i in range(fileNumb):
                    filenames = outFold + "/frame" + str(i) + ".jpg"
                    images.append(imageio.imread(filenames));
                output_file = filename + ".gif"
                ''' Checking to see that it is a valid File Number '''
                fileWorks = False
                fileCount = 0
                while not fileWorks:
                    try:
                        if(os.path.exists(output_file)):
                            output_file = filename + str(fileCount) + ".gif"
                            fileCount += 1
                        else:
                            fileWorks = True
                    except OSError:
                        print('Error when Creating the file name or something')
        
                imageio.mimsave(output_file, images, duration=duration)
                
                print("Now we have an option since the GIF is probably big")
                conback = input("Would you like to convert back to a MP4 File? ")
                if check(conback):  # Convert to MP4
                    print("Ok I can convert the GIF to MP4\nJust give me a second")
                    clip = mp.VideoFileClip(output_file)
                    clip.write_videofile(filename + "_Lapse.MP4")
                else:
                    print("OK. Was just trying to help out")
                print("Time to clean up")
                print("Releasing all the lose ends")
                vidcap.release()
                
                print("Destroying all the evidence. MWahhhahha")
                cv2.destroyAllWindows()
#                clip.end()
                print("WARNING: THIS IS GOING TO TAKE UP A LOT OF SPACE")
                cleanRemove = input("Do you want to delete the folder of all the images.")
                if check(cleanRemove):
                    print("Cleaning out the files")
                    shutil.rmtree(outFold)
                    print("They have been removed as requested")
                else:
                    print("It helps on space. Please reconsider")
                    cleanRemove = input("Do you want to delete the folder of all the images.")
                    if check(cleanRemove):
                        print("The hard drive thanks you\nCleaning out the files")
                        shutil.rmtree(outFold)
                        print("They have been removed as requested")
                    else:
                        print("That is a shame. I was trying to save you space.")
        
            print("Welp, there we go. We are all done.")
            print("Continue with the rest of your day")
        
    print("Finally we are done")


main()

Code exe

The exe file of the final code. It is in the git repository because I was unable to include all the classes and the size could not be less than 75 MB. It started out being 120 MB.

Last updated