修改整體架構
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
|
import numpy as np
|
||||||
from camera_window import Ui_MainWindow
|
from camera_window import Ui_MainWindow
|
||||||
|
|
||||||
|
|
||||||
class CameraThread(QThread):
|
class CameraThread(QThread):
|
||||||
update_image_signal = pyqtSignal(np.ndarray) # 訊號:傳遞影像數據
|
update_image_signal = pyqtSignal(np.ndarray) # 訊號:傳遞影像數據
|
||||||
|
|
||||||
@ -45,9 +44,6 @@ class CameraThread(QThread):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"影像擷取錯誤: {e}")
|
print(f"影像擷取錯誤: {e}")
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"影像擷取錯誤: {e}")
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""停止執行緒"""
|
"""停止執行緒"""
|
||||||
self.running = False
|
self.running = False
|
||||||
@ -57,9 +53,9 @@ class CameraThread(QThread):
|
|||||||
self.wait() # 確保執行緒完全停止
|
self.wait() # 確保執行緒完全停止
|
||||||
|
|
||||||
|
|
||||||
class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
class Camera(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(CameraApp, self).__init__()
|
super(Camera, self).__init__()
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
# 連接按鈕事件
|
# 連接按鈕事件
|
||||||
@ -76,6 +72,7 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.camera = None
|
self.camera = None
|
||||||
self.camera_thread = None
|
self.camera_thread = None
|
||||||
self.current_image = None
|
self.current_image = None
|
||||||
|
self.latest_image = None # 用於存儲最新影像
|
||||||
self.last_exposure_time = None # 記錄最後的曝光時間,避免重複更新
|
self.last_exposure_time = None # 記錄最後的曝光時間,避免重複更新
|
||||||
|
|
||||||
def connect_camera(self):
|
def connect_camera(self):
|
||||||
@ -83,9 +80,11 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
try:
|
try:
|
||||||
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
|
||||||
self.camera.Open()
|
self.camera.Open()
|
||||||
self.statusbar.showMessage("相機連線成功")
|
print("相機連線成功")
|
||||||
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.statusbar.showMessage(f"相機連線失敗: {e}")
|
print(f"相機連線失敗: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def get_exposure_time(self):
|
def get_exposure_time(self):
|
||||||
"""取得曝光時間,若為空則使用預設值"""
|
"""取得曝光時間,若為空則使用預設值"""
|
||||||
@ -102,23 +101,34 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
if not (self.camera and self.camera.IsOpen()):
|
if not (self.camera and self.camera.IsOpen()):
|
||||||
return
|
return
|
||||||
|
|
||||||
new_exposure_time = self.get_exposure_time()
|
try:
|
||||||
|
new_exposure_time = self.get_exposure_time()
|
||||||
|
|
||||||
# 避免重複設定相同的曝光時間,降低 CPU 負載
|
# 確保曝光時間在相機允許的範圍內
|
||||||
if new_exposure_time == self.last_exposure_time:
|
min_exp = self.camera.ExposureTime.GetMin()
|
||||||
return
|
max_exp = self.camera.ExposureTime.GetMax()
|
||||||
|
|
||||||
self.last_exposure_time = new_exposure_time # 更新最後的曝光時間
|
if new_exposure_time < min_exp or new_exposure_time > max_exp:
|
||||||
|
self.statusbar.showMessage(f"曝光時間超出範圍 ({min_exp} ~ {max_exp}),請輸入有效值")
|
||||||
|
return
|
||||||
|
|
||||||
self.camera.ExposureTime.SetValue(float(new_exposure_time))
|
# 避免重複設定相同的曝光時間
|
||||||
self.statusbar.showMessage(f"曝光時間更新為 {new_exposure_time} 微秒")
|
if new_exposure_time == self.last_exposure_time:
|
||||||
|
return
|
||||||
|
|
||||||
# 若相機正在連續擷取,直接應用新的曝光時間
|
self.last_exposure_time = new_exposure_time
|
||||||
if self.camera_thread and self.camera_thread.isRunning():
|
self.camera.ExposureTime.SetValue(float(new_exposure_time)) # 設定曝光時間
|
||||||
self.stop_keep_shot()
|
self.statusbar.showMessage(f"曝光時間更新為 {new_exposure_time} 微秒")
|
||||||
self.keep_shot_capture()
|
|
||||||
else:
|
# 重新擷取影像
|
||||||
self.one_shot_capture()
|
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):
|
def one_shot_capture(self):
|
||||||
"""單張擷取"""
|
"""單張擷取"""
|
||||||
@ -189,6 +199,6 @@ class CameraApp(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
window = CameraApp()
|
window = Camera()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec_())
|
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