Python creating video from images using opencv - python

I was trying to create a video to show the dynamic variation of the data, like just continuously showing the images one by one quickly, so I used images (the images just called 1,2,3,4,.....) and wrote the following code:
import cv2
import numpy as np
img=[]
for i in range(0,5):
img.append(cv2.imread(str(i)+'.png'))
height,width,layers=img[1].shape
video=cv2.VideoWriter('video.avi',-1,1,(width,height))
for j in range(0,5):
video.write(img)
cv2.destroyAllWindows()
video.release()
and a error was raised:
TypeError: image is not a numpy array, neither a scalar
I think I used the list in a wrong way but I'm not sure. So where did I do wrong?

You are writing the whole array of frames. Try to save frame by frame instead:
...
for j in range(0,5):
video.write(img[j])
...
reference

You can read the frames and write them to video in a loop. Following is your code with a small modification to remove one for loop.
import cv2
import numpy as np
# choose codec according to format needed
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video = cv2.VideoWriter('video.avi', fourcc, 1, (width, height))
for j in range(0,5):
img = cv2.imread(str(i) + '.png')
video.write(img)
cv2.destroyAllWindows()
video.release()
Alternatively, you can use skvideo library to create video form sequence of images.
import numpy as np
import skvideo.io
out_video = np.empty([5, height, width, 3], dtype = np.uint8)
out_video = out_video.astype(np.uint8)
for i in range(5):
img = cv2.imread(str(i) + '.png')
out_video[i] = img
# Writes the the output image sequences in a video file
skvideo.io.vwrite("video.mp4", out_video)

You can use this pip package. It provides CLI commands to make video from images.
img_to_vid.py -f images_directory

Related

Create 7 hour video from +250.000 frames

I have 14 videos of 30 minutes (7 hours of videodata). I read in every video seperately, perform some morphological processing on each frame and then use cv2.imwrite() to save each processed frame. I'd like to make 1 big videofile of 7 hours of all processed frames. So far, I've been trying to use this code:
import numpy as np
import glob
img_array = []
for filename in glob.glob('C:/New folder/Images/*.jpg'):
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
img_array.append(img)
out = cv2.VideoWriter('project.avi',cv2.VideoWriter_fourcc(*'DIVX'), 15, size)
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
But an error is given when creating the img_array (memory overload). Is there any other way to make a 7 hour video from +250.000 frames?
Thank you.
check that all pictures are of the same size
as stated by others, don't read all pictures at once. it's not necessary.
Usually I'd prefer to create the VideoWriter before the loop but you need the size for that, and you only know that after you've read the first image. That's why I initialize that variable to None and create the VideoWriter once I have the first image
Also: DIVX and .avi may work but that's not the best option. the built-in option is to use MJPG (with .avi), which is always available in OpenCV. I would however recommend .mkv and avc1 (H.264) for general video, or you could look for a lossless codec that stores data in RGB instead of YUV (which may distort color information from screenshots... and also drawn lines and other hard edges). You could try the rle (note the space) codec, which is a lossless codec based on run-length encoding.
import cv2 # `import cv2 as cv` is preferred these days
import numpy as np
import glob
out = None # VideoWriter initialized after reading the first image
outsize = None
for filename in glob.glob('C:/New folder/Images/*.jpg'):
img = cv2.imread(filename)
assert img is not None, filename # file could not be read
(height, width, layers) = img.shape
thissize = (width, height)
if out is None: # this happens once at the beginning
outsize = thissize
out = cv2.VideoWriter('project.avi', cv2.VideoWriter_fourcc(*'DIVX'), 15, outsize)
assert out.isOpened()
else: # error checking for every following image
assert thissize == outsize, (outsize, thissize, filename)
out.write(img)
# finalize the video file (write headers/footers)
out.release()
You could also do this with an invocation of ffmpeg on the command line (or from your program):
How to create a video from images with FFmpeg?
You don't need to store each frame inside an array.
You can read the frame and write it to the video directly.
You can modify your code as:
import numpy as np
import glob
out = None
for filename in glob.glob('C:/New folder/Images/*.jpg'):
img = cv2.imread(filename)
if not out:
height, width, layers = img.shape
size = (width,height)
out = cv2.VideoWriter('project.avi',cv2.VideoWriter_fourcc(*'DIVX'), 15, size)
out.write(img)
out.release()

I've been trying to create a video from numpy arrays using OpenCv. It gives a "corrupted" file most of the time

For some reason some resolutions work and others don't. Many codec-container combinations work but some don't. I know not all containers work with all codecs but it seems weird.
The code below is a version with the same behavior.
import cv2
import numpy
import os
os.chdir(os.path.expanduser("~/Desktop"))
shape = (640, 480)
frame = numpy.zeros(tuple([*shape] + [3]), numpy.uint8)
video_name = 'output.avi'
fps = 60
video = cv2.VideoWriter(video_name, cv2.VideoWriter_fourcc(*'XVID'), fps, shape)
for i in range(fps * 5):
video.write(frame)
video.release()
Opencv works (height, width) while numpy (width, height) when defining the shape.
so i need to reverse the tuple before using it to define the VideoWriter shape

