Save Video with OpenCV Python

Save Video with OpenCV Python

OpenCV provides a lot of useful functions to work with images and video files. One such operation is saving video using OpenCV. However, saving video with OpenCV, although is a straightforward operation, but at times it becomes very time consuming to debug because of some silly mistakes. Any time there is any issue, the frames do not get added to video file, and the video thus generated does not work. For example if the extension of video file saved does not match the encoding that we are using, the video file does not get written. Similarly, if there is issue with fps or frame size, the video file does not get written to and hence does not work.

In this blog post we are going to review how to easily create video file and in the process, I will create utility class that will make the process of video saving straight forward. Before getting started, if you are not aware of how videos work, do checkout this blog link .

Download the code here saveVideo-1.zip

Lets get started with creating a class “writeVideo” that will help us save videos easily and avoid common issues faced with video with OpenCV.

#import needed packages
import cv2
import math

On line 4 to 6 we get started with importing the needed packages.

class writeVideo:

On line 18 we get started with declaring our writeVideo.py class. This class shall encapsulate the video writer class from OpenCV. This class implementation will help us avoid following issues that happen very commonly while saving video with OpenCV:
1. Mismatch in video frame size when video writer object was instantiated vis-à-vis what’s being actually saved
2. Mismatch in file extension with which the file is being saved and video encoding
Also, we shall add a feature to save the video between a given time frame.

	def __init__(self, filepath, capture, currentframe = None, fps = None ):
		
		# if current frame is passed, then we inialise the video capture size
		if currentframe is not None:
			self.frameHeight = currentframe.shape[1]
			self.frameWidth = currentframe.shape[0]
		else:
			self.frameHeight = -1
			self.frameWidth = -1
		# initialising the class level variables
		self.capture = capture
		self.filepath = filepath
		
		#get the file extension based on the path for saving video.
		ext = filepath.split('.')
		ext = ext[-1]

On line 28 we declare the constructor for the class. The constructor that takes following arguments
filepath – path where it will save the generated video file
capture – video capture class object from OpenCV
currentframe – current frame of the video that will need to be saved. This is an optional parameter, I will explain why in a little bit
fps – This is again an optional parameter. When recording video with webcam, at times OpenCV is not able to detect the fps automatically, then this parameter can be used or in case you want to alter the fps of generated video compared to original video, then this parameter can be used.

Now coming back to “Current Frame” parameter, this has been made optional, since most common error that happens when saving video is difference in frame size when creating object of “cv2.VideoWriter” class, versus when we are actually saving the frame. If the frame is not provided in constructor, the class does lazy initialization and creates the required object when actually saving the video.

Between line 31 and 36, we initialize the frame height and width based on if the information is passed in the constructor. Line 38 and 39 simply initialize the class variables with inputs.
On line 42 and 43, we are extracting the file extension from the file path passed for saving the video.

		self.formatStr = None
		if ext == "mp4":
			self.formatStr = "MP4V"
		elif ext == "avi":
			self.formatStr = "MJPG"
		
		self.fps = fps
		if self.fps is None:
			self.fps = capture.get(cv2.CAP_PROP_FPS)
		# if current frame of the video was passed then we initiate the Video writer
		self.writer = None
		if currentframe is not None:
			self.createWriter()

Now that we know the file extension on line 43, we use the same to use the correct encoding for the video file generated. As can be seen the class currently only supports encoding for avi and mp4 file extensions. Then on line 55 to 57 we initialize the fps from the input value or based on the video capture object passed in the parameters. And finally, if we have the correct frame size then we instantiate the video writer class, else we defer it for later.


	def createWriter(self):
		if self.formatStr is not None:
			fourcc = cv2.VideoWriter_fourcc(*self.formatStr)
			self.writer = cv2.VideoWriter(self.filepath, fourcc, self.fps, (self.frameHeight, self.frameWidth), True)

