我从网络摄像头读取图像并在其中找到一些运动数据。 我写了一个小类,它测量从读取图像、检测运动等所有步骤,然后打印它。
现在,当我在没有特定分辨率的情况下初始化网络摄像头时,拍摄的帧的大小为 640x480。现在我想提高分辨率,所以我将其设置为1920x1080:
WEBCAM_RAW_RES = (640, 480)
FRAMERATE = 20
vid = cv2.VideoCapture(0)
vid.set(cv2.CAP_PROP_FPS, FRAMERATE)
vid.set(cv2.CAP_PROP_FRAME_WIDTH, WEBCAM_RAW_RES[0])
vid.set(cv2.CAP_PROP_FRAME_HEIGHT, WEBCAM_RAW_RES[1])
它可以工作,但是 vid.read() 语句的平均时间从 ~10ms 增加到 >200ms。所以我将其更改为 1280x800,vid.read() 语句下降到 ~80ms。
顺便说一句,这是支持的网络摄像头分辨率:
pi@rpi:~/ $ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'MJPG' (Motion-JPEG, compressed)
Size: Discrete 1920x1080
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 1280x800
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 640x400
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
[1]: 'YUYV' (YUYV 4:2:2)
Size: Discrete 1920x1080
Interval: Discrete 0.200s (5.000 fps)
Interval: Discrete 0.333s (3.000 fps)
Size: Discrete 1280x800
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 640x400
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
如何在不延迟 200 毫秒的情况下获得更高的分辨率?我必须切换到 MJPEG 流格式吗?我在网上找不到任何相关信息...
这是我的整个代码:
import os
import cv2
from MotionDetectorBlob import MotionDetectorBlob
from utils import CalcTimer, resize_and_crop
from config import *
WEBCAM_RAW_RES = (640, 480)
FRAMERATE = 20
def start_webcam():
print("Setting up webcam and finding the correct brightness.")
vid = cv2.VideoCapture(0)
vid.set(cv2.CAP_PROP_FPS, FRAMERATE)
vid.set(cv2.CAP_PROP_FRAME_WIDTH, WEBCAM_RAW_RES[0])
vid.set(cv2.CAP_PROP_FRAME_HEIGHT, WEBCAM_RAW_RES[1])
vid.set(cv2.CAP_PROP_AUTO_EXPOSURE, 3) # auto mode
time.sleep(5) # find brightness
#vid.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1) # manual exposure mode
vid.set(cv2.CAP_PROP_AUTO_WB, 0) # manual awb
vid.set(cv2.CAP_PROP_WB_TEMPERATURE, 4000) # manual awb
print("Webcam setup done.")
detector = MotionDetectorBlob()
firstrun = True
timer = CalcTimer()
while True:
# Capture the video frame
# by frame
try:
timer.start()
ret, frame = vid.read()
print(f"original webcam res: {frame.shape}")
timer.measure("read")
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
timer.measure("rotate")
# as we skip analyser, directly write the frames
data = detector.detect(frame)
print(timer.results())
except KeyboardInterrupt:
break
# After the loop release the cap object
vid.release()
start_webcam()
vid.read() 性能缓慢的主要原因是您使用的是 YUYV 格式,该格式未压缩,比 MJPG 格式需要更多的带宽和处理能力,MJPG 格式经过压缩,可以更快地处理更高分辨率。
要切换到 MJPG 格式,您需要在初始化 vid 对象后在代码中再添加一行:
vid = cv2.VideoCapture(0)
vid.set(cv2.CAP_PROP_FPS, FRAMERATE)
vid.set(cv2.CAP_PROP_FRAME_WIDTH, WEBCAM_RAW_RES[0])
vid.set(cv2.CAP_PROP_FRAME_HEIGHT, WEBCAM_RAW_RES[1])
vid.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG")) # add this line
这将告诉 OpenCV 使用 MJPG 编解码器来捕获视频流。您还可以使用 vid.get() 方法和适当的属性代码(例如 cv2.CAP_PROP_FOURCC 和 cv2.CAP_PROP_FORMAT)来检查网络摄像头支持的格式和编解码器。
说明:YUYV 格式是一种原始格式,它用两个字节对每个像素进行编码,一个用于亮度 (Y),一个用于色度 (UV)。这意味着对于 1920x1080 分辨率,每帧将有 1920x1080x2 = 4147200 字节,或大约 4 MB。要以 20 FPS 传输如此大量的数据,您需要 4x20 = 80 MB/s 的带宽,这对于 USB 网络摄像头来说相当高。
MJPG格式是一种压缩格式,使用JPEG算法将每一帧编码为图像。这会显着减小每帧的大小,具体取决于图像的质量和复杂性。例如,分辨率为 1920x1080 的典型 JPEG 图像的大小约为 200 KB 或 0.2 MB。要以 20 FPS 传输如此大量的数据,需要 0.2x20 = 4 MB/s 的带宽,这比 YUYV 格式要低得多。 因此,通过使用 MJPG 格式,您可以减少延迟并提高 vid.read() 的速度,并节省一些用于处理帧的 CPU 周期。
就我而言,如果每次更改分辨率时也更改 fourcc,效果会很好。内部更改分辨率时,除非单独指定视频格式,否则似乎会设置为默认格式。