Hi i am trying to break a long video down into smaller videos. I got some code of the internet but when I run it it does not write the video what is wrong with my code?
I am not getting any errors.
import cv2
count = 0
if __name__ == '__main__':
vidPath = 'VideoNietBewerkt.mp4'
shotsPath = '/videos/%d.avi' % count
segRange = [(0,1000),(1000,2000),(2000,3000)] # a list of starting/ending frame indices pairs
cap = cv2.VideoCapture(vidPath)
fps = int(cap.get(cv2.CAP_PROP_FPS))
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = int(cv2.VideoWriter_fourcc('X','V','I','D')) # XVID codecs
for idx,(begFidx,endFidx) in enumerate(segRange):
writer = cv2.VideoWriter(shotsPath,fourcc,fps,size)
cap.set(cv2.CAP_PROP_POS_FRAMES,begFidx)
ret = True # has frame returned
while(cap.isOpened() and ret and writer.isOpened()):
ret, frame = cap.read()
frame_number = cap.get(cv2.CAP_PROP_POS_FRAMES) - 1
if frame_number < endFidx:
writer.write(frame)
else:
break
writer.release()
count += 1
The problem was that I did not closed my cap variable I fixed this by putting everything in the for loop
import cv2
vidPath = 'VideoNietBewerkt.mp4'
segRange = [(0,5000),(5000,50000),(50000,100400)] # <-- to fit my sample movie
for idx,(begFidx,endFidx) in enumerate(segRange):
cap = cv2.VideoCapture(vidPath) # <---- Open Cap
fps = int(cap.get(cv2.CAP_PROP_FPS))
size = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = int(cv2.VideoWriter_fourcc(*'jpeg'))
shotsPath = f'movie_{str(idx)}.avi' # <-- use idx for naming the output file
print(f'saving file: {shotsPath}')
writer = cv2.VideoWriter() # <-- instantiate the writer this way
writer.open(shotsPath, fourcc, fps, size) # <-- open the writer
cap.set(cv2.CAP_PROP_POS_FRAMES, begFidx)
while(cap.isOpened() and writer.isOpened()): # removed and ret
ret, frame = cap.read()
frame_number = cap.get(cv2.CAP_PROP_POS_FRAMES) - 1
if frame_number < endFidx:
writer.write(frame)
else:
break
writer.release()
cap.release() #<--- Closed Cap
It seems like there is a problem with the codec (at least for me) and with the output filename, which is not updated outside the loop.
I made some changes for working on my machine, try this out with a short movie, there are few comments in the code itself.
This worked for me:
import cv2
vidPath = 'movie.mp4'
segRange = [(0,30),(30,60),(60,90)] # <-- to fit my sample movie
cap = cv2.VideoCapture(vidPath)
fps = int(cap.get(cv2.CAP_PROP_FPS))
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = int(cv2.VideoWriter_fourcc(*'jpeg')) # <-- I had to change the codec
for idx,(begFidx,endFidx) in enumerate(segRange):
shotsPath = f'movie_{str(idx)}.avi' # <-- update filename here, use idx for naming the output file
print(f'saving file: {shotsPath}')
writer = cv2.VideoWriter() # <-- instantiate the writer this way
writer.open(shotsPath, fourcc, fps, size) # <-- open the writer
cap.set(cv2.CAP_PROP_POS_FRAMES, begFidx)
while(cap.isOpened() and writer.isOpened()): # removed and ret
ret, frame = cap.read()
frame_number = cap.get(cv2.CAP_PROP_POS_FRAMES) - 1
if frame_number < endFidx:
writer.write(frame)
else:
break
writer.release()
cap.release()
Related
In my code i'm looping over frames of a video, and trying to generate another mp4 video.
This is my code:
cap = cv2.VideoCapture(args.video)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(cap.get(cv2.CAP_PROP_FPS))
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (frame_width, frame_height))
while cap.isOpened():
ret, img = cap.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
out.release()
break
#<code>...
#<code>...
print(type(my_image))
out.write(my_image)
The output of print(type(my_image)) is numpy.ndarray for each frame. When I ran the code, i got output_video.mp4 file, but weights only 300 kb (it needs to be about 50 mb).
I tried to save each frame as an image, and to see if it will work, and it did. This is the code:
img = Image.fromarray(my_image, 'RGB')
img.save('frameeeee-%s.png'%i)
I coded this function to solve a similiar problem, you need to save the images singularly into a folder and then you can use frames2video to convert it into a video.
def frames2video( path_in = "/content/original_frames" , path_out = "/content/outputvideo",
frame_rate = 30 , video_name="output_video" ):
"""
Given an input path to a folder that contains a set of frames, this function
convert them into a video and then save it in the path_out.
You need to know the fps of the original video, are 30 by default.
"""
img_path_list = natsorted(os.listdir(path_in))
assert(len(img_path_list)>0)
img_array = []
print("[F2V] Frames to video...", end="\n\n")
with tqdm(total=len(img_path_list)) as pbar:
for count,filename in enumerate(img_path_list):
img = cv2.imread(path_in+"/"+filename)
if(img is None):break
height, width, layers = img.shape
img_array.append(img)
size = (width,height)
pbar.update()
if os.path.exists(path_out): shutil.rmtree(path_out)
os.mkdir(path_out)
out = cv2.VideoWriter(path_out+"/"+str(video_name)+'.mp4', cv2.VideoWriter_fourcc(*'DIVX'), frame_rate, size)
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
print("\n[F2V] Video made from "+str(count+1)+" frames", end="\n\n")
For completeness, i post also the viceversa, a function that given a video extract the frames.
def n_frames(video):
"""
Given an input video returns the EXACT number of frames(CV2 was not precise)
"""
success = True
count = 0
while success:
success,image = video.read()
if success == False: break
count+=1
return count
def video2frames( path_in = "/content/video.mp4" , path_out = "/content/original_frames",
n_of_frames_to_save = 999999, rotate=True, frames_name = "OrigFrame" ):
"""
Given a video from path_in saves all the frames inside path_out.
The number of frames(in case of long videos) can be truncated with
the n_of_frames_to_save parameter. Rotate is used to save rotated
frames by 90 degree. All the frames are named frames_name with an
index
"""
blur_threshold = 0
if os.path.exists(path_out): shutil.rmtree(path_out)
os.mkdir(path_out)
count = 0
success = True
vidcap = cv2.VideoCapture(path_in)
v2 = cv2.VideoCapture(path_in)
fps = vidcap.get(cv2.CAP_PROP_FPS)
if(fps>120):
print("CAP_PROP_FPS > 120, probabily you are using a webcam. Setting fps manually")
fps = 25
n_of_frames = n_frames(v2) # #int(video.get(cv2.CAP_PROP_FRAME_COUNT)) is not accurate, https://stackoverflow.com/questions/31472155/python-opencv-cv2-cv-cv-cap-prop-frame-count-get-wrong-numbers
if(n_of_frames_to_save < n_of_frames): n_of_frames = n_of_frames_to_save
print("[V2F] Dividing the video in " + str(n_of_frames) + " frames", end="\n\n")
for count in trange(n_of_frames):
success,image = vidcap.read()
if not success: break
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if(rotate): image = cv2.rotate(image,cv2.ROTATE_90_CLOCKWISE)
plt.imsave("%s/%s%d.png" % (path_out,frames_name+"_", count), image)
count+=1
print("\n[V2F] "+str(count)+" frames saved",end="\n\n")
return fps
Ok, I found a solution. I noticed that I had resize function in my code:
my_image = cv2.resize(image_before, (1280, 720))
So I changed
out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (frame_width, frame_height))
to
out = cv2.VideoWriter('outputttttt.mp4', fourcc, fps, (1280, 720))
And it works (:
I'm new to this community and I have a question about ffmpeg. I'm making a python script to concatenate videos (especially mp4) using ffmpeg and I also used multiprocessing to speed up the process.
this is the function to read and process the video file:
def process_video(group_number):
num_processes = mp.cpu_count()
width, height, frame_count = get_video_frame_details(file_name)
frame_jump_unit = frame_count // num_processes
# read video file
clip = cv.VideoCapture(file_name)
clip.set(cv.CAP_PROP_POS_FRAMES, frame_jump_unit * group_number)
# get height, width and frame count of the video
width, height = (
int(clip.get(cv.CAP_PROP_FRAME_WIDTH)),
int(clip.get(cv.CAP_PROP_FRAME_HEIGHT))
)
no_of_frames = int(clip.get(cv.CAP_PROP_FRAME_COUNT))
fps = int(clip.get(cv.CAP_PROP_FPS))
proc_frames = 0
# Define the codec and create VideoWriter object
fourcc = cv.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv.VideoWriter()
output_file_name = "Aktorik_sliced.mp4"
out.open(output_file_name, fourcc, fps, (width, height), True)
out.open("output_{}.mp4".format(group_number), fourcc, fps, (width,
height), True)
try:
while proc_frames < frame_jump_unit:
ret, frame = clip.read()
if not ret:
break
except:
# Release resources
clip.release()
out.release()
# Release resources
clip.release()
out.release()
This is the function to concatenate the videos:
def combine_output_files(num_processes):
output_file_name = "Aktorik_sliced.mp4"
# Create a list of output files and store the file names in a txt file
list_of_output_files = ["output_{}.mp4".format(i) for i in
range(num_processes)]
with open("list_of_output_files.txt", "w") as f:
for t in list_of_output_files:
f.write("file {} \n".format(t))
# use ffmpeg to combine the video output files
ffmpeg_cmd = "ffmpeg -y -loglevel error -f concat -safe 0 -i
list_of_output_files.txt -vcodec copy " + output_file_name
sp.Popen(ffmpeg_cmd, shell=True).wait()
# Remove the temperory output files
# for f in list_of_output_files:
# remove(f)
# remove("list_of_output_files.txt")
And this is the function to call the multiprocess:
def multi_process():
num_processes = mp.cpu_count()
width, height, frame_count = get_video_frame_details(file_name)
print("Video processing using {} processes...".format(num_processes))
start_time = time.time()
# Parallel the execution of a function across multiple input values
p = mp.Pool(num_processes)
p.map(process_video, range(num_processes))
combine_output_files(num_processes)
end_time = time.time()
total_processing_time = end_time - start_time
print("Time taken: {}".format(total_processing_time))
print("FPS : {}".format(frame_count/total_processing_time))
In the end, this is what I got as an output:
Output file #0 does not contain any stream
I've tried many things with this code, but I still got no clue where did I do wrong. It will be such a big help if somebody can help me :)
Thanks in advance!
I am using OpenCV 4.5.0 to stream a video from a USB webcam using the VideoCapture method of OpenCV. Here is the snippet of how I am reading the frames, processing them and then writing them to a file.
import cv2
import numpy as np
dev_id = 0
stream = cv2.VideoCapture(dev_id, cv2.CAP_V4L2)
fourcc = cv2.VideoWriter_fourcc(*'FMP4')
vpath = 'test.mp4'
writer = cv2.VideoWriter(vpath, fourcc, 30, (640,480), isColor=True)
if stream.isOpened():
rval, frame = stream.read()
else:
print('Could not open the stream for reading')
if not writer.isOpened():
print('Could not open the file for writing')
i = 0
while rval:
i += 1
with open('frame_{}.pkl'.format(i),'wb') as fout:
np.save(fout, frame)
writer.write(frame)
rval, frame = stream.read()
key = cv2.waitkey(1)
if key == 27: # ESC
break
writer.release()
My goal is to then read the video file that was written above and reproduce the exact same frames that I got during the live stream. I have not been able to find a way to write the live video stream to a file such that the frame extracted from the written video file matches exactly with the frame stored in the numpy array. In addition to FMP4 as the FourCC, I tried with MJPG, MP4V, XVID, LAGS, H264, MPG4. No luck so far. What am I doing wrong?
Here is how I am reading the video and comparing the frames:
vc = cv2.VideoCapture(vpath)
if vc.isOpened():
rval, frame_read = vc.read()
else:
print('Could not read video from the file')
j = 0
while rval:
j += 1
with open('frame_{}.pkl'.format(j),'rb') as fin:
frame_orig = np.load(fin)
if np.array_equal(frame_read, frame_orig):
print('same')
else:
print('different')
rval, frame_read = vc.read()
I have also tested if this is simply the indexing mismatch between the video writer and the video reader. It is not. This really looks like the difference caused by the codec used to write the frame to a file. Is there no codec which writes the same exact frame that is emitted by VideoCapture.read()?
I have a folder of pkl files in 'somefile' that I need to open as videos with opencv, but I keep getting the _pickle.UnpicklingError: unpickling stack underflow error. What am I doing wrong? I know that my code isn't pretty... Please don't roast me lol
import cv2 import os import pickle import numpy as np
subdir ='somefile' files = os.listdir(subdir)
# open pkl filesfor f in files:
with open(subdir + '/' + f, 'rb') as infile:
try:
unpickled_videos = pickle.load(infile)
for video in unpickled_videos:
print('{} has been unpickled'.format(os.path.abspath(video)))
# play video from file
for video in unpickled_videos:
if video == 'eye':
# create VideoCapture object, read from input file
cap = cv2.VideoCapture('eye' + '.mp4')
# check if camera opened successfully
if (cap.isOpened() == False):
print("Error opening {}".format(os.path.abspath(video)))
# convert resolutions from float to integer
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
fps = cap.get(cv2.cv.CV_CAP_PROP_FPS)
# define codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
out = cv2.VideoWriter(video + '.MP4', fourcc, fps, (frame_width, frame_height), True)
# read until video is completed
while True:
# capture frame-by-frame
ret, frame = cap.read()
# display resulting frame
cv2.imshow('frame', frame)
# press Q on keyboard to exit
if cv2.waitKey(0) & 0xFF == ord('q'):
break
except FileNotFoundError:
print('{} not found!'.format(f))
pass
except EOFError:
print('End of file error')
pass
#when everything done, release video capture object and close all frames
#cap.release() out.release() cv2.destroyAllWindows()
You could try to load it as a numpy array with:
import numpy as np
data = np.load(input_filename, allow_pickle=True)
The error sounds like it could be an issue with your data. In the case that some of your data is problematic, you could wrap the call in a try/except.
The idea is that, user should be able to load a video from their local machine and tell the program to take a screenshot from the video every 5sec or 30sec. Is there any library to help me with this task? Any idea from how to proceed would be helpful.
install opencv-python (which is an unofficial pre-built OpenCV package for Python) by issuing the following command:
pip install opencv-python
# Importing all necessary libraries
import cv2
import os
import time
# Read the video from specified path
cam = cv2.VideoCapture("C:/Users/User/Desktop/videoplayback.mp4")
try:
# creating a folder named data
if not os.path.exists('data'):
os.makedirs('data')
# if not created then raise error
except OSError:
print('Error: Creating directory of data')
# frame
currentframe = 0
while (True):
time.sleep(5) # take schreenshot every 5 seconds
# reading from frame
ret, frame = cam.read()
if ret:
# if video is still left continue creating images
name = './data/frame' + str(currentframe) + '.jpg'
print('Creating...' + name)
# writing the extracted images
cv2.imwrite(name, frame)
# increasing counter so that it will
# show how many frames are created
currentframe += 1
else:
break
# Release all space and windows once done
cam.release()
cv2.destroyAllWindows()
the above answer is partially right but time.sleep here does not help at all but rather it makes the process slower. however, if you want to take a screenshot at a certain time of a video you need to understand that every time you do "ret, frame = cam.read()" it reads the next frame of the video. every second in a video has a number of frames depends on the video. you get that number using:
frame_per_second = cam.get(cv2.CAP_PROP_FPS)
so if you need to take a screenshot of the 3rd second you can keep the iteration as is in the above answer and just add
if currentframe == (3*frame_per_second):
cv2.imwrite(name, frame)
this will take a screenshot of the first frame in the 3rd second.
#ncica & Data_sniffer solution remake
import cv2
import os
import time
step = 10
frames_count = 3
cam = cv2.VideoCapture('video/example.MP4')
currentframe = 0
frame_per_second = cam.get(cv2.CAP_PROP_FPS)
frames_captured = 0
while (True):
ret, frame = cam.read()
if ret:
if currentframe > (step*frame_per_second):
currentframe = 0
name = 'photo/frame' + str(frames_captured) + '.jpg'
print(name)
cv2.imwrite(name, frame)
frames_captured+=1
if frames_captured>frames_count-1:
ret = False
currentframe += 1
if ret==False:
break
cam.release()
cv2.destroyAllWindows()
#a generic function incorporating all the comments mentioned above.
def get_frames(inputFile,outputFolder,step,count):
'''
Input:
inputFile - name of the input file with directoy
outputFolder - name and path of the folder to save the results
step - time lapse between each step (in seconds)
count - number of screenshots
Output:
'count' number of screenshots that are 'step' seconds apart created from video 'inputFile' and stored in folder 'outputFolder'
Function Call:
get_frames("test.mp4", 'data', 10, 10)
'''
#initializing local variables
step = step
frames_count = count
currentframe = 0
frames_captured = 0
#creating a folder
try:
# creating a folder named data
if not os.path.exists(outputFolder):
os.makedirs(outputFolder)
#if not created then raise error
except OSError:
print ('Error! Could not create a directory')
#reading the video from specified path
cam = cv2.VideoCapture(inputFile)
#reading the number of frames at that particular second
frame_per_second = cam.get(cv2.CAP_PROP_FPS)
while (True):
ret, frame = cam.read()
if ret:
if currentframe > (step*frame_per_second):
currentframe = 0
#saving the frames (screenshots)
name = './data/frame' + str(frames_captured) + '.jpg'
print ('Creating...' + name)
cv2.imwrite(name, frame)
frames_captured+=1
#breaking the loop when count achieved
if frames_captured > frames_count-1:
ret = False
currentframe += 1
if ret == False:
break
#Releasing all space and windows once done
cam.release()
cv2.destroyAllWindows()
To add on data_sniffer's answer. I would recommend using round (Math function) on frame_per_second when checking as if the frame rate is a decimal number then it will go into an infinite loop.
The solutions provided do not work for me in several cases.
The FPS from cv2.CAP_PROP_FPS is a floating point value and the FPS rate of my testvid.mp4 was 23.976023976023978 according to this property.
When looping through current_frame / fps % 3, we will almost always have leftovers because of this floating point value. Same goes for (3*frame_per_second):, causing our imwrite to never be reached.
I solved this issue by using the same calculations, but storing the remainders and comparing those:
current_frame = 0
fps_calculator_previous = 0
while (True):
ret, frame = cam.read()
if ret:
# Still got video left.
file_name = f"./data_generation/out/{_fn}-{current_frame}.jpg"
fps_calculator = (current_frame / fps) % every_x_sec
if(fps_calculator - fps_calculator_previous < 0):
print("Found a frame to write!")
cv2.imwrite(file_name, frame)
fps_calculator_previous = fps_calculator
current_frame += 1
else:
break
This seems to work well for me with any value for both cv2.CAP_PROP_FPS as well as every_x_sec
My video was 18 minutes and 7 seconds long, and I captured 362 unique frames from that with every_x_sec set to 3.
Edited #ncica's code and noted that it is working fine.
import cv2
import os
import time
cam = cv2.VideoCapture("/path/to/videoIn.mp4")
try:
if not os.path.exists('data'):
os.makedirs('data')
except OSError:
print('Error: Creating directory of data')
intvl = 2 #interval in second(s)
fps= int(cam.get(cv2.CAP_PROP_FPS))
print("fps : " ,fps)
currentframe = 0
while (True):
ret, frame = cam.read()
if ret:
if(currentframe % (fps*intvl) == 0):
name = './data/frame' + str(currentframe) + '.jpg'
print('Creating...' + name)
cv2.imwrite(name, frame)
currentframe += 1
else:
break
cam.release()
cv2.destroyAllWindows()
The trick is it is looping frame-by-frame.
So, here we are capturing frames that we want and write disk as snapshot image file.
eg : If you want snapshot two second by two second intvl must be 2