如何使用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
相关产品推荐
相关产品推荐

