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

如何用Python+Pypylon实现Basler相机图像并行存储且不影响帧率?

Hey there! Let's solve your Basler camera image capture problem where you need to save 1000/2000 images at specified exposure/shutter speeds without killing your frame rate. The key is to decouple image capture from disk writing—here's a practical, tested approach using Pypylon with threading and a queue:

Core Strategy

Disk I/O is way slower than camera capture, so we'll use:

  • A main thread dedicated to grabbing images from the camera (no delays here)
  • A background thread that handles saving images to disk from a thread-safe queue
  • Optional in-memory caching if you need to handle sudden bursts of frames
Optimized Code Implementation
import pypylon.pylon as py
import threading
from queue import Queue
import cv2
import os

def save_worker(queue, save_dir):
    """Background thread to save images without blocking capture"""
    os.makedirs(save_dir, exist_ok=True)
    while True:
        img_data, img_idx = queue.get()
        # Save the image (cv2 is fast for numpy array formats)
        cv2.imwrite(f"{save_dir}/frame_{img_idx:04d}.png", img_data)
        queue.task_done()

def capture_images(num_frames, exposure_time_us, save_dir):
    # Initialize camera
    tl_factory = py.TlFactory.GetInstance()
    devices = tl_factory.EnumerateDevices()
    if not devices:
        raise RuntimeError("No Basler cameras found!")
    
    camera = py.InstantCamera(tl_factory.CreateDevice(devices[0]))
    camera.Open()

    # Set fixed exposure and disable auto-controls to avoid overhead
    camera.ExposureTime.SetValue(exposure_time_us)
    camera.ExposureAuto.SetValue("Off")
    camera.GainAuto.SetValue("Off")
    camera.WhiteBalanceAuto.SetValue("Off")

    # Create a thread-safe queue (limit size to prevent memory overload)
    img_queue = Queue(maxsize=50)

    # Start background save thread (daemon=True exits with main thread)
    save_thread = threading.Thread(target=save_worker, args=(img_queue, save_dir), daemon=True)
    save_thread.start()

    try:
        # Use LatestImageOnly to prioritize real-time capture over every frame
        camera.StartGrabbing(py.GrabStrategy_LatestImageOnly)
        converter = py.ImageFormatConverter()
        converter.OutputPixelFormat = py.PixelType_BGR8packed  # OpenCV-friendly format
        converter.OutputBitAlignment = py.OutputBitAlignment_MsbAligned

        for idx in range(num_frames):
            if not camera.IsGrabbing():
                break
            
            grab_result = camera.RetrieveResult(5000, py.TimeoutHandling_ThrowException)
            if grab_result.GrabSucceeded():
                # Convert to numpy array (minimal overhead)
                img = converter.Convert(grab_result)
                img_np = img.GetArray()
                
                # Add to queue (non-blocking if queue has space)
                img_queue.put((img_np, idx))
                
                # Lightweight progress update
                if idx % 100 == 0:
                    print(f"Captured frame {idx}/{num_frames}")
            
            grab_result.Release()
        
        # Wait for all queued images to be saved
        img_queue.join()
        print(f"All {num_frames} frames saved successfully!")

    finally:
        camera.StopGrabbing()
        camera.Close()

# Example usage
if __name__ == "__main__":
    capture_images(
        num_frames=1000,
        exposure_time_us=10000,  # 10ms exposure (adjust to your needs)
        save_dir="./basler_captures"
    )
Key Tips to Preserve Frame Rate
  • Thread over Process: Threads share memory, so passing images to the save worker avoids expensive data copying (processes would require pickling/unpickling, which kills speed).
  • Queue Size Limit: The maxsize=50 prevents RAM overload if disk writing lags temporarily. If the queue fills up, the capture thread blocks briefly—better than losing frames or crashing.
  • Grab Strategy: Use GrabStrategy_LatestImageOnly if you prioritize real-time capture over capturing every single frame. If you need every frame, switch to GrabStrategy_OneByOne but ensure your save thread keeps up.
  • Optimize Saving: Use lossless PNG for accuracy, or high-quality JPEG if compression is acceptable. Avoid uncompressed BMP—it's slow and uses excessive disk space.
  • Pre-Set All Camera Params: Disable all auto-controls before capturing—auto-adjustments add camera processing delay and frame rate fluctuations.
Batch Caching Alternative (For Very Slow Disks)

If your storage is extremely slow, modify the save worker to cache batches of images before writing to reduce disk I/O operations:

def save_worker_batch(queue, save_dir, batch_size=20):
    os.makedirs(save_dir, exist_ok=True)
    batch = []
    while True:
        img_data, img_idx = queue.get()
        batch.append((img_data, img_idx))
        
        if len(batch) >= batch_size:
            # Write entire batch to disk
            for img, idx in batch:
                cv2.imwrite(f"{save_dir}/frame_{idx:04d}.png", img)
            batch = []
        queue.task_done()
        
        # Clean up remaining images when queue is empty
        if queue.empty() and batch:
            for img, idx in batch:
                cv2.imwrite(f"{save_dir}/frame_{idx:04d}.png", img)
            batch = []

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:46:17