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_())