Want to append colored images to a list and convert that list to grayscale using OpenCV

So basically I'm trying to convert a set of RGB images to grayscale using cv2.cvtColor and python is throwing the following error:
Traceback (most recent call last):
File "MCG.py", line 53, in
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
TypeError: src is not a numpy array, neither a scalar.
This here is the code:
import numpy as np
import cv2
import dlib
import sys
import skimage
from PIL import Image
import os
import glob
folderpath = sys.argv[1]
cascPath = sys.argv[2]
imageformat = ".tif"
path = folderpath
imfilelist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith(imageformat)]
data = []
for IMG in imfilelist:
print IMG
image = cv2.imread(IMG)
data.append(image)
cv2.imshow('Image', image)
cv2.waitKey(0)
faceCascade = cv2.CascadeClassifier(cascPath)
predictor = dlib.shape_predictor(PREDICTOR_PATH)
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.05,
minNeighbors=5,
minSize=(100,100)
)
As you can see, I'm trying to append all these images to a list, which will then be converted using the cv2.cvtColor function. However, that error is thrown. What am I doing wrong? Thank you.
P.S if anyone is wondering why I imported modules that don't seem to be used in this code, this code is just a segment of the whole thing and all of those modules have are being utilized in one way or the other.
If you read the cv2.cvtColor documentation, you can see that the first parameter is the Src 8-bit single channel image. However, in your case you are giving an entire list of images.
So change the code as
gray = []
for j in range(0,len(data)):
gray.append(cv2.cvtColor(np.array(data[j]), cv2.COLOR_BGR2GRAY))
I guess this should work.
You are collecting the images into a list with
data = []
for IMG in imfilelist:
...
data.append(image)
....
and then trying to convert the list with
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
This is why you are getting the error - the error is telling you that data is not an image (numpy array) but is a list. You need to convert one image at a time with cv2.cvtColor().
You could try
gray = []
for img in data:
gray.append(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
This would give you a list of greyscaled images, which is what I think you want to do.

How to make a movie out of images in python

I currently try to make a movie out of images, but i could not find anything helpful .
Here is my code so far:
import time
from PIL import ImageGrab
x =0
while True:
try:
x+= 1
ImageGrab().grab().save('img{}.png'.format(str(x))
except:
movie = #Idontknow
for _ in range(x):
movie.save("img{}.png".format(str(_)))
movie.save()
You could consider using an external tool like ffmpeg to merge the images into a movie (see answer here) or you could try to use OpenCv to combine the images into a movie like the example here.
I'm attaching below a code snipped I used to combine all png files from a folder called "images" into a video.
import cv2
import os
image_folder = 'images'
video_name = 'video.avi'
images = [img for img in os.listdir(image_folder) if img.endswith(".png")]
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape
video = cv2.VideoWriter(video_name, 0, 1, (width,height))
for image in images:
video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
video.release()
It seems that the most commented section of this answer is the use of VideoWriter. You can look up it's documentation in the link of this answer (static) or you can do a bit of digging of your own. The first parameter is the filename, followed by an integer (fourcc in the documentation, the codec used), the FPS count and a tuple of the dimensions of the frame. If you really like digging in that can of worms, here's the fourcc video codecs list.
Thanks , but i found an alternative solution using ffmpeg:
def save():
os.system("ffmpeg -r 1 -i img%01d.png -vcodec mpeg4 -y movie.mp4")
But thank you for your help :)
Here is a minimal example using moviepy. For me this was the easiest solution.
import os
import moviepy.video.io.ImageSequenceClip
image_folder='folder_with_images'
fps=1
image_files = [os.path.join(image_folder,img)
for img in os.listdir(image_folder)
if img.endswith(".png")]
clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile('my_video.mp4')
I use the ffmpeg-python binding. You can find more information here.
import ffmpeg
(
ffmpeg
.input('/path/to/jpegs/*.jpg', pattern_type='glob', framerate=25)
.output('movie.mp4')
.run()
)
When using moviepy's ImageSequenceClip it is important that the images are in an ordered sequence.
While the documentation states that the frames can be ordered alphanumerically under the hood, I found this not to be the case.
So, if you are having problems, make sure to manually order the frames first.
#Wei Shan Lee (and others): Sure, my whole code looks like this
import os
import moviepy.video.io.ImageSequenceClip
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
image_files = []
for img_number in range(1,20):
image_files.append(path_to_images + 'image_folder/image_' + str(img_number) + '.png')
fps = 30
clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile(path_to_videos + 'my_new_video.mp4')
I've created a function to do this. Similar to the first answer (using opencv) but wanted to add that for me, ".mp4" format did not work. That's why I use the raise within the function.
import cv2
import typing
def write_video(video_path_out:str,
frames_sequence:typing.Tuple[np.ndarray,...]):
if ".mp4" in video_path_out: raise ValueError("[ERROR] This method does not support .mp4; try .avi instead")
height, width, _ = frames_sequence[0].shape
# 0 means no preprocesing
# 1 means each image will be played with 1 sec delay (1fps)
out = cv2.VideoWriter(video_path_out,0, 1,(width,height))
for frame in frames_sequence:
out.write(frame)
out.release()
# you can use as much images as you need, I just use 3 for this example
# put your img1_path,img2_path, img3_path
img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)
img3 = cv2.imread(img3_path)
# img1 can be cv2.imread out; which is a np.ndarray; you can also se PIL
# if you'd like to.
frames_sequence = [img1,img2,img3]
write_video(video_path_out = "mypath_outvideo.avi",
frames_sequence = frames_sequence
)
Hope it's useful!
Little hacky but avoids creating the file and just lets you watch it in real time.
import glob
from PIL import Image
import cv2
import numpy as np
import time
####### PARAMS
imgs_path = "/Users/user/Desktop/lidar_rig/ouster_data_wide_angle_cam_v9/imgs/*"
cur_img_index = 0
ds_st_index = 0
ds_en_index = -1
fps = 35 # tweak this
###### PARAMS
def cnvt_pil_to_cv2(pil_img):
open_cv_image = np.array(pil_img)
# Convert RGB to BGR
open_cv_image = open_cv_image[:, :, ::-1].copy()
return open_cv_image
img_files = sorted(glob.glob(imgs_path), key = lambda x: int(x.split('/')[-1].split('.')[0]))[ds_st_index:ds_en_index][cur_img_index:]
cnt = 0
for img_pth in img_files:
if not cnt %50: ## DDD -- avoid mem overflow
cv2.destroyAllWindows()
img = Image.open(img_pth).resize((750,750))
cv2.imshow(img_pth.split("/")[-1], cnvt_pil_to_cv2(img))
time.sleep(float(1.0/float(fps)))
cnt+=1

