166 lines
5.9 KiB
Python
166 lines
5.9 KiB
Python
|
import sys
|
||
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||
|
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
|
||
|
from PyQt5.QtWidgets import QFileDialog
|
||
|
from pypylon import pylon
|
||
|
import cv2
|
||
|
import numpy as np
|
||
|
import os
|
||
|
from Main import Ui_MainWindow
|
||
|
|
||
|
class CameraThread(QThread):
|
||
|
update_image_signal = pyqtSignal(np.ndarray) # 訊號:傳遞影像數據
|
||
|
|
||
|
def __init__(self, camera, exposure_time=5000, continuous=False):
|
||
|
super().__init__()
|
||
|
self.camera = camera
|
||
|
self.exposure_time = exposure_time
|
||
|
self.continuous = continuous
|
||
|
self.running = True # 控制執行狀態
|
||
|
|
||
|
def run(self):
|
||
|
"""影像擷取執行緒"""
|
||
|
try:
|
||
|
if not self.camera.IsOpen():
|
||
|
return
|
||
|
|
||
|
# 設定曝光時間
|
||
|
self.camera.ExposureTime.SetValue(float(self.exposure_time))
|
||
|
|
||
|
if not self.continuous: # 單張擷取
|
||
|
self.camera.StartGrabbing(1)
|
||
|
else: # 連續擷取
|
||
|
self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
|
||
|
|
||
|
while self.running and self.camera.IsGrabbing():
|
||
|
grab_result = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
|
||
|
if grab_result.GrabSucceeded():
|
||
|
self.update_image_signal.emit(grab_result.Array) # 發送影像到 GUI
|
||
|
grab_result.Release()
|
||
|
|
||
|
if not self.continuous: # 若是單張擷取則停止
|
||
|
break
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f"影像擷取錯誤: {e}")
|
||
|
|
||
|
def stop(self):
|
||
|
"""停止執行緒"""
|
||
|
self.running = False
|
||
|
if self.camera.IsGrabbing():
|
||
|
self.camera.StopGrabbing()
|
||
|
self.quit()
|
||
|
self.wait() # 等待執行緒完全停止
|
||
|
|
||
|
|
||
|
class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||
|
def __init__(self):
|
||
|
super(CameraApp, self).__init__()
|
||
|
self.setupUi(self)
|
||
|
|
||
|
# 連接按鈕事件
|
||
|
self.bt_camera_connect.clicked.connect(self.connect_camera)
|
||
|
self.bt_OneShot.clicked.connect(self.one_shot_capture)
|
||
|
self.bt_KeetShot.clicked.connect(self.keep_shot_capture)
|
||
|
self.bt_camera_close.clicked.connect(self.close_camera)
|
||
|
|
||
|
# 初始化變數
|
||
|
self.camera = None
|
||
|
self.camera_thread = None
|
||
|
self.current_image = None
|
||
|
|
||
|
def connect_camera(self):
|
||
|
"""連接相機"""
|
||
|
try:
|
||
|
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
||
|
self.camera.Open()
|
||
|
self.statusbar.showMessage("相機連線成功")
|
||
|
except Exception as e:
|
||
|
self.statusbar.showMessage(f"相機連線失敗: {e}")
|
||
|
|
||
|
def get_exposure_time(self):
|
||
|
"""從 QTextEdit 取得曝光時間,若為空則使用預設值"""
|
||
|
exposure_time_text = self.Ex_time.toPlainText().strip()
|
||
|
|
||
|
if not exposure_time_text:
|
||
|
return 5000 # 預設曝光時間(微秒)
|
||
|
|
||
|
if exposure_time_text.isdigit():
|
||
|
return int(exposure_time_text)
|
||
|
|
||
|
self.statusbar.showMessage("請輸入有效的曝光時間(整數),使用預設值 5000 微秒")
|
||
|
return 5000
|
||
|
|
||
|
def one_shot_capture(self):
|
||
|
"""單張擷取"""
|
||
|
if not (self.camera and self.camera.IsOpen()):
|
||
|
self.statusbar.showMessage("請先連接相機")
|
||
|
return
|
||
|
|
||
|
# 停止連續擷取
|
||
|
self.stop_keep_shot()
|
||
|
|
||
|
exposure_time = self.get_exposure_time()
|
||
|
self.camera_thread = CameraThread(self.camera, exposure_time, continuous=False)
|
||
|
self.camera_thread.update_image_signal.connect(self.process_image)
|
||
|
self.camera_thread.start()
|
||
|
|
||
|
def keep_shot_capture(self):
|
||
|
"""連續擷取"""
|
||
|
if not (self.camera and self.camera.IsOpen()):
|
||
|
self.statusbar.showMessage("請先連接相機")
|
||
|
return
|
||
|
|
||
|
# 確保 self.camera_thread 存在且正在運行
|
||
|
if self.camera_thread and self.camera_thread.isRunning():
|
||
|
self.stop_keep_shot()
|
||
|
self.statusbar.showMessage("停止連續取像")
|
||
|
else:
|
||
|
exposure_time = self.get_exposure_time()
|
||
|
self.camera_thread = CameraThread(self.camera, exposure_time, continuous=True)
|
||
|
self.camera_thread.update_image_signal.connect(self.process_image)
|
||
|
self.camera_thread.start()
|
||
|
self.statusbar.showMessage("開始連續取像")
|
||
|
|
||
|
def stop_keep_shot(self):
|
||
|
"""停止連續擷取"""
|
||
|
if self.camera_thread:
|
||
|
self.camera_thread.stop()
|
||
|
self.camera_thread.wait() # 確保執行緒完全終止
|
||
|
self.camera_thread = None
|
||
|
|
||
|
def process_image(self, image):
|
||
|
"""處理擷取的影像"""
|
||
|
self.current_image = image
|
||
|
self.display_original_image()
|
||
|
|
||
|
def display_original_image(self):
|
||
|
"""顯示原始影像"""
|
||
|
if self.current_image is not None:
|
||
|
image_bgr = cv2.cvtColor(self.current_image, cv2.COLOR_BayerBG2BGR) if len(self.current_image.shape) == 2 else self.current_image
|
||
|
height, width, channel = image_bgr.shape
|
||
|
bytes_per_line = 3 * width
|
||
|
qimage = QtGui.QImage(image_bgr.data, width, height, bytes_per_line, QtGui.QImage.Format_BGR888)
|
||
|
pixmap = QtGui.QPixmap.fromImage(qimage).scaled(self.label.size(), QtCore.Qt.KeepAspectRatio)
|
||
|
self.label.setPixmap(pixmap)
|
||
|
|
||
|
def close_camera(self):
|
||
|
"""關閉相機"""
|
||
|
if self.camera and self.camera.IsOpen():
|
||
|
self.stop_keep_shot() # 確保先停止擷取
|
||
|
self.camera.Close()
|
||
|
self.statusbar.showMessage("相機已關閉")
|
||
|
self.camera = None
|
||
|
|
||
|
def closeEvent(self, event):
|
||
|
"""關閉程式時停止擷取執行緒"""
|
||
|
self.stop_keep_shot()
|
||
|
event.accept()
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
app = QtWidgets.QApplication(sys.argv)
|
||
|
window = CameraApp()
|
||
|
window.show()
|
||
|
sys.exit(app.exec_())
|