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 (:
Related
I am working on a python script to get images from a microscope camera and then combine them to have an avi file.(15fps for 3sec).
I wrote a program but the final result is too dark.
I am looking for a solution to make it brighter.
When I add enhancer I get this error
ValueError: image has wrong mode
The camera gives 45 dat files .I converted them to tiff and then concatenated then to have an avi file.
The camera is Andor Neo 5.5 sCMOS
Here is a part of my code:
with tifffile.TiffWriter('datfiles.tif', bigtiff=False) as tif:
for datfile in datfiles:
data = numpy.fromfile(
datfile,
count=width * height * 3 // 2, # 12 bit packed
offset=4, # 4 byte integer header
dtype=numpy.uint8,
).astype(numpy.uint16)
image = numpy.zeros(width * height, numpy.uint16)
image[0::2] = (data[1::3] & 15) | (data[0::3] << 4)
image[1::2] = (data[1::3] >> 4) | (data[2::3] << 4)
image.shape = height, width
#img = increase_brightness(image)
tif.write(
image, photometric='minisblack', compression=None,
metadata=None)
img = Image.open('datfiles.tif')
enhancer = ImageEnhance.Brightness(img)
img=enhancer.enhance(4)
for i in range(45):
try:
img.seek(i)
img.save('page_%s.tif'%(i,))
except EOFError:
break
os.remove("datfiles.tif")
'''
------------------------------------------
Concatenate the tif files ----> .avi of 3 sec
'''
image_folder =os.getcwd() #to be modified
print(image_folder) #testing
video_name = "ConcatenatedVideo.avi"
images = [img for img in os.listdir(image_folder) if img.endswith(".tif")]
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape
video = cv2.VideoWriter(video_name, 0, 15, (width,height))
for image in images:
video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
video.release()
for f in os.listdir(image_folder): #delete allthe tiff files
if(f.endswith(".tif")):
os.remove(os.path.join(image_folder, f))
print('finished')
os.chdir(path)
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!
When I used below built-in properties of OpenCv to count number of frame of a video, its shows correct result.
video = cv2.VideoCapture(path)
total = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
But, when I tried to save all those frames, its store less number of frames. Below is another code for saving frame-
def ExtractFrames(self, videoPath):
srcVideo = cv2.VideoCapture(videoPath)
try:
if not os.path.exists('VideoFrames'):
os.makedirs('VideoFrames')
except OSError:
print('Error: Creating directory.')
currentframe = 0
while (True):
success, image = srcVideo.read()
if success:
fileName = './VideoFrames/frame' + str(currentframe) + '.jpg'
self.lstVideoFrames.append(fileName)
cv2.imwrite(fileName, image)
currentframe += 1
else:
break
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()
When using opencv to save a bunch of pictures to a video file the file won't open unless I resize the image:
(Windows error - "This item is in a format we don't support. 0xc00d36b4")
My code looks like this:
import cv2
import os
input_path = "input_images_folder_path"
imgs_lst = os.listdir(r"C:\Users\...\input_path")
outvid_path = r"C:\Users\...\output.avi"
image0 = input_path +"\\"+ imgs_lst[0]
img0 = cv2.imread(image)
size = (int(img0.shape[1]), int(img0.shape[0]))
fps = 12.0
is_color = True
fourcc = cv2.VideoWriter_fourcc(*"XVID")
vid = cv2.VideoWriter(outvid_path, fourcc, 10.0, size, True)
for i in range(0,50):# int(len(imgs_lst))):
image = input_path +"\\"+ imgs_lst[i]
img = cv2.imread(image,1)
img = cv2.resize(img, size) #tried to comment this out... wont work either
cv2.imshow("img", img)
cv2.waitKey(1)
vid.write(img)
vid.release()
if I resize -
size = (int(img0.shape[1]/2), int(img0.shape[0]/2))
all works well.
My question is whether or not there is a limit on the frame size. I found some other answers regarding the output file size limitation, but nothing about the single frame shape.
(changing fps, format didn't work either)