1
This commit is contained in:
231
andor-test/06_gui_viewer.py
Normal file
231
andor-test/06_gui_viewer.py
Normal file
@@ -0,0 +1,231 @@
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QPushButton, QDoubleSpinBox,
|
||||
QSpinBox, QGroupBox, QFormLayout)
|
||||
from PyQt5.QtCore import QTimer, Qt
|
||||
from PyQt5.QtGui import QImage, QPixmap
|
||||
from pylablib.devices.Andor import AndorSDK3Camera
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
class CameraViewer(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.cam = None
|
||||
self.is_acquiring = False
|
||||
self.init_ui()
|
||||
self.init_camera()
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle("Andor Camera Viewer")
|
||||
self.setGeometry(100, 100, 1200, 800)
|
||||
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
main_layout = QHBoxLayout(central_widget)
|
||||
|
||||
# 左侧:图像显示
|
||||
self.image_label = QLabel()
|
||||
self.image_label.setMinimumSize(800, 600)
|
||||
self.image_label.setStyleSheet("border: 1px solid black; background-color: black;")
|
||||
self.image_label.setAlignment(Qt.AlignCenter)
|
||||
main_layout.addWidget(self.image_label, stretch=3)
|
||||
|
||||
# 右侧:控制面板
|
||||
control_panel = QVBoxLayout()
|
||||
|
||||
# 连接控制
|
||||
conn_group = QGroupBox("相机控制")
|
||||
conn_layout = QVBoxLayout()
|
||||
self.btn_connect = QPushButton("连接相机")
|
||||
self.btn_connect.clicked.connect(self.toggle_connection)
|
||||
self.btn_start = QPushButton("开始采集")
|
||||
self.btn_start.clicked.connect(self.toggle_acquisition)
|
||||
self.btn_start.setEnabled(False)
|
||||
conn_layout.addWidget(self.btn_connect)
|
||||
conn_layout.addWidget(self.btn_start)
|
||||
conn_group.setLayout(conn_layout)
|
||||
control_panel.addWidget(conn_group)
|
||||
|
||||
# 曝光设置
|
||||
exp_group = QGroupBox("曝光设置")
|
||||
exp_layout = QFormLayout()
|
||||
self.spin_exposure = QDoubleSpinBox()
|
||||
self.spin_exposure.setRange(0.001, 10.0)
|
||||
self.spin_exposure.setValue(0.05)
|
||||
self.spin_exposure.setSingleStep(0.01)
|
||||
self.spin_exposure.setSuffix(" s")
|
||||
self.spin_exposure.valueChanged.connect(self.update_exposure)
|
||||
exp_layout.addRow("曝光时间:", self.spin_exposure)
|
||||
exp_group.setLayout(exp_layout)
|
||||
control_panel.addWidget(exp_group)
|
||||
|
||||
# ROI设置
|
||||
roi_group = QGroupBox("ROI设置")
|
||||
roi_layout = QFormLayout()
|
||||
|
||||
self.spin_roi_x = QSpinBox()
|
||||
self.spin_roi_x.setRange(0, 2048)
|
||||
self.spin_roi_x.setValue(0)
|
||||
|
||||
self.spin_roi_y = QSpinBox()
|
||||
self.spin_roi_y.setRange(0, 2048)
|
||||
self.spin_roi_y.setValue(0)
|
||||
|
||||
self.spin_roi_width = QSpinBox()
|
||||
self.spin_roi_width.setRange(1, 2048)
|
||||
self.spin_roi_width.setValue(2048)
|
||||
|
||||
self.spin_roi_height = QSpinBox()
|
||||
self.spin_roi_height.setRange(1, 2048)
|
||||
self.spin_roi_height.setValue(2048)
|
||||
|
||||
self.btn_set_roi = QPushButton("应用ROI")
|
||||
self.btn_set_roi.clicked.connect(self.update_roi)
|
||||
|
||||
self.btn_full_roi = QPushButton("全幅")
|
||||
self.btn_full_roi.clicked.connect(self.set_full_roi)
|
||||
|
||||
roi_layout.addRow("起始X:", self.spin_roi_x)
|
||||
roi_layout.addRow("起始Y:", self.spin_roi_y)
|
||||
roi_layout.addRow("宽度:", self.spin_roi_width)
|
||||
roi_layout.addRow("高度:", self.spin_roi_height)
|
||||
roi_layout.addRow(self.btn_set_roi)
|
||||
roi_layout.addRow(self.btn_full_roi)
|
||||
roi_group.setLayout(roi_layout)
|
||||
control_panel.addWidget(roi_group)
|
||||
|
||||
# 信息显示
|
||||
info_group = QGroupBox("图像信息")
|
||||
info_layout = QVBoxLayout()
|
||||
self.lbl_image_size = QLabel("图像尺寸: --")
|
||||
self.lbl_min_max = QLabel("Min/Max: --")
|
||||
self.lbl_fps = QLabel("FPS: --")
|
||||
info_layout.addWidget(self.lbl_image_size)
|
||||
info_layout.addWidget(self.lbl_min_max)
|
||||
info_layout.addWidget(self.lbl_fps)
|
||||
info_group.setLayout(info_layout)
|
||||
control_panel.addWidget(info_group)
|
||||
|
||||
control_panel.addStretch()
|
||||
main_layout.addLayout(control_panel, stretch=1)
|
||||
|
||||
# 定时器
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.update_frame)
|
||||
|
||||
def init_camera(self):
|
||||
pass
|
||||
|
||||
def toggle_connection(self):
|
||||
if self.cam is None:
|
||||
try:
|
||||
self.cam = AndorSDK3Camera(idx=0)
|
||||
self.cam.open()
|
||||
detector_size = self.cam.get_detector_size()
|
||||
self.spin_roi_width.setMaximum(detector_size[0])
|
||||
self.spin_roi_height.setMaximum(detector_size[1])
|
||||
self.spin_roi_x.setMaximum(detector_size[0])
|
||||
self.spin_roi_y.setMaximum(detector_size[1])
|
||||
self.spin_roi_width.setValue(detector_size[0])
|
||||
self.spin_roi_height.setValue(detector_size[1])
|
||||
self.btn_connect.setText("断开相机")
|
||||
self.btn_start.setEnabled(True)
|
||||
print("相机已连接")
|
||||
except Exception as e:
|
||||
print(f"连接失败: {e}")
|
||||
else:
|
||||
if self.is_acquiring:
|
||||
self.toggle_acquisition()
|
||||
self.cam.close()
|
||||
self.cam = None
|
||||
self.btn_connect.setText("连接相机")
|
||||
self.btn_start.setEnabled(False)
|
||||
print("相机已断开")
|
||||
|
||||
def toggle_acquisition(self):
|
||||
if not self.is_acquiring:
|
||||
try:
|
||||
self.cam.setup_acquisition(mode="sequence", nframes=100)
|
||||
self.cam.start_acquisition()
|
||||
self.is_acquiring = True
|
||||
self.btn_start.setText("停止采集")
|
||||
self.timer.start(50)
|
||||
print("开始采集")
|
||||
except Exception as e:
|
||||
print(f"启动采集失败: {e}")
|
||||
else:
|
||||
self.timer.stop()
|
||||
self.cam.stop_acquisition()
|
||||
self.is_acquiring = False
|
||||
self.btn_start.setText("开始采集")
|
||||
print("停止采集")
|
||||
|
||||
def update_exposure(self):
|
||||
if self.cam and not self.is_acquiring:
|
||||
exp = self.spin_exposure.value()
|
||||
self.cam.set_exposure(exp)
|
||||
print(f"曝光设置为: {exp}s")
|
||||
|
||||
def update_roi(self):
|
||||
if self.cam and not self.is_acquiring:
|
||||
x = self.spin_roi_x.value()
|
||||
y = self.spin_roi_y.value()
|
||||
w = self.spin_roi_width.value()
|
||||
h = self.spin_roi_height.value()
|
||||
try:
|
||||
self.cam.set_roi(hstart=x, hend=x+w, vstart=y, vend=y+h)
|
||||
print(f"ROI设置为: ({x},{y}) {w}x{h}")
|
||||
except Exception as e:
|
||||
print(f"ROI设置失败: {e}")
|
||||
|
||||
def set_full_roi(self):
|
||||
if self.cam and not self.is_acquiring:
|
||||
self.cam.set_roi()
|
||||
detector_size = self.cam.get_detector_size()
|
||||
self.spin_roi_x.setValue(0)
|
||||
self.spin_roi_y.setValue(0)
|
||||
self.spin_roi_width.setValue(detector_size[0])
|
||||
self.spin_roi_height.setValue(detector_size[1])
|
||||
print("ROI设置为全幅")
|
||||
|
||||
def update_frame(self):
|
||||
try:
|
||||
frames = self.cam.read_multiple_images()
|
||||
if frames and len(frames) > 0:
|
||||
image = frames[0]
|
||||
|
||||
# 归一化到0-255
|
||||
img_min = image.min()
|
||||
img_max = image.max()
|
||||
if img_max > img_min:
|
||||
image_norm = ((image - img_min) / (img_max - img_min) * 255).astype(np.uint8)
|
||||
else:
|
||||
image_norm = np.zeros_like(image, dtype=np.uint8)
|
||||
|
||||
# 转换为QImage并显示
|
||||
height, width = image_norm.shape
|
||||
bytes_per_line = width
|
||||
q_image = QImage(image_norm.data, width, height, bytes_per_line, QImage.Format_Grayscale8)
|
||||
pixmap = QPixmap.fromImage(q_image)
|
||||
scaled_pixmap = pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
self.image_label.setPixmap(scaled_pixmap)
|
||||
|
||||
# 更新信息
|
||||
self.lbl_image_size.setText(f"图像尺寸: {width}x{height}")
|
||||
self.lbl_min_max.setText(f"Min/Max: {img_min}/{img_max}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取帧失败: {e}")
|
||||
|
||||
def closeEvent(self, event):
|
||||
if self.is_acquiring:
|
||||
self.toggle_acquisition()
|
||||
if self.cam:
|
||||
self.cam.close()
|
||||
event.accept()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
viewer = CameraViewer()
|
||||
viewer.show()
|
||||
sys.exit(app.exec_())
|
||||
Reference in New Issue
Block a user