Inconsistent number of read video frames with OpenCv - python

I'm trying to extract the frames from the following video (Disclosure: I'm not the owner of the video, the video is taken from a public dataset). To get the number of video frames I do:
cap = cv2.VideoCapture(video_path)
cap.get(cv2.CAP_PROP_FRAME_COUNT) # This returns 32
To extract the frames I have this method:
def obtain_frames(video_path: str):
cap = cv2.VideoCapture(video_path)
frames = []
while True:
success, image = cap.read()
if not success:
break
frames.append(image)
return frames
Finally, I count the number of extracted video frames with:
frames = obtain_frames(video_path)
len(frames) # This returns 17
and I get an inconsistent number compared to cv2.CAP_PROP_FRAME_COUNT.
I'm also aware of this SO question but still, when I display the video I can go all through the end and yet I can't read all the frames.
Any pointers/directions are welcome.

Related

Duration of videoWriter being controlled with fps and frame Count (OpenCV & Python)

When use the code below, it gives me a video from stream which has desired number of frames and FPS I choose. However, video length becomes duration =(1/fps)*frameCount. I guess that mp4 does video compression so that the duration of the video does not make the file size bigger, this is a good thing. Nevertheless, is there a way to have a video with smaller duration?
EDIT: An example scenario:
The thing I wanna do is to have 1 Frame for every minutes of streaming and I want to have, lets say 1 FPS video with 1000 Frames. In this scenario, the actual streaming duration becomes 1000 minutes, but I want a 1 FPS video which has 1000 seconds of duration.
Here is the code I am using below:
import os
import cv2 as cv
import math
cap = cv.VideoCapture('rtsp://**.***.**.*:*****/***********')
# Frame sizes
down_width = 640
down_height = 480
down_points = (down_width, down_height)
# stream fps and desired FPS
fpsSystem = cap.get(cv.CAP_PROP_FPS) # 25 in my case
fps = 1.0/60
# Frame Count parameters
frameRead = 0 # successfully readed frame number
DesFrameCount = 60*6 # Desired frame count
takeFrameTime = 1/fps # every TakeFrameTime seconds frames will be stored
frameCycle = 0 # every frameCycle. frame will be stored
frameWritten = 0
success = True
# Define the codec and create VideoWriter object
fourcc = cv.VideoWriter_fourcc(*'mp4v')
# Video Name
randomName = np.random.randint(0,1000)
out = cv.VideoWriter('output'+str(randomName) + ".mp4", fourcc, fps, (down_width, down_height))
while success:
success, frame = cap.read()
if not success:
print("Can't receive frame (stream end?). Exiting ...")
break
else:
frameRead += 1
frameCycle += 1
# Frame Resizing
frame = cv.resize(frame,down_points,interpolation= cv.INTER_LINEAR)
# Save the particular frame desired to be written according to frame parameters
if frameCycle == math.floor(fpsSystem*takeFrameTime):
frameCycle = 0
out.write(frame)
frameWritten += 1
# Stop the loop when desired number of Frame obtained
if cv.waitKey(1) == ord('q') or (frameWritten == DesFrameCount):
break
# Release everything if job is finished
cap.release()
out.release()
cv.destroyAllWindows()

Save video clip from longer video between two timestamps in Python cv2

I have an hour-long video that I would like to save a clip between two timestamps-- say, 11:20-11:35. Is the best way to do this frame-by-frame, or is there a better way?
Here's the gist of what I did frame-by-frame. If there's a less lossy way to do it, I'd love to know! I know I could do it from the terminal using ffmpeg, but I am curious for how to best do it using cv2.
def get_clip(input_filename, output_filename, start_sec, end_sec):
# input and output videos are probably mp4
vidcap = cv2.VideoCapture(input_filename)
# math to find starting and ending frame number
fps = find_frames_per_second(vidcap)
start_frame = int(start_sec*fps)
end_frame = int(end_sec*fps)
vidcap.set(cv2.CAP_PROP_POS_FRAMES,start_frame)
# open video writer
vidwrite = cv2.VideoWriter(output_filename, cv2.VideoWriter_fourcc(*'MP4V'), fps, get_frame_size(vidcap))
success, image = vidcap.read()
frame_count = start_frame
while success and (frame_count < end_frame):
vidwrite.write(image) # write frame into video
success, image = vidcap.read() # read frame from video
frame_count+=1
vidwrite.release()

Extract images from live video feed of ipcamera

