修改整體架構
This commit is contained in:
parent
cea99ab198
commit
7eeba44dfe
2
.idea/.name
generated
2
.idea/.name
generated
@ -1 +1 @@
|
||||
test.py
|
||||
detection.py
|
54
Detection_window.py
Normal file
54
Detection_window.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'Detection_window.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.6
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(1308, 865)
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
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.gridLayout.addWidget(self.bt_detection, 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.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)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1308, 22))
|
||||
self.menubar.setObjectName("menubar")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName("statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
||||
self.bt_detection.setText(_translate("MainWindow", "檢測"))
|
||||
self.view_origin.setText(_translate("MainWindow", "原圖影像"))
|
||||
self.view_predict.setText(_translate("MainWindow", "檢測影像"))
|
91
Detection_window.ui
Normal file
91
Detection_window.ui
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1308</width>
|
||||
<height>865</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="bt_detection">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>檢測</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="view_origin">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1000</width>
|
||||
<height>1000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1000</width>
|
||||
<height>1000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>原圖影像</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="view_predict">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1000</width>
|
||||
<height>1000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1000</width>
|
||||
<height>1000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>檢測影像</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1308</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -7,7 +7,6 @@ import cv2
|
||||
import numpy as np
|
||||
from camera_window import Ui_MainWindow
|
||||
|
||||
|
||||
class CameraThread(QThread):
|
||||
update_image_signal = pyqtSignal(np.ndarray) # 訊號:傳遞影像數據
|
||||
|
||||
@ -45,9 +44,6 @@ class CameraThread(QThread):
|
||||
except Exception as e:
|
||||
print(f"影像擷取錯誤: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"影像擷取錯誤: {e}")
|
||||
|
||||
def stop(self):
|
||||
"""停止執行緒"""
|
||||
self.running = False
|
||||
@ -57,9 +53,9 @@ class CameraThread(QThread):
|
||||
self.wait() # 確保執行緒完全停止
|
||||
|
||||
|
||||
class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
class Camera(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self):
|
||||
super(CameraApp, self).__init__()
|
||||
super(Camera, self).__init__()
|
||||
self.setupUi(self)
|
||||
|
||||
# 連接按鈕事件
|
||||
@ -76,6 +72,7 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self.camera = None
|
||||
self.camera_thread = None
|
||||
self.current_image = None
|
||||
self.latest_image = None # 用於存儲最新影像
|
||||
self.last_exposure_time = None # 記錄最後的曝光時間,避免重複更新
|
||||
|
||||
def connect_camera(self):
|
||||
@ -83,9 +80,11 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
try:
|
||||
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
||||
self.camera.Open()
|
||||
self.statusbar.showMessage("相機連線成功")
|
||||
print("相機連線成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.statusbar.showMessage(f"相機連線失敗: {e}")
|
||||
print(f"相機連線失敗: {e}")
|
||||
return False
|
||||
|
||||
def get_exposure_time(self):
|
||||
"""取得曝光時間,若為空則使用預設值"""
|
||||
@ -102,24 +101,35 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
if not (self.camera and self.camera.IsOpen()):
|
||||
return
|
||||
|
||||
try:
|
||||
new_exposure_time = self.get_exposure_time()
|
||||
|
||||
# 避免重複設定相同的曝光時間,降低 CPU 負載
|
||||
# 確保曝光時間在相機允許的範圍內
|
||||
min_exp = self.camera.ExposureTime.GetMin()
|
||||
max_exp = self.camera.ExposureTime.GetMax()
|
||||
|
||||
if new_exposure_time < min_exp or new_exposure_time > max_exp:
|
||||
self.statusbar.showMessage(f"曝光時間超出範圍 ({min_exp} ~ {max_exp}),請輸入有效值")
|
||||
return
|
||||
|
||||
# 避免重複設定相同的曝光時間
|
||||
if new_exposure_time == self.last_exposure_time:
|
||||
return
|
||||
|
||||
self.last_exposure_time = new_exposure_time # 更新最後的曝光時間
|
||||
|
||||
self.camera.ExposureTime.SetValue(float(new_exposure_time))
|
||||
self.last_exposure_time = new_exposure_time
|
||||
self.camera.ExposureTime.SetValue(float(new_exposure_time)) # 設定曝光時間
|
||||
self.statusbar.showMessage(f"曝光時間更新為 {new_exposure_time} 微秒")
|
||||
|
||||
# 若相機正在連續擷取,直接應用新的曝光時間
|
||||
# 重新擷取影像
|
||||
if self.camera_thread and self.camera_thread.isRunning():
|
||||
self.stop_keep_shot()
|
||||
self.keep_shot_capture()
|
||||
else:
|
||||
self.one_shot_capture()
|
||||
|
||||
except Exception as e:
|
||||
self.statusbar.showMessage(f"設定曝光時間失敗: {e}")
|
||||
|
||||
def one_shot_capture(self):
|
||||
"""單張擷取"""
|
||||
if not (self.camera and self.camera.IsOpen()):
|
||||
@ -189,6 +199,6 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = CameraApp()
|
||||
window = Camera()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
49
camera/camera_process.py
Normal file
49
camera/camera_process.py
Normal file
@ -0,0 +1,49 @@
|
||||
import time
|
||||
import numpy as np
|
||||
import cv2
|
||||
import multiprocessing
|
||||
from pypylon import pylon
|
||||
|
||||
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.running = multiprocessing.Value('b', True) # 控制進程是否運行
|
||||
self.camera = None
|
||||
|
||||
def connect_camera(self):
|
||||
""" 連接相機 """
|
||||
try:
|
||||
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
||||
self.camera.Open()
|
||||
self.camera.ExposureTime.SetValue(float(self.exposure_time))
|
||||
print("相機連線成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"相機連線失敗: {e}")
|
||||
return False
|
||||
|
||||
def run(self):
|
||||
""" 進程啟動後執行影像擷取 """
|
||||
if not self.connect_camera():
|
||||
return
|
||||
|
||||
self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
|
||||
|
||||
while self.running.value and self.camera.IsGrabbing():
|
||||
grab_result = self.camera.RetrieveResult(20000, pylon.TimeoutHandling_ThrowException)
|
||||
if grab_result.GrabSucceeded():
|
||||
image = grab_result.Array # 取得影像數據
|
||||
if not self.image_queue.full():
|
||||
self.image_queue.put(image) # 將影像放入 Queue
|
||||
grab_result.Release()
|
||||
time.sleep(0.05) # 控制擷取速度,避免 CPU 過載
|
||||
|
||||
self.camera.Close()
|
||||
print("相機關閉")
|
||||
|
||||
def stop(self):
|
||||
""" 停止相機擷取進程 """
|
||||
self.running.value = False
|
52
detection.py
Normal file
52
detection.py
Normal file
@ -0,0 +1,52 @@
|
||||
import sys
|
||||
import cv2
|
||||
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
|
||||
|
||||
class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self):
|
||||
super(DetectionApp, self).__init__()
|
||||
self.setupUi(self)
|
||||
|
||||
self.image_queue = multiprocessing.Queue(maxsize=1)
|
||||
|
||||
# ✅ 啟動 CameraProcess
|
||||
self.camera_process = CameraProcess(self.image_queue)
|
||||
self.camera_process.start()
|
||||
|
||||
# ✅ 設定 QTimer,每 100ms 更新影像
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.update_view_origin)
|
||||
self.timer.start(100) # 每 100ms 更新一次影像
|
||||
|
||||
def update_view_origin(self):
|
||||
""" 從 Queue 獲取影像並顯示在 QLabel (view_origin) 上 """
|
||||
if not self.image_queue.empty():
|
||||
image = self.image_queue.get() # 取得最新影像
|
||||
self.display_image(image)
|
||||
|
||||
def display_image(self, image):
|
||||
""" 顯示影像到 QLabel (view_origin) """
|
||||
image_bgr = cv2.cvtColor(image, cv2.COLOR_BayerBG2BGR) if len(image.shape) == 2 else 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.view_origin.size(), QtCore.Qt.KeepAspectRatio)
|
||||
self.view_origin.setPixmap(pixmap)
|
||||
|
||||
def closeEvent(self, event):
|
||||
""" 確保程式關閉時正確停止相機擷取進程 """
|
||||
self.camera_process.stop() # 停止相機擷取
|
||||
self.camera_process.join() # 等待進程結束
|
||||
event.accept()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = DetectionApp()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
58
main.ui
58
main.ui
@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1308</width>
|
||||
<height>865</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>110</y>
|
||||
<width>471</width>
|
||||
<height>461</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>40</y>
|
||||
<width>91</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1308</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user