import sys from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QFileDialog from pypylon import pylon import cv2 import numpy as np import os from Main import Ui_MainWindow 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_open_image.clicked.connect(self.open_image) self.bt_save_image.clicked.connect(self.save_image) # 連接儲存影像按鈕事件 # 初始化變數 self.camera = None self.timer = QTimer() self.timer.timeout.connect(self.keep_shot_process) # 設定 QSlider 範圍和事件 self.threshold_value.setMinimum(0) self.threshold_value.setMaximum(255) self.threshold_value.setValue(127) # 預設閥值 self.threshold_value.valueChanged.connect(self.update_threshold) self.threshold.setText(str(self.threshold_value.value())) self.current_image = None def get_bgr_image(self): """取得 BGR 格式的影像""" if self.current_image is None: return None # 若影像為單通道或 Bayer 格式,進行轉換 if len(self.current_image.shape) == 2 or self.current_image.shape[2] == 1: return cv2.cvtColor(self.current_image, cv2.COLOR_BayerBG2BGR) else: return self.current_image 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() # 當 QTextEdit 值為空時使用預設值 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 try: exposure_time = self.get_exposure_time() self.camera.ExposureTime.SetValue(float(exposure_time)) self.statusbar.showMessage(f"曝光時間設定為 {exposure_time} 微秒") if self.camera.IsGrabbing(): self.camera.StopGrabbing() self.camera.StartGrabbing(1) grab_result = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_Return) if grab_result.GrabSucceeded(): self.current_image = grab_result.Array self.display_original_image() self.apply_threshold_and_display() self.statusbar.showMessage("單張擷取成功") else: self.statusbar.showMessage("擷取失敗") grab_result.Release() except Exception as e: self.statusbar.showMessage(f"擷取失敗: {e}") if self.camera.IsGrabbing(): self.camera.StopGrabbing() def display_original_image(self): """顯示原始影像到 label 上""" if self.current_image is not None: # 判斷是否需要轉換 Bayer 格式 if len(self.current_image.shape) == 2 or self.current_image.shape[2] == 1: # 影像來自相機(Bayer 格式) image_bgr = cv2.cvtColor(self.current_image, cv2.COLOR_BayerBG2BGR) else: # 影像已經是 BGR 格式 image_bgr = 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 apply_threshold_and_display(self): """套用二值化處理並顯示影像到 label_2 上""" if self.current_image is not None: # 判斷是否需要轉換 Bayer 格式 if len(self.current_image.shape) == 2 or self.current_image.shape[2] == 1: # 影像來自相機(Bayer 格式) image_bgr = cv2.cvtColor(self.current_image, cv2.COLOR_BayerBG2BGR) else: # 影像已經是 BGR 格式 image_bgr = self.current_image # 轉換為灰階 image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY) # 套用二值化處理 threshold_value = self.threshold_value.value() _, thresholded_image = cv2.threshold(image_gray, threshold_value, 255, cv2.THRESH_BINARY) # 顯示影像 height, width = thresholded_image.shape bytes_per_line = width qimage = QtGui.QImage(thresholded_image.data, width, height, bytes_per_line, QtGui.QImage.Format_Grayscale8) pixmap = QtGui.QPixmap.fromImage(qimage).scaled(self.label_2.size(), QtCore.Qt.KeepAspectRatio) self.label_2.setPixmap(pixmap) def update_threshold(self): """當 QSlider 值改變時更新影像""" self.apply_threshold_and_display() # 更新 QTextEdit 顯示的值 self.threshold.setText(str(self.threshold_value.value())) def keep_shot_capture(self): if self.camera and self.camera.IsOpen(): if not self.timer.isActive(): self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly) self.timer.start(30) self.statusbar.showMessage("開始連續取像") else: self.timer.stop() self.camera.StopGrabbing() self.statusbar.showMessage("停止連續取像") else: self.statusbar.showMessage("請先連接相機") def keep_shot_process(self): if self.camera and self.camera.IsGrabbing(): grab_result = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException) if grab_result.GrabSucceeded(): self.current_image = grab_result.Array self.display_original_image() self.apply_threshold_and_display() grab_result.Release() def open_image(self): options = QFileDialog.Options() file_path, _ = QFileDialog.getOpenFileName(self, "打開影像檔", "", "Images (*.png *.xpm *.jpg *.bmp *.tiff)", options=options) if file_path: if not os.path.exists(file_path): self.statusbar.showMessage("檔案不存在或路徑錯誤") return image_data = np.fromfile(file_path, dtype=np.uint8) image = cv2.imdecode(image_data, cv2.IMREAD_COLOR) if image is not None: self.current_image = image self.display_original_image() self.apply_threshold_and_display() self.statusbar.showMessage("影像載入成功") else: self.statusbar.showMessage("無法讀取影像") def save_image(self): """將二值化影像儲存到檔案""" if self.current_image is None: self.statusbar.showMessage("沒有影像可儲存") return # 使用 QFileDialog 取得儲存檔案路徑和名稱 file_path, _ = QFileDialog.getSaveFileName(self, "儲存二值化影像", "", "Images (*.png *.jpg *.bmp *.tiff)") if file_path: try: # 取得 BGR 影像並生成二值化影像 image_bgr = self.get_bgr_image() if image_bgr is None: self.statusbar.showMessage("無法取得影像") return image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY) threshold_value = self.threshold_value.value() _, thresholded_image = cv2.threshold(image_gray, threshold_value, 255, cv2.THRESH_BINARY) # 儲存影像 cv2.imwrite(file_path, thresholded_image) self.statusbar.showMessage(f"二值化影像成功儲存到 {file_path}") except Exception as e: self.statusbar.showMessage(f"影像儲存失敗: {e}") if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = CameraApp() window.show() sys.exit(app.exec_())