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

OpenCV VideoCapture无法访问部分摄像头设备索引的解决方法

问题根因
  • 硬件带宽瓶颈:PC的USB总线为共享带宽架构,14个摄像头如果全部接在同一组USB根集线器(比如同一个扩展坞、同一排前置USB口)上,OpenCV默认以最高分辨率、最高帧率拉流时,单路摄像头就会占用大量带宽,后续端口的摄像头抢不到带宽资源,就会出现端口可识别但读帧失败的情况,偶数端口全部失效基本都是这个原因导致的。
  • 代码存在多处bug:
    • 端口枚举逻辑错误:原list_ports()遇到第一个打不开的端口就直接终止扫描,且扫描时打开摄像头后没有释放句柄,持续占用设备资源。
    • 摄像头资源泄漏:采集流程中打开摄像头后,从未调用release()释放句柄,后续摄像头访问会被占用阻塞。
    • 堆叠逻辑错误:原代码在每帧采集的循环内重复初始化像素和数组为0,最终平均计算实际只用到最后1帧,完全没有实现多帧平均的效果。
    • 基础语法错误:list_ports()函数内代码没有正确缩进,循环使用了未定义的变量stunden(实际定义的变量是hours)。
    • 采集参数未优化:打开摄像头时没有指定分辨率、帧率,默认参数占用带宽极高,自动对焦参数在帧循环内重复设置,生效逻辑不稳定。
修复方法
  • 硬件调整:把14个摄像头分散接到不同的USB控制器上,台式机优先接后置面板分属不同USB总线的接口,不要全部串在同一个USB Hub/扩展坞上,必要时加装PCIe转USB扩展卡分摊带宽。
  • 代码修正:
    • 优化端口扫描逻辑,固定扫描0-20端口,遇到打不开的端口直接跳过不终止,扫描完成立刻释放摄像头句柄。
    • 采用串行采集逻辑:每次只打开1个摄像头,采完指定帧数、计算完平均图、保存文件后立刻释放摄像头资源,避免同时占用多个设备带宽。
    • 打开摄像头后主动设置较低的分辨率、帧率,大幅降低带宽占用,摄像头初始化完成后就关闭自动对焦,避免参数反复调整。
    • 修正图像堆叠逻辑:把像素和数组的初始化移到帧采集循环外,所有帧采集完成后统一计算平均值。
    • 修复所有语法、变量名错误,提前创建1-14号摄像头对应的存储文件夹,避免文件保存失败。
修正后可运行代码
import os
import numpy as np
import time
from time import localtime
import subprocess
import sys

def install(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

# 避免每次运行都重复安装OpenCV
try:
    import cv2
except ImportError:
    install("opencv-python")
    import cv2


def list_ports(max_scan_port=20):
    working_ports = []
    available_ports = []
    for dev_port in range(max_scan_port):
        camera = cv2.VideoCapture(dev_port, cv2.CAP_DSHOW)
        if not camera.isOpened():
            print(f"Port {dev_port} is not working.")
            camera.release()
            continue
        is_reading, img = camera.read()
        w = camera.get(cv2.CAP_PROP_FRAME_WIDTH)
        h = camera.get(cv2.CAP_PROP_FRAME_HEIGHT)
        if is_reading:
            print(f"Port {dev_port} is working and reads images ({h} x {w})")
            working_ports.append(dev_port)
        else:
            print(f"Port {dev_port} for camera ({h} x {w}) is present but does not read.")
            available_ports.append(dev_port)
        # 扫描完成立刻释放资源
        camera.release()
    return available_ports, working_ports


if __name__ == "__main__":
    # 提前创建1-14号摄像头的存储目录
    for cam_id in range(1, 15):
        os.makedirs(f"./cam_{cam_id}", exist_ok=True)

    available, camports = list_ports()
    print(f"Detected working ports: {camports}")

    # 采集配置参数
    picture_count = 5  # 单次采集用于堆叠平均的帧数
    collect_hours = int(input("amount of hours: "))
    target_minute = localtime().tm_min
    # 采集分辨率、帧率,可根据需求调整,数值越低带宽占用越小
    FRAME_WIDTH = 640
    FRAME_HEIGHT = 480
    FRAME_FPS = 15

    for collect_round in range(collect_hours):
        current_hour = localtime().tm_hour
        print(f"starting hour round: {collect_round + 1}")

        for port_idx, cam_port in enumerate(camports):
            cam_id = port_idx + 1
            if cam_id > 14:
                break
            print(f"currently taking images at camera id: {cam_id}")
            cam = cv2.VideoCapture(cam_port, cv2.CAP_DSHOW)
            # 提前设置采集参数降低带宽占用
            cam.set(cv2.CAP_PROP_FRAME_WIDTH, FRAME_WIDTH)
            cam.set(cv2.CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT)
            cam.set(cv2.CAP_PROP_FPS, FRAME_FPS)
            cam.set(cv2.CAP_PROP_AUTOFOCUS, 0)
            # 等待摄像头参数生效
            time.sleep(0.8)

            img_sum = None
            capture_success = True
            timestr = time.strftime("%d.%m.%Y-%H_%M_%S")

            for frame_idx in range(picture_count):
                result, image = cam.read()
                if not result:
                    print(f"capture failure at camera: {cam_id}, frame: {frame_idx + 1}")
                    capture_success = False
                    break
                if img_sum is None:
                    img_sum = np.zeros_like(image, dtype=np.float64)
                img_sum += image.astype(np.float64)
                time.sleep(0.1)

            # 采完立刻释放摄像头
            cam.release()

            if capture_success:
                avg_img = (img_sum / picture_count).round().astype(np.uint8)
                save_path = f"./cam_{cam_id}/pic_{collect_round + 1}_{timestr}.tiff"
                cv2.imwrite(save_path, avg_img)
                print(f"save average image to {save_path}")

        print("waiting for the next hour")
        # 等待到下一小时的目标分钟点
        while True:
            current_time = localtime()
            if current_time.tm_min == target_minute and current_time.tm_hour != current_hour:
                break
            time.sleep(1)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:54:15