We are defining a simple function that instantiates an object of VideoWriter class of OpenCV

	def save(self, currentframe, starttime = 0, elapsedtime = -1):
		
		#lazy initialisation of Video writer class
		if self.writer is None:
			if currentframe is not None:
				self.frameHeight = currentframe.shape[1]
				self.frameWidth = currentframe.shape[0]
				self.createWriter()
			else:
				return False
		# if elapsed time is -1 we need to set it to full time duration of the video
		# minus the start time
		if elapsedtime == -1 :
			frame_count =self.capture.get(cv2.CAP_PROP_FRAME_COUNT)
			fps = self.capture.get(cv2.CAP_PROP_FPS)
			elapsedtime = frame_count/fps * 1000
			elapsedtime = elapsedtime - starttime
		
		#get the current time in the video processing
		currenttime = self.capture.get(cv2.CAP_PROP_POS_MSEC)
		#check if we are in the correct time frame then we start saving the video
		if currenttime > starttime and currenttime <= starttime+elapsedtime :
			if self.frameHeight ==  currentframe.shape[1] and self.frameWidth == currentframe.shape[0]:
				if self.writer is not None :
					self.writer.write(currentframe)
					return True
			#return false if the video is not being saved
			return False
		return True

On line 80 we are defining the “save” function. This is the function which allows us to save part video from a given start time to a given duration. Here is the list of parameters for the function:
currentframe: The current frame of video to be saved. If the video writer object was not initialized in the constructor, then it will be initialized here on the first call
starttime: Default value is 0. This indicates the start time for the video saving 0 means the video needs to be saved from beginning
elapsedtime: Default value is -1. This indicates the duration of video to be saved starting from starttime. -1 means full duration of video needs to be saved

Saving video with OpenCV is very error prone. This function handles the error if there is any difference in the frame size, since when there is difference in frame. In such a case this function returns False so that the error can be handled by the caller.

Between line 82 and 89, if the VideoWriter class was not already instantiated in the constructor, we create the same. Then between lines 92 and 96 we handle the logic for elapsed time. On line 99 we call the OpenCV function to get the current time of the video stream we are processing currently.

Then on line 101, we ensure that we only save the video if the timings match the input values. Line 102 checks if the size of frame is consistent with the size with which “VideoWriter” class was instantiated. If not it returns false where OpenCV would stop saving the frame to video and the process will fail.

	def release(self):
		if self.writer is not None :
			self.writer.release()

And finally, on lines 111 to 114 we release the object of VideoWriter class.

Now let’s write code to put this newly created class to use.

For this lets create  saveVideo.py file

#import the required packages including OpenCV
import cv2
from codedeepai import utilsVideo

We import the required packages. The 3rd line imports the class that we just created above.

vid = "./vid/cctv.mp4"
savePath = "./vid/temp.mp4"
capture = cv2.VideoCapture(vid)
# initialise write Video object. Notice since we do not have the video frame yet, we have not
#passed the same. Which means writer object will be created when we start saving.
vw = writeVideo(savePath, capture)

Then on line 5 and 6 we provide the path the original video to be processed and the path of the video file to be saved respectively. Then on line 7 we create an instance of video capture class from OpenCV. Line 10 is important where we instantiate the class we just defined above with minimum parameters.

while(True):
	# Capture frame-by-frame
	ret, currentframe = capture.read()

Then on line 12 we start an infinite loop to process the incoming video and then read the next frames in the loop

	if currentframe is not None:
		#notice that we are passing the current frame now so video writer
		#object will be initiated when the writing begins. Also in this case
		#we start saving from 10 Seconds onwards until video complete
		saved = vw.save(currentframe, 10000,-1)
		if not saved :
			print ("Video not saved!")
		#display video stream
		cv2.imshow('frame',currentframe)

On line 20 we pass call the “save” function. Notice that we had not passed the frame object while creating the class, so, VideoWriter object will be initiated when the writing actually begins. Also, note that in this case we start saving from 10 Seconds onwards until video complete (indicated by -1). Then we do some error handling based on the return values.

	else:
		break
	if cv2.waitKey(1) & 0xFF == ord('q'):
		break
	
cv2.destroyAllWindows()

And then from line 23 onwards we just display the video and then process some cleanup.

So, as you can see, it is very easy to save video with OpenCV. However, if you use the class we wrote above to encapsulate the basic functions of OpenCV VideoWrite functions then it becomes less error prone and much easier to manage.

Download the code here saveVideo-1.zip

 
 #requirement python3 and openCv
 Run project with python saveVideo.py

Do let me know if you have any questions and comments.

Reference:
1. https://docs.opencv.org/3.4/dd/d9e/classcv_1_1VideoWriter.html

Leave a Reply

Your email address will not be published. Required fields are marked *