I use OpenCV to capture webcam video as frames to a list variable, then write that list to disk both as image sequence and as a video file. The image quality is fine. But the video is horribly compressed, with a bitrate of less than 4mbit, sometimes even less than 3mbit.
I've spent days and nights on Google finding solutions, tried many fourccs and other wrappers like WriteGear and ffmpeg-python, but to no avail.
Can anyone please tell me how I
a) specify the bitrate for my H.264 file, and
b) choose a lossless compression codec?
(Python 3.10 on MacOS MacBook Pro M1 / MacOS 13.0.1)
# ::::: import libraries and set variables
import cv2, time, os, shutil, datetime as dt
frames = []
frame_dimensions_known = False
# ::::: define file and folder names
output_folder_name = "output"
frame_folder_name = "frames"
video_file_name = "video.mp4"
frame_file_name = "frame_x.jpg"
# ::::: create output folders and paths
path_script = os.path.abspath(os.path.dirname(__file__))
path_output = os.path.join(path_script,output_folder_name)
if not os.path.exists(path_output):
os.mkdir(path_output)
path_session_fold
path_session_folder = os.path.join(
path_script,
output_folder_name,
dt.datetime.now().strftime("%y%m%d_%H%M%S"))
os.mkdir(path_session_folder)
path_video = os.path.join(path_session_folder,video_file_name)
path_frames = os.path.join(path_session_folder,frame_folder_name)
os.mkdir(path_frames)
# ::::: open webcam stream
vcap = cv2.VideoCapture(0)
start_time = (time.time() * 1000)
if vcap.isOpened() is False:
print("[Exiting]: Error accessing webcam stream.")
exit(0)
# ::::: read frames
while True:
_grabbed, _frame = vcap.read()
if _grabbed is False:
print('[Exiting] No more frames to read')
exit(0)
# ::::: get frame dimensions on first read
if not frame_dimensions_known:
height, width, channels = _frame.shape
frame_dimensions_known = True
# ::::: append frame to list
frames.append(_frame)
# ::::: process frames
pass
# ::::: display frame
cv2.imshow("cam", _frame)
if cv2.waitKey(1) == ord("q"):
break
# ::::: close webcam stream
vcap.release()
cv2.destroyAllWindows()
# ::::: calculate frame rate
stop_time = (time.time() * 1000)
total_seconds = (stop_time - start_time) / 1000
fps = len(frames) / total_seconds
# ::::: open video writer
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter(
path_video,
fourcc,
fps,
(width, height)
)
# ::::: write frames to disk as files and video
frame_number = 0
for frame in frames:
# ::::: write video
video_writer.write(frame) # Write out frame to video
# ::::: construct image file path
frame_number = frame_number + 1
frame_number_file = str(frame_number).zfill(4)
frame_name = frame_file_name.replace('x',frame_number_file)
frame_path = os.path.join(path_frames,frame_name)
# ::::: write image
cv2.imwrite(
frame_path,
frame,
[cv2.IMWRITE_JPEG_QUALITY, 100]
)
# ::::: clean up
video_writer.release
print (f"Written {frame_number} frames at {fps} FPS.")