1

my overall goal is to capture a video with the Raspberry Pi HQ Camera. When using the terminal and the standard raspivid command like raspivid -w 640 -h 480 -fps 90 -t 10000 -o video.h264 the resulting video didn't store the right infomation regarding fps (and frame_count). I checked it using openCV and python with the following code:

import cv2

cap = cv2.VideoCapture('video.h264')

if cap.isOpened():

    fps = cap.get(cv2.CAP_PROP_FPS)

    print('fps:', fps)  # float `fps`

    frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)

    print('frames count:', frame_count)  # float `frame_count`

I expected it to be fps: 90.0 and frame_count: 900.0, but it was fps: 25.0 and frame_count: -192153584101141.0.

After some research I came to the conclusion, that .h264 doesn't store the right fps value but a default value (in my case 25.0 fps).

Now my next attempt was to use openCV and python to capture video with the HQ camera. I tried to do so with the following code (which is pretty much standart when trying to capture a video with openCV):

import numpy as np
import cv2

#had to use (-1,cv2.CAP_V4L) instead of (0) for it to find my HQ Camera
cap = cv2.VideoCapture(-1,cv2.CAP_V4L)

fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('video_out.avi', fourcc, 30.0, (640, 480))

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        out.write(frame)
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break
cap.release()
out.release()
cv2.destroyAllWindows()

Now when recording roughly a 10 sec. long video and and analyzing it afterwards using the same method as before, the result was as expected fps: 30.0 and frame_count: (roughly) 300.0.

Now when changing the fps from 30 to 60 in line #8 from out = cv2.VideoWriter('video_out.avi', fourcc, 30.0, (640, 480)) to out = cv2.VideoWriter('video_out.avi', fourcc, 60.0, (640, 480)) and analyzing a roughly 10 sec. long video afterwards the result was different as expected. Instead of fps: 60.0 and frame_count: (roughly) 600.0 the result was fps: 60.0 and frame_count: (roughly) 300.0. Also the video was now only 5 sec. long instead of 10 sec.

I came to the conclusion, that the video was still recorded at 30 fps and with 10 sec. of recording the total number of 300 frames is correct. But in the video_out.avi file the fps was set to 60 fps, what resulted in a (60 fps) video which is only half the time long, because there are still only 300 frames in total available.

Now what still remains is the question:

In which way do I change the above code, to be able to capture a 10 sec. long video with 60 fps and get 600 frames in total?

EDIT #1

@Tiphel stated in his comment that adding cap.set(cv2.CAP_PROP_FPS, 10) could solve the problem; and it seemed to work. I now tried to edit the code in a way, that it records a previous defined time (e.g. 5 sec.) and then stops. By doing this I could verify, if a 60 fps video with 5 sec. of recording would indeed have 300 frames. This attempt resulted in following code:

import numpy as np
import cv2
import time

f=open("fps_and_time_passed.txt", "w")

cap = cv2.VideoCapture(-1,cv2.CAP_V4L)
cap.set(cv2.CAP_PROP_FPS, 30)

fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('vid_out.avi', fourcc, 30.0, (640, 480))

capture_time=5.0
start_time=time.time()

while(time.time() - start_time < capture_time):
    
    #print time passed in console
    #print('time passed: ' + str(time.time()-start_time))
    
    #write time passed in .txt file
    f.write('time passed: ' + str(time.time()-start_time) + ' ')
    
    fps=cap.get(cv2.CAP_PROP_FPS)
    
    #print fps in console
    #print('fps: ',fps)
    
    #write fps in .txt file
    f.write('fps: ' + str(fps) + '\n')
    ret, frame = cap.read()
    if ret==True:
        out.write(frame)
        cv2.imshow('frame', frame)       
        #if cv2.waitKey(1) & 0xFF == ord('q'):
        #    break
    else:
        break
f.close()
cap.release()
out.release()
cv2.destroyAllWindows()

After analyzing the now resulting "vid_out.avi" file I expected it to be fps: 30.0 and frame_count 150.0 but it was fps: 30.0 and frame_count: 115.0. I then looked into the "fps_and_time_passed.txt" file and to my surprise realized, that there is a roughly 1,4 sec. delay in the beginning:

time passed: 1.1682510375976562e-05 fps: 30.0
time passed: 1.4038400650024414 fps: 30.0
time passed: 1.4290287494659424 fps: 30.0
time passed: 1.4499273300170898 fps: 30.0
...
time passed: 4.89248514175415 fps: 30.0
time passed: 4.925801515579224 fps: 30.0
time passed: 4.9621946811676025 fps: 30.0
time passed: 4.995436191558838 fps: 30.0

As you can see the delay is between the first and the second output.

So my new question is:

How can I get rid of this delay?

adroid
  • 25
  • 4
  • Maybe set the fps in the VideoCapture. My guess is that the camera capture fps is lower than what you want to write. Use cap.set(cv2.CAP_PROP_FPS, 10) to set the capture to 10fps and monitor it to make sure your camera is actually streaming at the desired fps. Read the current fps with fps = cap.get(cv2.cv.CV_CAP_PROP_FPS) – Tiphel Jun 02 '21 at 08:38
  • @Tiphel first of all, thank you! Your recommendation worked so far. After this I tried to change the code, so it would capture a specific time period (e.g. 5 sec.) by itself (without me pressing 'q'). I will edit the above text, to explain the new problem in more detail. – adroid Jun 02 '21 at 10:23
  • @Tiphel I described the new problem above, thanks again! – adroid Jun 02 '21 at 11:36

1 Answers1

0

I think it is because you start your timer before the first frame is captured so you end up short of your desired duration.

I believe if you count the frames, based on the video duration you want, you will have the desired output.

Try something like :

fps = 30
capture_time = 5.0
n_frames = capture_time * fps
curr_frame = 0

while (curr_frame < n_frames):
    ret, frame = cap.read()
    if ret==True:
        out.write(frame)
        cv2.imshow('frame', frame)       
    else:
        break

    fps=cap.get(cv2.CAP_PROP_FPS)  
    #write fps in .txt file
    f.write('fps: ' + str(fps) + '\n')

    curr_frame+=1

I haven't tested this, it might not work but that's the idea.

Tiphel
  • 323
  • 1
  • 11
  • 1
    thank you! I checked your code, it worked for me. Unfortunately, the delay between the 1st and the 2nd frame is still there, but I changed the while loop to 'while(curr_frame < n_frames +1)' so I get one more frame. Then I just forget about the 1st frame. Still your idea with counting frames instead of waiting for a specific time to pass works better, beacause for me it´s more important to get a total number of frames, than to be 100% accurate with the rec time. When setting the cap time to 5.0 sec. and fps to 60, the real cap time was about 5,1 sec. for 300 frames ignoring the delay. – adroid Jun 04 '21 at 11:12