I have a USB camera that is able to record 60 fps. I am using openCV with simple python code on Windows to capture some videos however it is not going all smoothly!
My main problem is that, if I set the [width, height] properties to the maximum the camera accepts it is able to record 60 fps just fine otherwise (i.e. any lower resolution) the recording/streaming will drop to 30 fps max (the recording will be so funny that is either sped up or slowed down to match the specified recording fps; i.e. if the resolution specified 320X240 and recording fps set to 60 and for 10 seconds the resultant video will be squashed to 5 seconds so basically sped up 2x!!)
I don't understand why exactly that happens? any ideas?
here is a snippet of the code:
import cv2
import os
import time
def readVideo(Did):
cap = cv2.VideoCapture(Did)
# cap.set(cv2.CAP_PROP_FPS, 60) # no matter if you specify or not it selects what suits!!
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) # 640 is maximum
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) # 480 is maximum
ret,frame = cap.read()
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('./output.avi', fourcc, 60.0, (320,240)) # (width,height) must match what is stated above in CAP!
while ret:
ret,frame = cap.read()
elapsed = time.time() - start
count = count + 1 # frame numbers
cv2.putText(frame,str(cfpsBacked), (25,15),font,fontScale,fontColor,lineType)
out.write(frame)
cv2.imshow('camera',frame)
if elapsed - tick >= 1:
print("Actual count:{}",count)
tick += 1
cfpsBacked = count
count = 0
if tick - 10 == 0: # just records 10 seconds
break
if cv2.waitKey(10) & 0xFF == ord('q'):
break
out.release()
cap.release()
cv2.destroyAllWindows() # destroy all the opened windows
Related
I could record the screen, but whenever I play the video it is very fast. How can I solve this issue?
import pyautogui
import cv2
import numpy as np
resolution = (1920, 1080)
codec = cv2.VideoWriter_fourcc(*"XVID")
filename = "Recording.avi"
fps = 60.0
out = cv2.VideoWriter(filename, codec, fps, resolution)
cv2.namedWindow("Live", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Live", 480, 270)
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
cv2.imshow('Live', frame)
if cv2.waitKey(1) == ord('q'):
break
time.sleep(1/30)
out.release()
cv2.destroyAllWindows()
There are a few things you can try to make the recorded video play at a normal speed. One possible solution is to reduce the number of frames per second (fps) that are being recorded. In your code, you are setting the fps value to 60.0, which is a very high value and may be causing the recorded video to play back too quickly. Try set fps to 25 or 30. Also you can try increasing the amount of time that the sleep() function is called, which will cause the loop to pause for a longer period of time between frames.
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()
I am using OpenCV4 along with python 3 to open a webcam, grab the frames and display them in a window, just like the first code tutorial provided here. However, it takes a different amount of time grabbing different frames: sometimes it takes 0.01 s to grab, and sometimes it takes 0.33 s, which creates lags when showing the frames in the window.
Is there a way to force a constant time when grabbing frames so that i can see the video without lag? I think it is happening with OpenCV because when i use a default windows camera viewer to see the video it displays it normally.
What i already tried is wait for some time using time.sleep() before grabbing a frame again. But this does not help.
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the resulting frame
cv2.imshow('frame',gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
One potential way is to set a timestamp within the loop and keep track of the time the last frame was shown. For instance, only once a certain amount of time has elapsed then you show the frame. At the same time, you constantly read frames to keep the buffer empty to ensure that you have the most recent frame. You don't want to use time.sleep() because it will freeze the program and not keep the buffer empty. Once the timestamp hits, you show the frame and reset the timestamp.
import cv2
import time
cap = cv2.VideoCapture(0)
# Timeout to display frames in seconds
# FPS = 1/TIMEOUT
# So 1/.025 = 40 FPS
TIMEOUT = .025
old_timestamp = time.time()
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
if (time.time() - old_timestamp) > TIMEOUT:
# Display the resulting frame
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
old_timestamp = time.time()
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
When working correctly, this app will be suspended at the read() call until the next frame from the streaming webcam is available. Smooth display depends on being able to execute whatever you may have added to the loop in less than the 1/FPS seconds. It also depends on the camera being UVC compliant and it may depend on the encoding algorithm being MJPEG, which is the case for most webcams. However the fact that you see delay up to 1/3 second is curious because that is a typical GOP period for mpeg or other inter-frame encoders.
If none of the above applies to your case then I suspect the problem is platform related rather than an OCV issue. Have you tried to duplicate the problem on another system?
I was facing a similar problem, and this is the solution I came up with. This would be the exact way to set a constant fps. This works on both live video and recorded video.
import cv2
import time
cap = cv2.VideoCapture('your video location')
initial_time = time.time()
to_time = time.time()
set_fps = 25 # Set your desired frame rate
# Variables Used to Calculate FPS
prev_frame_time = 0 # Variables Used to Calculate FPS
new_frame_time = 0
while True:
while_running = time.time() # Keep updating time with each frame
new_time = while_running - initial_time # If time taken is 1/fps, then read a frame
if new_time >= 1 / set_fps:
ret, frame = cap.read()
if ret:
# Calculating True FPS
new_frame_time = time.time()
fps = 1 / (new_frame_time - prev_frame_time)
prev_frame_time = new_frame_time
fps = int(fps)
fps = str(fps)
print(fps)
cv2.imshow('joined', frame)
initial_time = while_running # Update the initial time with current time
else:
total_time_of_video = while_running - to_time # To get the total time of the video
print(total_time_of_video)
break
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I am trying to record a video in 720p at 60 FPS or 1080p at 30 FPS, However when using the C920 webcam and OpenCV on python I can only get about 10 fps on 720p and 5fps on 1080p.
I have tried a lot different settings for openCV, none change the FPS however of the output.
import cv2
import time
FPS = 0
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
if(not cap.isOpened()):
exit()
cap.set(cv2.CAP_PROP_FOURCC, fourcc);
cap.open(cv2.CAP_ANY);
cap.set(cv2.CAP_PROP_CONVERT_RGB, 0);
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cap.set(cv2.CAP_PROP_FPS, 60)
last = time.time()
for i in range(0,100):
before = time.time()
rval, frame = cap.read()
now = time.time()
print("cap.read() took: " + str(now - before))
if(now - last >= 1):
print(FPS)
last = now
FPS = 0
else:
FPS += 1
cap.release()
I expect it to output 60fps but it only gives 9 or 10 fps
OpenCV automatically selects the first available capture backend (see here). It can be that it is not using V4L2 automatically.
Also set both -D WITH_V4L=ON and -D WITH_LIBV4L=ON if building from source.
Possibly, the pixel format that is selected by OpenCV does not support the frame rate that you want, in the resolution that you want. On Linux you can use v4l2-ctl --list-formats-ext and v4l2-ctl --all to see the settings.
In order to set the pixel format to be used set the CAP_PROP_FOURCC property of the capture:
capture = cv2.VideoCapture(cam_id, cv2.CAP_V4L2)
capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
I’m using OpenCV in a Python environment to capture a video stream from an external source, display the video, and write the video to a file. The video stream can come from different video sources. I need to write the video using the exact same frame rate as the incoming video (e.g., 60 fps, 29.97 fps, 30 fps, etc.).
Because the streaming video does not have the frame rate embedded in the stream, I need to determine the correct frame rate. I have tried suggestions by others of sampling some frames then divide the number of captured frames by the elapsed time. For me, this results in a frame rate that is close, but not close enough.
When I capture the video with VLC Media Player, VLC determines the frame rate correctly.
Here is the Python script that I’m currently using. It buffers 500 frames to compute the frame rate and then starts writing the video while continuing to capture (with a 500 frame delay). (VLS capture/write doesn’t have a noticeable delay.)
Foremost importance to me – correctly determine the frame rate of the incoming video stream. Second importance – I want to write the video with minimum delay after capture.
Any suggestions?
import numpy as np
import cv2
from time import time
cap = cv2.VideoCapture(0)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
kount = 0
delay = 500
buffer = []
start = time()
while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:
kount += 1
buffer.append(frame)
cv2.imshow('frame',frame)
if kount >= delay:
if kount == delay:
end = time()
fps = kount / (end - start)
out = cv2.VideoWriter('output.avi',fourcc, fps, (frame_width,frame_height))
out.write(buffer[kount-delay])
if cv2.waitKey(1) & 0xFF == ord('q'):
for i in range(kount - delay, kount):
out.write(buffer[i])
break
else:
break
print("Frames Per Second = ", fps)
cap.release()
out.release()
cv2.destroyAllWindows()