I want to extract images every 5 minutes from a webcam live video feed using opencv. I have the below code to extract from a video. But don't know how to do it for a live video stream from an ipcamera
Below code is used to get an image every 5 seconds from a valid video
import cv2
videoFile = "folder-path"
cap = cv2.VideoCapture(videoFile)
success, image = cap.read()
success = True
count = 0
while success:
# Capture frame-by-frame
cap.set(cv2.CAP_PROP_POS_MSEC,(count*1000)
success, image = cap.read()
cv2.imwrite("file path/frame%d.jpg" % count, image)
count = count + 5
Use cv2.VideoCapture() with the index of the camera you care about. If you have only one camera, cv2.VideoCapture(0) will do the trick. If you have multiple, you will want to increment the index until you are accessing the correct camera.
This code will capture a frame from camera 0 every 5 minutes:
camera = cv2.VideoCapture(0) # start a connection to the camera
ret, frame = camera.read() # read a frame
cv2.waitKey(300000) # wait 5 minutes

How to create array of frames from an mp4 with opencv for python

I am attempting to use opencv_python to break an mp4 file down into it's frames so I can later open them with pillow, or at least be able to use the images to run my own methods on them.
I understand that the following snippet of code gets a frame from a live video or a recorded video.
import cv2
cap = cv2.VideoCapture("myfile.mp4")
boolean, frame = cap.read()
What exactly does the read function return and how can I create an array of images which I can modify.
adapted from How to process images of a video, frame by frame, in video streaming using OpenCV and Python. Untested. However, the frames are read into a numpy array and and append to a list that is converted to a numpy array when the all the frames are read in.
import cv2
import numpy as np
images = []
cap = cv2.VideoCapture("./out.mp4")
while not cap.isOpened():
cap = cv2.VideoCapture("./out.mp4")
cv2.waitKey(1000)
print "Wait for the header"
pos_frame = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
while True:
frame_ready, frame = cap.read() # get the frame
if frame_ready:
# The frame is ready and already captured
# cv2.imshow('video', frame)
# store the current frame in as a numpy array
np_frame = cv2.imread('video', frame)
images.append(np_frame)
pos_frame = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
else:
# The next frame is not ready, so we try to read it again
cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
print "frame is not ready"
# It is better to wait for a while for the next frame to be ready
cv2.waitKey(1000)
if cv2.waitKey(10) == 27:
break
if cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) == cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT):
# If the number of captured frames is equal to the total number of frames,
# we stop
break
all_frames = np.array(images)
Simply use this code to get an array of frames from your video:
import cv2
import numpy as np
frames = []
video = cv2.VideoCapture("spiderino_turning.mp4")
while True:
read, frame= video.read()
if not read:
break
frames.append(frame)
frames = np.array(frames)
but regarding your question, video.read() returns two values. The first one (read in the example code) indicates if the frame is successfully read or not (i.e., True on succeeding and False on any error). The second returning value is the frame that can be empty if the read attempt is unsuccessful or a 3D array (i.e., color image) otherwise.
But why can a read attempt be unsuccessful?
If you are reading from a camera, any problem with the camera (e.g., the cable is disconnected or the camera's battery is dead) can cause an error.
If you are reading from a video, the read attempt will fail when all the frames are read, and there are no more.

How does QueryFrame work?

import cv
# create a window
winname = "myWindow"
win = cv.NamedWindow(winname, cv.CV_WINDOW_AUTOSIZE)
# load video file
invideo = cv.CaptureFromFile("video.avi")
# interval between frame in ms.
fps = cv.GetCaptureProperty(invid, cv.CV_CAP_PROP_FPS)
interval = int(1000.0 / fps)
# play video
while (True):
im = cv.QueryFrame(invideo)
cv.ShowImage(winname, im)
if cv.WaitKey(interval) == 27: # ASCII 27 is the ESC key
break
del invideo
cv.DestroyWindow(winname)
Above is a simple python code using opencv libraray to play a video file.
The only part I don't understand is im = cv.QueryFrame(invideo)
According to opencv api, " QueryFrame grabs a frame from a camera or video file, decompresses it and returns it."
For my understanding, it just returns an image in iplimage format for one single frame, but how does it know which frame it returns? The only parameter QueryFrame need is the video capture, but there no index to tell it which frame amount the video frames I need to retrieve. What if I need to play a video starting from middle part?
You have to use cv.GetCaptureProperty with CV_CAP_PROP_FRAME_COUNT to get the number of frames of your video.
Divide it by 2 to find the middle.
Use QueryFrame until you reach this value.

Categories