AP/camera.py
2025-02-05 13:32:04 +08:00

228 lines
9.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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