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

如何使用Vimba SDK使Allied Vision相机保持恒定30FPS?

实现Allied Vision Manta G-201C恒定30FPS图像采集的解决方案

问题背景

使用Allied Vision Manta G-201C相机进行图像采集,要求输出恒定30FPS,但当前采集帧率在33-34之间波动且不稳定,原代码如下:

#! /usr/bin/python3.7

from datetime import datetime
from functools import partial
import queue
import time

from vimba import *
import cv2


def setup_camera(cam):
    cam.set_pixel_format(PixelFormat.BayerRG8)
    cam.ExposureTimeAbs.set(10000)
    cam.BalanceWhiteAuto.set('Off')
    cam.Gain.set(0)
    cam.AcquisitionMode.set('Continuous')
    cam.GainAuto.set('Off')
    # NB: Following adjusted for my Manta G-033C
    cam.Height.set(492)
    cam.Width.set(656)

# Called periodically as frames are received by Vimba's capture thread
# NB: This is invoked in a different thread than the rest of the code!
def frame_handler(frame_queue, cam, frame):
    img = frame.as_numpy_ndarray()
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BAYER_RG2RGB)
    try:
        # Try to put the frame in the queue...
        frame_queue.put_nowait(img_rgb)
    except queue.Full:
        # If that fials (queue is full), just drop the frame
        # NB: You may want to handle this better...
        print('Dropped Frame')
    cam.queue_frame(frame)
    
def do_something(img, count):
    filename = 'data/IMG_' + str(count) + '.jpg'
    cv2.putText(img, str(datetime.now()), (20, 40)
        , cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255)
        , 2, cv2.LINE_AA)
    cv2.imwrite(filename, img)

def run_processing(cam):
    try:
        # Create a queue to use for communication between Vimba's capture thread
        # and the main thread, limit capacity to 10 entries
        frame_queue = queue.Queue(maxsize=10)
        # Start asynchronous capture, using frame_handler
        # Bind the first parameter of frame handler to our frame_queue
        cam.start_streaming(handler=partial(frame_handler,frame_queue)
            , buffer_count=10)

        start = time.time()
        frame_count = 0
        while True:
            if frame_queue.qsize() > 0:
                # If there's something in the queue, try to fetch it and process
                try:
                    frame = frame_queue.get_nowait()
                    frame_count += 1
                    cv2.imshow('Live feed', frame)
                    do_something(frame, frame_count)
                except queue.Empty:
                    pass
                
            key = cv2.waitKey(1)
            if (key == ord('q')) or (frame_count >= 100):
                cv2.destroyAllWindows()
                break
        
        fps = int((frame_count + 1)/(time.time() - start))
        print('FPS:', fps)
    finally:
        # Stop the asynchronous capture
        cam.stop_streaming()

#@profile
def main():
    with Vimba.get_instance() as vimba:
        with vimba.get_all_cameras()[0] as cam:
            setup_camera(cam)
            run_processing(cam)

if __name__ == "__main__":
    main()

可行解决思路及代码修改

1. 强制相机输出固定30FPS

相机默认可能采用自动帧率模式,需手动锁定帧率参数:

  • 在setup_camera函数中添加AcquisitionFrameRateAbs设置,直接指定30FPS:
def setup_camera(cam):
    cam.set_pixel_format(PixelFormat.BayerRG8)
    cam.ExposureTimeAbs.set(10000)
    cam.BalanceWhiteAuto.set('Off')
    cam.Gain.set(0)
    cam.AcquisitionMode.set('Continuous')
    cam.GainAuto.set('Off')
    # 新增:设置固定30FPS
    cam.AcquisitionFrameRateAbs.set(30.0)
    cam.Height.set(492)
    cam.Width.set(656)

Manta G-201C硬件支持30FPS输出,此设置会让相机严格按照30帧/秒的频率生成图像,从源头控制帧率。

2. 分离耗时的帧处理操作

当前do_something中的cv2.imwrite是同步磁盘写入操作,会阻塞主线程导致帧率波动。将保存任务放到独立线程执行:

import threading

def save_worker(save_queue):
    while True:
        img, count = save_queue.get()
        if img is None:
            break
        filename = 'data/IMG_' + str(count) + '.jpg'
        cv2.putText(img, str(datetime.now()), (20, 40)
            , cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255)
            , 2, cv2.LINE_AA)
        cv2.imwrite(filename, img)
        save_queue.task_done()

def run_processing(cam):
    try:
        frame_queue = queue.Queue(maxsize=5)  # 缩小队列容量,减少延迟
        save_queue = queue.Queue(maxsize=20)
        # 启动后台保存线程
        threading.Thread(target=save_worker, args=(save_queue,), daemon=True).start()
        
        cam.start_streaming(handler=partial(frame_handler,frame_queue), buffer_count=3)

        start = time.time()
        frame_count = 0
        while True:
            if frame_queue.qsize() > 0:
                try:
                    frame = frame_queue.get_nowait()
                    frame_count += 1
                    cv2.imshow('Live feed', frame)
                    # 将帧传递给保存线程,不阻塞主线程
                    save_queue.put_nowait((frame.copy(), frame_count))
                except queue.Empty:
                    pass
                
            key = cv2.waitKey(1)
            if (key == ord('q')) or (frame_count >= 100):
                save_queue.put_nowait((None, None))  # 通知保存线程退出
                cv2.destroyAllWindows()
                break
        
        # 等待所有保存任务完成
        save_queue.join()
        fps = frame_count / (time.time() - start)
        print('FPS:', round(fps, 2))
    finally:
        cam.stop_streaming()

此修改将磁盘IO操作从主线程剥离,保证帧读取和显示的稳定性。

3. 优化流缓冲参数

  • 调整buffer_count为3-5:相机流缓冲过多会增加内存占用和延迟,30FPS下3个缓冲帧足够应对临时波动。
  • 缩小frame_queue容量:队列过大可能导致帧积压,设置为5以内可以及时丢弃超期帧,保证处理的是最新帧。

4. 修正帧率计算方式

原代码总帧数除以总时间的方式忽略了实时波动,改用每秒统计一次的方式更准确:

def run_processing(cam):
    try:
        # ... 其他代码 ...
        start = time.time()
        frame_count = 0
        last_stat_time = start
        while True:
            if frame_queue.qsize() > 0:
                try:
                    frame = frame_queue.get_nowait()
                    frame_count += 1
                    cv2.imshow('Live feed', frame)
                    save_queue.put_nowait((frame.copy(), frame_count))
                    
                    # 每1秒统计一次实时帧率
                    current_time = time.time()
                    if current_time - last_stat_time >= 1.0:
                        current_fps = frame_count / (current_time - start)
                        print(f'Current FPS: {round(current_fps, 2)}')
                        last_stat_time = current_time
                except queue.Empty:
                    pass
            # ... 其他代码 ...

额外注意事项

  • 确保曝光时间不超过单帧最大允许值:30FPS对应单帧约33.33ms(33333微秒),当前设置的10000微秒符合要求,后续调整曝光需注意此限制。
  • 保持自动增益、自动曝光等功能关闭:这些功能会导致帧率波动,代码中已设置为关闭状态。
  • 监控系统资源:CPU、磁盘IO占用过高会影响帧率稳定性,可通过系统工具排查瓶颈。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 19:25:19