109 lines
3.5 KiB
Python
109 lines
3.5 KiB
Python
import threading
|
||
import queue
|
||
import cv2
|
||
from pypylon import pylon
|
||
from datetime import datetime
|
||
import os
|
||
|
||
# =================== 配置 ===================
|
||
SAVE_DIR = "camera/continuous"
|
||
SAVE_EVERY_N_FRAMES = 1 # 每N帧保存一张,None表示不保存
|
||
SHOW = True
|
||
QUEUE_MAXSIZE = 50
|
||
|
||
# =================== 相机搜索 ===================
|
||
def search_get_device():
|
||
"""搜索并返回 Basler GigE 相机对象"""
|
||
tl_factory = pylon.TlFactory.GetInstance()
|
||
for dev_info in tl_factory.EnumerateDevices():
|
||
if dev_info.GetDeviceClass() == 'BaslerGigE':
|
||
print(f"Found Basler GigE Camera: {dev_info.GetModelName()} IP: {dev_info.GetIpAddress()}")
|
||
return pylon.InstantCamera(tl_factory.CreateDevice(dev_info))
|
||
raise EnvironmentError("没有找到 Basler GigE 相机")
|
||
|
||
# =================== 异步处理线程 ===================
|
||
frame_queue = queue.Queue(maxsize=QUEUE_MAXSIZE)
|
||
exit_event = threading.Event()
|
||
|
||
def worker(save_dir, save_every_n_frames, show):
|
||
frame_count = 0
|
||
while not exit_event.is_set():
|
||
try:
|
||
item = frame_queue.get(timeout=0.1) # 队列空时短暂等待
|
||
except queue.Empty:
|
||
continue
|
||
|
||
if item is None:
|
||
break
|
||
|
||
img, frame_num = item
|
||
frame_count += 1
|
||
|
||
# 显示图像
|
||
if show:
|
||
cv2.imshow("Basler Live Feed", img)
|
||
if cv2.waitKey(1) & 0xFF in (ord('q'), 27):
|
||
exit_event.set()
|
||
break
|
||
|
||
# 保存图像
|
||
if save_every_n_frames and frame_num % save_every_n_frames == 0:
|
||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3]
|
||
filename = os.path.join(save_dir, f"frame_{frame_num:06d}_{timestamp}.png")
|
||
cv2.imwrite(filename, img)
|
||
print(f"[Saved] {filename}")
|
||
|
||
if show:
|
||
cv2.destroyAllWindows()
|
||
|
||
# =================== 主采集函数 ===================
|
||
def continuous_grab(save_dir=SAVE_DIR, show=SHOW, save_every_n_frames=SAVE_EVERY_N_FRAMES):
|
||
os.makedirs(save_dir, exist_ok=True)
|
||
cam = search_get_device()
|
||
cam.Open()
|
||
# Load the User Set 1 user set
|
||
cam.UserSetSelector.Value = "UserSet1"
|
||
cam.UserSetLoad.Execute()
|
||
cam.MaxNumBuffer = 30
|
||
|
||
converter = pylon.ImageFormatConverter()
|
||
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
|
||
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
|
||
|
||
# 启动异步处理线程
|
||
t = threading.Thread(target=worker, args=(save_dir, save_every_n_frames, show), daemon=True)
|
||
t.start()
|
||
|
||
cam.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
|
||
frame_count = 0
|
||
try:
|
||
while cam.IsGrabbing() and not exit_event.is_set():
|
||
grab_result = cam.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
|
||
if grab_result.GrabSucceeded():
|
||
image = converter.Convert(grab_result)
|
||
img = image.GetArray()
|
||
frame_count += 1
|
||
|
||
# 拷贝一份到队列
|
||
if not frame_queue.full():
|
||
frame_queue.put((img.copy(), frame_count))
|
||
grab_result.Release()
|
||
|
||
except KeyboardInterrupt:
|
||
print("被 Ctrl+C 中断")
|
||
cam.StopGrabbing()
|
||
cam.Close()
|
||
exit_event.set()
|
||
|
||
finally:
|
||
cam.StopGrabbing()
|
||
cam.Close()
|
||
# 通知线程退出
|
||
frame_queue.put(None)
|
||
t.join()
|
||
print(f"共采集 {frame_count} 帧")
|
||
|
||
# =================== 主入口 ===================
|
||
if __name__ == "__main__":
|
||
continuous_grab()
|