Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to create a video from a sequence of images without writing them on memory #159

Closed
papar22 opened this issue Apr 20, 2015 · 9 comments
Closed

Comments

@papar22
Copy link

papar22 commented Apr 20, 2015

Hi together,

I looked for my question in the net, but unfortunately, I did not find any answer for that. My purpose is to read a video via moviepy/opencv libraries and then extract the frames and do some tasks on them and concatenate them again to create a new video. To this end, I wanna use moviepy "write_videofile" method due to promising codecs and audio provider. As far as I understood, for creating the video from a sequence of images, I have to provide a list of them in "ImageSequenceClip(images_list, fps=25)". But I wanna avoid to write the images on the hard disk one by one and read them from a folder. Do you have any recommendation how to use a loop to read the frames of a video one by one, do something and concatenate them together to form a new video. any recommendation would be appreciated.
Thank you
This is a simple example which I have in mind.

import numpy as np
from moviepy.editor import *
from moviepy.Clip import *
from moviepy.video.VideoClip import *
import cv2

targetVideo = "pathToMyVideoFile"
cap = cv2.VideoCapture(targetVideo)

while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:

    # Do somethimg here (For example flipping)
    frame = cv2.flip(frame,0)        
    # ??????? How to concatenetae the frames to craete the video!
    video = ImageSequenceClip([frame1, ...], fps=30)

video.write_videofile('test.avi', fps=30, codec='mpeg4')
cap.release()
cv2.destroyAllWindows()
print "done"

@Zulko
Copy link
Owner

Zulko commented Apr 20, 2015

Have a look at this section of the docs.

Here is how you flip a video with moviepy

from moviepy.editor import VideoFileClip
def flip(image):
    """Flips an image vertically """
    return image[::-1] # remember that image is a numpy array

clip = VideoFileClip("my_original_video.mp4")
new_clip = clip.fl_image( flip )
new_clip.write_videofile("my_new_clip", some other parameters)

note that there is also a predefined FX function mirror_y in moviepy so you can simply do:

from moviepy.editor import *
clip = VideoFileClip("my_original_video.mp4")
new_clip = clip.fx(vfx.mirror_y)
new_clip.write_videofile("my_new_clip", some other parameters)

@papar22
Copy link
Author

papar22 commented Apr 21, 2015

Thanks Zulko for your answer. But flipping the video was just an example. My main problem is related to how create a new video and write it from several images or numpy arrays. The point is that, in my case, I have to analysis each frame separately, do some thing and then concatenate them together. Please let me know whether it is possible to create a new video from several numpy arrays in moviePy?

@Zulko
Copy link
Owner

Zulko commented Apr 21, 2015

If you replace the "flip" function by any other function you can do any transformation. For instance, this will apply the effect "invert the green and blue components" to each frame of the clip, and write the result to a new video file:

def invert_green_blue(image):
    return image[:,:,[0,2,1]]
modified_clip = my_clip.fl_image( invert_green_blue )
modified_clip.write_videofile("new_video.mp4")

I really you want to make a list of (transformed) numpy arrays first, here is how you could do:

from moviepy.editor import *
clip  = VideoFileClip("original_file.mp4")
new_frames = [ some_effect(frame) for frame in clip.iter_frames()]
new_clip = ImageSequenceClip(new_frames, fps=clip.fps)
new_clip.write_videofile("new_file.mp4") 

@papar22
Copy link
Author

papar22 commented Apr 24, 2015

Many Thanks Zulko. I got your general point, tried and it works perfectly. But let me ask another question. Let me know in the function "ImageSequenceClip", there is a solid need to have all frames in one numpy array/list once. For example, if I have, 20,000 frames, I have to put all in one list/array? In this case, memory allocation error raises up. As far as I know, in FFMPEG, there is the opportunity of stream writing. I really appreciate it if you let me know in MoviePy is there any stream option?

Thanks a lot

@Zulko
Copy link
Owner

Zulko commented Apr 24, 2015

Again, I think ImageSequenceClip is NOT what you want to use, because it forces you to have all the frames at once.

All the other examples I gave you do that "streaming": you only have one frame at once in the RAM. Also have a look here for more examples (in particular the very last one).

@papar22
Copy link
Author

papar22 commented Apr 24, 2015

Exactly. Thanks for the time you spent. It is awesome. :D If I want to pass some input argument to my function, How do I have to do that? For instance, in your example,

from moviepy.editor import VideoFileClip
def flip(image , num):
"""Flips an image vertically """
return image[::num]

clip = VideoFileClip("my_original_video.mp4")
new_clip = clip.fl_image( flip ) # How to call the function with some input parameters
new_clip.write_videofile("my_new_clip", some other parameters)

@papar22
Copy link
Author

papar22 commented Apr 29, 2015

@Zulko : I really appreciate it, if you let me know whether it is possible to call a function with more than one input parameters in fl_image() or not.

@Zulko
Copy link
Owner

Zulko commented Apr 29, 2015

For the moment you can't, but there are many other ways to do the same thing. One solution is to use fx instead of fl, because fx accepts additional parameters.

def image_transformation(image, par1, par2, par3):
    """ image transformation with three additional parameters """
    # ...
    return some_new_image

def my_transformation(clip, par1, par2, par3):
    def new_tranformation(image):
        return image_transformation(image, par1, par2, par3)
    return clip.fl_image(new_transformation)

# Then you can tranform any clip with parameters:
new_clip1 = some_clip.fx (my_transformation, some_par1, some_par2, some_par3)
new_clip2 = some_clip.fx (my_transformation, other_par1, other_par2, other_par3)

See the documentation link I give in my first answer for more details.

@keikoro
Copy link
Collaborator

keikoro commented Feb 17, 2017

I think the replies in this issue answered the original question. For a potential feature request, I'd suggest opening a new issue.

@keikoro keikoro closed this as completed Feb 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants