執行緒與並行處理#
此視覺化圖表說明影像擷取如何在 Python 應用程式中運作。
- 影像擷取在 pylon 內部的 原生 (C++) 執行緒 中執行。
- 您的 Python 程式碼從 緩衝區佇列 擷取影像。
- 這種分離機制實現了非同步擷取。
在許多應用程式中,影像擷取速度快於處理速度:
若無並行處理:
- 緩衝區 Fill 滿。
- 影格遺失。
- 延遲增加。
基礎執行模型#
單執行緒方法是影像擷取管線最簡單的形式。
範例:
while camera.IsGrabbing():
with camera.RetrieveResult(5000) as grab_result:
if grab_result.GrabSucceeded():
image = grab_result.Array
process(image)
這具有下列缺點:
- 處理會阻塞擷取。
- CPU 利用率不佳
- 可擴充性受限
生產者–消費者模式(建議)#
這是影像擷取管線更為精密的方法。
- Producer:擷取執行緒會擷取影像並將其推送至佇列中。
- Consumer:工作執行緒會獨立處理影像。
使用自訂執行緒的範例實作#
資訊
您可以使用 InstantCamera 提供的 grab loop 執行緒(請參閱 grab_using_grab_loop_thread 範例)。這是僅供示範用途的自訂實作。
import threading
import queue
from pypylon import pylon
def process(image):
print(image.shape)
image_queue = queue.Queue(maxsize=10)
# Producer thread
def grab_loop(camera):
while camera.IsGrabbing():
with camera.RetrieveResult(5000) as grab_result:
if grab_result.GrabSucceeded():
image = grab_result.Array
image_queue.put(image)
# Consumer thread
def process_loop():
while True:
image = image_queue.get()
process(image)
image_queue.task_done()
with pylon.InstantCamera(pylon.FirstFound) as camera:
camera.StartGrabbing()
grab_thread = threading.Thread(target=grab_loop, args=(camera,))
processing_thread = threading.Thread(target=process_loop)
grab_thread.start()
processing_thread.start()
grab_thread.join()
image_queue.join()
佇列行為與背壓 (Backpressure)#
策略:
- 增加佇列大小。
- 手動捨棄影格。
- 使用
LatestImageOnly.
與 Grab Strategies 結合#
LatestImageOnly:減少積壓。OneByOne:確保完整性。
最佳實務:
執行緒安全性考量#
- 避免在沒有鎖定的情況下共用可變資料。
- 在將影像傳遞給其他執行緒之前先進行複製,例如透過使用
Array函式搭配使用。 - 使用
queue.Queue以進行安全的通訊。
CPU 利用率#
平行處理具有以下優點:
- 更好的 CPU 使用率
- 關注點分離
- 可擴充的架構
何時使用多執行緒#
在以下情況下使用執行緒:
- 處理速度慢於擷取速度。
- 存在多個處理階段。
- 需要即時回應性。
在以下情況下避免使用執行緒:
- 處理程序微不足道。
- 系統複雜度必須降到最低。
進階模式(概觀)#
多階段管線#
多階段管線將影像處理分割為獨立的步驟,這些步驟可同時執行並透過佇列交換資料。
多消費者設定#
概念管線#
重點整理#
- 擷取與處理應該相互解耦。
- 使用生產者-消費者模式以實現可擴展性。
- 佇列提供執行緒安全的通訊。
- 多執行緒能提升效能,但也會增加複雜度。