diff --git a/Detection_window.py b/Detection_window.py index d9adb6b..3f602d7 100644 --- a/Detection_window.py +++ b/Detection_window.py @@ -14,44 +14,44 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(2024, 1118) + MainWindow.resize(2182, 1118) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.bt_KeepShot = QtWidgets.QPushButton(self.centralwidget) - self.bt_KeepShot.setMinimumSize(QtCore.QSize(100, 50)) - self.bt_KeepShot.setMaximumSize(QtCore.QSize(100, 50)) - self.bt_KeepShot.setObjectName("bt_KeepShot") - self.horizontalLayout.addWidget(self.bt_KeepShot) - self.bt_StopKeepShot = QtWidgets.QPushButton(self.centralwidget) - self.bt_StopKeepShot.setMinimumSize(QtCore.QSize(100, 50)) - self.bt_StopKeepShot.setMaximumSize(QtCore.QSize(100, 50)) - self.bt_StopKeepShot.setObjectName("bt_StopKeepShot") - self.horizontalLayout.addWidget(self.bt_StopKeepShot) - self.bt_detection = QtWidgets.QPushButton(self.centralwidget) - self.bt_detection.setMinimumSize(QtCore.QSize(100, 50)) - self.bt_detection.setMaximumSize(QtCore.QSize(100, 50)) - self.bt_detection.setObjectName("bt_detection") - self.horizontalLayout.addWidget(self.bt_detection) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) self.view_origin = QtWidgets.QLabel(self.centralwidget) self.view_origin.setMinimumSize(QtCore.QSize(1000, 1000)) self.view_origin.setMaximumSize(QtCore.QSize(1000, 1000)) self.view_origin.setObjectName("view_origin") - self.gridLayout.addWidget(self.view_origin, 1, 0, 1, 1) + self.gridLayout.addWidget(self.view_origin, 2, 0, 1, 1) self.view_predict = QtWidgets.QLabel(self.centralwidget) self.view_predict.setMinimumSize(QtCore.QSize(1000, 1000)) self.view_predict.setMaximumSize(QtCore.QSize(1000, 1000)) self.view_predict.setObjectName("view_predict") - self.gridLayout.addWidget(self.view_predict, 1, 1, 1, 1) + self.gridLayout.addWidget(self.view_predict, 2, 1, 1, 1) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.bt_KeepShot = QtWidgets.QPushButton(self.centralwidget) + self.bt_KeepShot.setMinimumSize(QtCore.QSize(150, 50)) + self.bt_KeepShot.setMaximumSize(QtCore.QSize(100, 50)) + self.bt_KeepShot.setObjectName("bt_KeepShot") + self.verticalLayout.addWidget(self.bt_KeepShot) + self.bt_StopKeepShot = QtWidgets.QPushButton(self.centralwidget) + self.bt_StopKeepShot.setMinimumSize(QtCore.QSize(150, 50)) + self.bt_StopKeepShot.setMaximumSize(QtCore.QSize(100, 50)) + self.bt_StopKeepShot.setObjectName("bt_StopKeepShot") + self.verticalLayout.addWidget(self.bt_StopKeepShot) + self.bt_detection = QtWidgets.QPushButton(self.centralwidget) + self.bt_detection.setMinimumSize(QtCore.QSize(150, 50)) + self.bt_detection.setMaximumSize(QtCore.QSize(100, 50)) + self.bt_detection.setObjectName("bt_detection") + self.verticalLayout.addWidget(self.bt_detection) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.gridLayout.addLayout(self.verticalLayout, 2, 2, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 2024, 22)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 2182, 22)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) @@ -64,8 +64,8 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.view_origin.setText(_translate("MainWindow", "原圖影像")) + self.view_predict.setText(_translate("MainWindow", "檢測影像")) self.bt_KeepShot.setText(_translate("MainWindow", "開始連續取像")) self.bt_StopKeepShot.setText(_translate("MainWindow", "停止連續取像")) self.bt_detection.setText(_translate("MainWindow", "檢測")) - self.view_origin.setText(_translate("MainWindow", "原圖影像")) - self.view_predict.setText(_translate("MainWindow", "檢測影像")) diff --git a/Detection_window.ui b/Detection_window.ui index abfef76..06db60e 100644 --- a/Detection_window.ui +++ b/Detection_window.ui @@ -6,7 +6,7 @@ 0 0 - 2024 + 2182 1118 @@ -15,81 +15,7 @@ - - - - - - - 100 - 50 - - - - - 100 - 50 - - - - 開始連續取像 - - - - - - - - 100 - 50 - - - - - 100 - 50 - - - - 停止連續取像 - - - - - - - - 100 - 50 - - - - - 100 - 50 - - - - 檢測 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + @@ -108,7 +34,7 @@ - + @@ -127,6 +53,80 @@ + + + + + + + 150 + 50 + + + + + 100 + 50 + + + + 開始連續取像 + + + + + + + + 150 + 50 + + + + + 100 + 50 + + + + 停止連續取像 + + + + + + + + 150 + 50 + + + + + 100 + 50 + + + + 檢測 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + @@ -134,7 +134,7 @@ 0 0 - 2024 + 2182 22 diff --git a/camera/camera_process.py b/camera/camera_process.py index 3fe5468..ee68ce4 100644 --- a/camera/camera_process.py +++ b/camera/camera_process.py @@ -4,12 +4,15 @@ import cv2 import multiprocessing from pypylon import pylon +from read_ini import ConfigReader + class CameraProcess(multiprocessing.Process): """ 相機擷取獨立進程 """ def __init__(self, image_queue, exposure_time=20000): super(CameraProcess, self).__init__() self.image_queue = image_queue - self.exposure_time = exposure_time + self.config_reader = ConfigReader() # ✅ 創建 `ConfigReader` 物件 + self.exposure_time = exposure_time if exposure_time is not None else self.config_reader.get_exposure_time() self.running = multiprocessing.Value('b', True) # 控制進程是否運行 self.camera = None @@ -19,7 +22,7 @@ class CameraProcess(multiprocessing.Process): self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice()) self.camera.Open() self.camera.ExposureTime.SetValue(float(self.exposure_time)) - print("相機連線成功") + print(f"相機連線成功,曝光時間: {self.exposure_time} 微秒") return True except Exception as e: print(f"相機連線失敗: {e}") @@ -33,7 +36,7 @@ class CameraProcess(multiprocessing.Process): self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly) while self.running.value and self.camera.IsGrabbing(): - grab_result = self.camera.RetrieveResult(20000, pylon.TimeoutHandling_ThrowException) + grab_result = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException) if grab_result.GrabSucceeded(): image = grab_result.Array # 取得影像數據 if not self.image_queue.full(): @@ -46,7 +49,6 @@ class CameraProcess(multiprocessing.Process): def stop(self): """ 停止相機擷取進程 """ - print("正在停止相機擷取...") self.running.value = False # 設定為 False,讓 while 迴圈停止 self.terminate() # 強制終止進程 print("相機擷取進程已終止") diff --git a/detection.py b/detection.py index 3b47da2..ef9bc9d 100644 --- a/detection.py +++ b/detection.py @@ -4,18 +4,29 @@ import numpy as np import multiprocessing from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import QTimer + from Detection_window import Ui_MainWindow from camera.camera_process import CameraProcess +from read_ini import ConfigReader +from log_handler import LogHandler class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self): super(DetectionApp, self).__init__() self.setupUi(self) + self.log_handler = LogHandler() # ✅ 建立 LogHandler 物件 + self.log_handler.write_log("程式啟動") # ✅ 寫入 log: 程式啟動 + + # ✅ 讀取 `exposure_time` + self.config_reader = ConfigReader() + self.exposure_time = self.config_reader.get_exposure_time() + # ✅ 先初始化 image_queue,再啟動相機擷取 self.image_queue = multiprocessing.Queue(maxsize=1) self.camera_process = None # 相機進程尚未啟動 + # ✅ 連接按鈕事件 self.bt_KeepShot.clicked.connect(self.KeepShot) self.bt_StopKeepShot.clicked.connect(self.StopKeepShot) @@ -32,9 +43,11 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow): # ✅ 重新建立 Queue,避免殘留影像影響新擷取 self.image_queue = multiprocessing.Queue(maxsize=1) - # ✅ 確保 `CameraProcess` 是新的 - self.camera_process = CameraProcess(self.image_queue) + # ✅ 使用 `exposure_time` 啟動相機 + self.camera_process = CameraProcess(self.image_queue, exposure_time=self.exposure_time) self.camera_process.start() + self.log_handler.write_log("相機啟動") # ✅ 寫入 log: 相機啟動 + self.statusbar.showMessage(f"開始擷取影像 (曝光時間: {self.exposure_time} 微秒)") else: print("相機已經在擷取") @@ -45,6 +58,7 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow): self.camera_process.join() # 等待進程完全結束 self.camera_process = None print("已停止影像擷取") + self.log_handler.write_log("相機停止") # ✅ 寫入 log: 相機停止 # ✅ 清空 Queue,確保新擷取不會讀取到舊影像 while not self.image_queue.empty(): @@ -69,12 +83,20 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow): self.view_origin.setPixmap(pixmap) def closeEvent(self, event): - """ 確保程式關閉時正確停止相機擷取進程 """ - self.StopKeepShot() # 確保關閉程式時停止相機擷取 - event.accept() # ✅ 允許視窗關閉 + reply = QtWidgets.QMessageBox.question( + self, "確認", "確定要關閉應用程式嗎?", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No + ) + if reply == QtWidgets.QMessageBox.Yes: + self.StopKeepShot() # 停止相機 + self.log_handler.write_log("程式關閉") # ✅ 寫入 log: 程式關閉 + event.accept() # 允許視窗關閉 + else: + event.ignore() # 阻止視窗關閉 if __name__ == "__main__": + multiprocessing.freeze_support() # 在 Windows 上執行 multiprocessing 需要這行 app = QtWidgets.QApplication(sys.argv) window = DetectionApp() window.show() diff --git a/log_handler.py b/log_handler.py new file mode 100644 index 0000000..e46890a --- /dev/null +++ b/log_handler.py @@ -0,0 +1,34 @@ +import os +import datetime + +class LogHandler: + """ 日誌處理類別,負責寫入 log 檔案 """ + + def __init__(self, log_dir="logging"): + """ 初始化 log 系統 """ + self.log_dir = log_dir + self.ensure_log_directory_exists() + self.log_file = self.get_log_filename() + + def ensure_log_directory_exists(self): + """ 確保 logging 資料夾存在,如果不存在則建立 """ + if not os.path.exists(self.log_dir): + os.makedirs(self.log_dir) + + def get_log_filename(self): + """ 取得今日的 log 檔案名稱(YYYY-MM-DD.txt) """ + today = datetime.datetime.now().strftime("%Y-%m-%d") + return os.path.join(self.log_dir, f"{today}.txt") + + def write_log(self, event): + """ 寫入 log 訊息 """ + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + log_message = f"[{timestamp}] {event}\n" + + # 確保 log 檔案是最新的(每天更新) + self.log_file = self.get_log_filename() + + with open(self.log_file, "a", encoding="utf-8") as file: + file.write(log_message) + + print(f"📄 [LOG] {log_message.strip()}") # 也在 console 顯示 log diff --git a/logging/2025-03-10.txt b/logging/2025-03-10.txt new file mode 100644 index 0000000..96eda15 --- /dev/null +++ b/logging/2025-03-10.txt @@ -0,0 +1,18 @@ +[2025-03-10 17:50:57] 程式啟動 +[2025-03-10 17:51:04] 相機啟動 +[2025-03-10 17:51:22] 相機停止 +[2025-03-10 17:51:33] 相機啟動 +[2025-03-10 17:51:57] 相機停止 +[2025-03-10 17:51:58] 相機啟動 +[2025-03-10 17:52:04] 相機停止 +[2025-03-10 17:52:13] 程式關閉 +[2025-03-10 21:02:35] 程式啟動 +[2025-03-10 21:02:44] 相機啟動 +[2025-03-10 21:03:24] 相機停止 +[2025-03-10 21:03:27] 程式關閉 +[2025-03-10 21:04:15] 程式啟動 +[2025-03-10 21:04:18] 相機啟動 +[2025-03-10 21:04:29] 相機停止 +[2025-03-10 21:04:32] 相機啟動 +[2025-03-10 21:04:37] 相機停止 +[2025-03-10 21:04:44] 程式關閉 diff --git a/model/best.pt b/model/best.pt new file mode 100644 index 0000000..b614e34 Binary files /dev/null and b/model/best.pt differ diff --git a/parameter.ini b/parameter.ini index fa4475a..07fd03b 100644 --- a/parameter.ini +++ b/parameter.ini @@ -1,2 +1,2 @@ [Camera_Setting] -exposuretime_edge = 18122 +exposuretime = 30000 diff --git a/read_ini.py b/read_ini.py new file mode 100644 index 0000000..26546eb --- /dev/null +++ b/read_ini.py @@ -0,0 +1,25 @@ +import configparser + + +class ConfigReader: + """ 用於讀取 parameter.ini 設定檔的類別 """ + + def __init__(self, ini_file="parameter.ini"): + """ 初始化並讀取 ini 檔案 """ + self.ini_file = ini_file + self.config = configparser.ConfigParser() + self.read_config() + + def read_config(self): + """ 讀取 ini 檔案內容 """ + self.config.read(self.ini_file) + + def get_exposure_time(self): + """ 取得曝光時間,如果讀取失敗則回傳預設值 5000 """ + try: + exposure_time = int(self.config["Camera_Setting"]["exposuretime"]) + print(f"讀取曝光時間: {exposure_time} 微秒") + return exposure_time + except Exception as e: + print(f"讀取 `parameter.ini` 失敗: {e}") + return 5000 # 預設曝光時間