Python JPEG to movie

I am looking for a way to concatenate a directory of images files (e.g., JPEGs) to a movie file (MOV, MP4, AVI) with Python. Ideally, this would also allow me to take multiple JPEGs from that directory and "paste" them into a grid which is one frame of a movie file. Which modules could achieve this?
You could use the Python interface of OpenCV, in particular a VideoWriter could probably do the job. From what I understand of the doc, the following would do what you want:
w = cvCreateVideoWriter(filename, -1, <your framerate>,
<your frame size>, is_color=1)
and, in a loop, for each file:
cvWriteFrame(w, frame)
Note that I have not tried this code, but I think that I got the idea right. Please tell me if it works.
here's a cut-down version of a script I have that took frames from one video and them modified them(that code taken out), and written to another video. maybe it'll help.
import cv2
fourcc = cv2.cv.CV_FOURCC(*'XVID')
out = cv2.VideoWriter('out_video.avi', fourcc, 24, (704, 240))
c = cv2.VideoCapture('in_video.avi')
while(1):
_, f = c.read()
if f is None:
break
f2 = f.copy() #make copy of the frame
#do a bunch of stuff (missing)
out.write(f2) #write frame to the output video
out.release()
cv2.destroyAllWindows()
c.release()
If you have a bunch of images, load them in a loop and just write one image after another to your vid.
I finally got into a working version of the project that got me into this question.
Now I want to contribute with the knowledge I got.
Here is my solution for getting all pictures in current directory and converting into a video having then centralized in a black background, so this solution works for different size images.
import glob
import cv2
import numpy as np
DESIRED_SIZE = (800, 600)
SLIDE_TIME = 5 # Seconds each image
FPS = 24
fourcc = cv2.VideoWriter.fourcc(*'X264')
writer = cv2.VideoWriter('output.avi', fourcc, FPS, DESIRED_SIZE)
for file_name in glob.iglob('*.jpg'):
img = cv2.imread(file_name)
# Resize image to fit into DESIRED_SIZE
height, width, _ = img.shape
proportion = min(DESIRED_SIZE[0]/width, DESIRED_SIZE[1]/height)
new_size = (int(width*proportion), int(height*proportion))
img = cv2.resize(img, new_size)
# Centralize image in a black frame with DESIRED_SIZE
target_size_img = np.zeros((DESIRED_SIZE[1], DESIRED_SIZE[0], 3), dtype='uint8')
width_offset = (DESIRED_SIZE[0] - new_size[0]) // 2
height_offset = (DESIRED_SIZE[1] - new_size[1]) // 2
target_size_img[height_offset:height_offset+new_size[1],
width_offset:width_offset+new_size[0]] = img
for _ in range(SLIDE_TIME * FPS):
writer.write(target_size_img)
writer.release()
Is it actually important to you that the solution should use python and produce a movie file? Or are these just your expectations of what a solution would look like?
If you just want to be able to play back a bunch of jpeg files as a movie, you can do it without using python or cluttering up your computer with .avi/.mov/mp4 files by going to vidmyfigs.com and using your mouse to select image files from your hard drive. The "movie" plays back in your Web browser.

Categories