You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派4上Python OpenCV多摄像头流交替读取帧的问题排查

树莓派4多路视频流交替帧读取优化

问题背景

我在树莓派4上播放多路摄像头流,为降低CPU负载,计划采用交替读取不同视频帧的策略:比如第一个视频仅显示奇数帧、第二个视频仅显示偶数帧,迭代逻辑为第1次读取视频1的帧1,第2次读取视频2的帧2,后续打算扩展至6路。但测试本地视频时,只有第一个视频能正常播放,第二个视频从一开始就卡顿;删除代码中%3==0的条件后,第二个视频才可正常播放。

原测试代码:

import numpy as np
import cv2
names = ['C:/Users/viv/test.mp4', 'C:/Users/viv/parking_lot_1.mp4'];
window_titles = ['first', 'second']
cap = [cv2.VideoCapture(i) for i in names]
frames = [None] * len(names);
gray = [None] * len(names);
ret = [None] * len(names);

while True:
    for i,c in enumerate(cap):
        #print (i)
        if c is not None:
            
            if (c.get(cv2.CAP_PROP_POS_FRAMES)%2==0) & i==0:
                ret[i], frames[i] = c.read();
            elif (c.get(cv2.CAP_PROP_POS_FRAMES)%3==0) & i==1:
                ret[i], frames[i] = c.read();
                print(i,c.get(cv2.CAP_PROP_POS_FRAMES))

    for i,f in enumerate(frames):
        if ret[i] is True:
            gray[i] = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
            cv2.imshow(window_titles[i], gray[i]);
           
    if cv2.waitKey(1) & 0xFF == ord('q'):
       break

for c in cap:
    if c is not None:
        c.release();
cv2.destroyAllWindows()

问题分析

  1. 帧指针停滞:每次循环遍历所有视频,但仅满足特定帧位置条件时才调用read(),未满足条件的视频帧指针停留在原地,后续判断永远卡在同一帧,导致视频卡顿。
  2. 逻辑运算符错误:用位运算&代替了逻辑与and,虽暂时可能生效,但属于错误用法,易引发隐性问题。
  3. 条件匹配不合理:第二个视频的%3==0条件过于严苛,且未与全局迭代节奏关联,导致大部分循环都不读取该视频帧,视觉上表现为卡顿。

修复方案

  • 引入全局迭代计数器,用其奇偶性控制交替读取逻辑,保证按预期顺序读取不同视频的帧。
  • 对未选中读取的视频,调用轻量的grab()方法推进帧指针,避免停滞且不增加过多CPU负载。
  • 替换位运算&为逻辑与and,修正逻辑判断方式。

优化后代码

import numpy as np
import cv2

names = ['C:/Users/viv/test.mp4', 'C:/Users/viv/parking_lot_1.mp4']
window_titles = ['first', 'second']
cap = [cv2.VideoCapture(i) for i in names]
frames = [None] * len(names)
gray = [None] * len(names)
ret = [False] * len(names)
# 全局迭代计数器,控制交替逻辑
iteration = 0

while True:
    iteration += 1
    for i, c in enumerate(cap):
        if not c.isOpened():
            continue
        
        # 根据全局迭代次数决定是否读取当前视频的帧
        # 第一个视频:奇数迭代读取(对应奇数帧)
        if i == 0 and iteration % 2 == 1:
            ret[i], frames[i] = c.read()
        # 第二个视频:偶数迭代读取(对应偶数帧)
        elif i == 1 and iteration % 2 == 0:
            ret[i], frames[i] = c.read()
        # 不读取时,用grab()推进帧指针
        else:
            c.grab()

    # 更新显示窗口
    for i, f in enumerate(frames):
        if ret[i]:
            gray[i] = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
            cv2.imshow(window_titles[i], gray[i])
        # 若未读取到新帧,保持之前的画面(可选)
        elif gray[i] is not None:
            cv2.imshow(window_titles[i], gray[i])

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放资源
for c in cap:
    if c.isOpened():
        c.release()
cv2.destroyAllWindows()

扩展到6路视频的思路

若要扩展至6路,只需调整迭代计数器的判断逻辑,比如按iteration % 6的结果对应不同视频的读取时机,例如:

  • iteration % 6 == 0:读取视频0的帧
  • iteration % 6 == 1:读取视频1的帧
  • ...以此类推,保证每6次循环依次读取6路视频各一帧,既控制CPU负载,又维持视频播放的连贯性。

内容的提问来源于stack exchange,提问作者vinita

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:33:58