131 lines
4.6 KiB
Python
131 lines
4.6 KiB
Python
import sys
|
|
import numpy as np
|
|
import cv2
|
|
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout, QPushButton
|
|
from PyQt5.QtGui import QImage, QPixmap
|
|
from PyQt5.QtCore import QThread, pyqtSignal
|
|
from pypylon import pylon
|
|
|
|
class BaslerCameraThread(QThread):
|
|
"""相機影像擷取執行緒"""
|
|
frame_signal = pyqtSignal(np.ndarray) # 發送影像訊號
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.running = True
|
|
self.camera = None
|
|
|
|
def run(self):
|
|
"""背景執行擷取 Basler 相機影像"""
|
|
try:
|
|
# 連接 Basler 相機
|
|
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
|
self.camera.Open()
|
|
print("✅ 相機成功開啟!")
|
|
|
|
# 設定擷取策略
|
|
self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
|
|
|
|
# 設定影像格式轉換
|
|
converter = pylon.ImageFormatConverter()
|
|
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
|
|
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
|
|
|
|
while self.running and self.camera.IsGrabbing():
|
|
grabResult = self.camera.RetrieveResult(500, pylon.TimeoutHandling_Return)
|
|
|
|
if grabResult is None:
|
|
print("⚠️ 無法獲取影像,可能相機未正常運行!")
|
|
elif grabResult.GrabSucceeded():
|
|
image = converter.Convert(grabResult)
|
|
frame = image.GetArray()
|
|
self.frame_signal.emit(frame) # 發送影像訊號
|
|
else:
|
|
print(f"❌ 擷取失敗!錯誤碼: {grabResult.GetErrorCode()}, 訊息: {grabResult.GetErrorDescription()}")
|
|
print("🛠️ 嘗試重新啟動相機...")
|
|
self.restart_camera() # 重新啟動相機
|
|
|
|
grabResult.Release()
|
|
|
|
except Exception as e:
|
|
print(f"🚨 Basler 相機錯誤: {e}")
|
|
|
|
def restart_camera(self):
|
|
"""重新啟動相機"""
|
|
if self.camera and self.camera.IsOpen():
|
|
self.camera.Close()
|
|
print("🔄 相機已關閉,嘗試重新啟動...")
|
|
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
|
self.camera.Open()
|
|
print("✅ 相機重新啟動成功!")
|
|
self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
|
|
|
|
def stop(self):
|
|
"""停止影像擷取"""
|
|
self.running = False
|
|
if self.camera and self.camera.IsOpen():
|
|
self.camera.StopGrabbing()
|
|
self.camera.Close()
|
|
print("🔻 相機已關閉!")
|
|
|
|
|
|
|
|
class BaslerCameraApp(QWidget):
|
|
"""主視窗,顯示 Basler 相機畫面"""
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.initUI()
|
|
self.camera_thread = BaslerCameraThread()
|
|
self.camera_thread.frame_signal.connect(self.update_frame)
|
|
|
|
def initUI(self):
|
|
self.setWindowTitle("Basler Camera Viewer")
|
|
self.setGeometry(100, 100, 800, 600)
|
|
|
|
self.image_label = QLabel(self)
|
|
self.image_label.setFixedSize(800, 600)
|
|
|
|
self.start_button = QPushButton("開始擷取", self)
|
|
self.start_button.clicked.connect(self.start_camera)
|
|
|
|
self.stop_button = QPushButton("停止擷取", self)
|
|
self.stop_button.clicked.connect(self.stop_camera)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.addWidget(self.image_label)
|
|
layout.addWidget(self.start_button)
|
|
layout.addWidget(self.stop_button)
|
|
self.setLayout(layout)
|
|
|
|
def start_camera(self):
|
|
"""開始影像擷取"""
|
|
if not self.camera_thread.isRunning():
|
|
self.camera_thread.running = True
|
|
self.camera_thread.start()
|
|
print("▶️ 影像擷取開始!")
|
|
|
|
def stop_camera(self):
|
|
"""停止影像擷取"""
|
|
self.camera_thread.stop()
|
|
print("⏹️ 影像擷取停止!")
|
|
|
|
def update_frame(self, frame):
|
|
"""更新 UI 影像"""
|
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 轉換顏色格式
|
|
h, w, ch = frame.shape
|
|
bytes_per_line = ch * w
|
|
qt_image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
|
|
pixmap = QPixmap.fromImage(qt_image)
|
|
self.image_label.setPixmap(pixmap)
|
|
|
|
def closeEvent(self, event):
|
|
"""視窗關閉時停止相機"""
|
|
self.camera_thread.stop()
|
|
event.accept()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication(sys.argv)
|
|
window = BaslerCameraApp()
|
|
window.show()
|
|
sys.exit(app.exec_()) |