本软件用于采集用户实验过程中的视频图像。
点击查看代码
# 用于录制实验视频软件, 摄像头编号为1import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QLineEdit, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QLabel, QFrame, QMessageBox)
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QImage, QPixmap
import os class CameraApp(QMainWindow):def __init__(self):super().__init__()self.camera = Noneself.is_camera_active = Falseself.timer = QTimer()self.recording = Falseself.video_writer = Noneself.save_path = None self.init_ui()self.connect_signals()def init_ui(self):self.setWindowTitle("摄像头控制界面")self.setGeometry(600, 200, 800, 600)# 创建中央窗口部件central_widget = QWidget()self.setCentralWidget(central_widget)# 创建主布局main_layout = QHBoxLayout(central_widget)# 左侧控制面板control_frame = QFrame()control_frame.setFrameShape(QFrame.StyledPanel)control_frame.setFixedWidth(300)control_layout = QVBoxLayout(control_frame)# 状态标签self.status_label = QLabel('请选择保存位置并开始录制')control_layout.addWidget(self.status_label)# 选择保存位置按钮self.select_button = QPushButton('选择保存位置')self.subject_id_label = QLabel('填写被试编号: ')self.subject_id = QLineEdit()self.subject_id.setPlaceholderText("如sub01")# self.subject_id.textChanged.connect(self.validate_filename)# control_layout.addRow("被试id:", self.subject_id)# 创建按钮self.open_btn = QPushButton("打开摄像头")self.start_btn = QPushButton("开始录制") # 点击之后变成结束录制self.pause_btn = QPushButton("暂停")self.stop_btn = QPushButton("关闭摄像头")# 设置按钮样式 # 16button_style = """QPushButton {background-color: #4CAF50;border: none;color: white;padding: 10px;text-align: center;text-decoration: none;font-size: 20px; margin: 4px 2px;border-radius: 8px;}QPushButton:hover {background-color: #45a049;}QPushButton:disabled {background-color: #cccccc;}"""self.select_button.setStyleSheet(button_style)self.select_button.clicked.connect(self.select_save_location)control_layout.addWidget(self.select_button)control_layout.addWidget(self.subject_id_label) #被试id放在文件夹保存按钮下面control_layout.addWidget(self.subject_id)self.open_btn.setStyleSheet(button_style)self.start_btn.setStyleSheet(button_style)self.pause_btn.setStyleSheet(button_style)self.stop_btn.setStyleSheet(button_style)self.start_btn.setEnabled(False) # 初始不可用 # 将按钮添加到控制布局control_layout.addWidget(self.open_btn)control_layout.addWidget(self.start_btn)control_layout.addWidget(self.pause_btn)control_layout.addWidget(self.stop_btn)control_layout.addStretch() # 添加弹性空间# 右侧显示面板display_frame = QFrame()display_frame.setFrameShape(QFrame.StyledPanel)display_layout = QVBoxLayout(display_frame)# 创建显示图像的标签self.image_label = QLabel()self.image_label.setAlignment(Qt.AlignCenter)self.image_label.setMinimumSize(640, 480)self.image_label.setText("摄像头未启动")self.image_label.setStyleSheet("border: 1px solid black; background-color: #f0f0f0;")display_layout.addWidget(self.image_label)# 将左右面板添加到主布局main_layout.addWidget(control_frame)main_layout.addWidget(display_frame)# 更新按钮状态self.update_button_states()def select_save_location(self):# 弹出文件夹选择对话框folder_path = QFileDialog.getExistingDirectory(self, "选择视频保存文件夹",os.path.expanduser("~"), # 默认从用户主目录开始QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)if folder_path:self.save_path = folder_pathself.status_label.setText(f'保存位置: {folder_path}')# self.start_btn.setEnabled(True)# 初始化摄像头# self.init_camera()def connect_signals(self):# 连接按钮信号self.open_btn.clicked.connect(self.open_camera)self.start_btn.clicked.connect(self.toggle_recording) #开始录制self.pause_btn.clicked.connect(self.pause_camera)self.stop_btn.clicked.connect(self.stop_camera)# 连接定时器信号self.timer.timeout.connect(self.update_frame)def open_camera(self):#打开之后,右边就开始显示了self.camera = cv2.VideoCapture(1) #0 罗技摄像头if not self.camera.isOpened():self.image_label.setText("无法访问摄像头")returnself.is_camera_active = Trueself.update_button_states()# 右边显示摄像头正在打开...self.image_label.setText("摄像头正在打开...")# 启动定时器,每30毫秒更新一帧self.timer.start(30)def update_button_states(self):# 根据摄像头状态更新按钮可用状态if not self.is_camera_active:self.open_btn.setEnabled(True)self.start_btn.setEnabled(False) # Trueself.pause_btn.setEnabled(False)self.stop_btn.setEnabled(False)else:self.open_btn.setEnabled(False) #打开摄像头设置为灰self.start_btn.setEnabled(True) #录制按钮self.pause_btn.setEnabled(True) # 暂停那妞self.stop_btn.setEnabled(True) #结束按钮def toggle_recording(self):if not self.recording:# 开始录制self.start_recording()else:# 停止录制self.stop_recording()def start_recording(self):# 设置保存数据的文件名if not self.save_path:QMessageBox.warning(self, "警告", "请先选择保存位置")return # 生成文件名(使用时间戳)from datetime import datetimetimestamp = datetime.now().strftime("%Y%m%d_%H%M%S")if self.subject_id.text():# print('subject id: ', self.subject_id.text())filename = f"{self.subject_id.text()}_video_{timestamp}.avi"else:filename = f"video_{timestamp}.avi"file_path = os.path.join(self.save_path, filename)# 设置视频编码器和参数frame_width = int(self.camera.get(cv2.CAP_PROP_FRAME_WIDTH))frame_height = int(self.camera.get(cv2.CAP_PROP_FRAME_HEIGHT))fps = 20.0# 创建视频写入器fourcc = cv2.VideoWriter_fourcc(*'XVID')self.video_writer = cv2.VideoWriter(file_path, fourcc, fps, (frame_width, frame_height))if not self.video_writer.isOpened():QMessageBox.critical(self, "错误", "无法创建视频文件")return self.recording = True# return True self.start_btn.setText('停止录制')self.status_label.setText(f'正在录制: {filename}')self.status_label.setStyleSheet("color: red")#font-size: 16px;def stop_recording(self):self.recording = Falseself.start_btn.setText('开始录制')self.status_label.setText(f'录制已停止,视频保存在: {self.save_path}')self.status_label.setStyleSheet("color: black")if self.video_writer:self.video_writer.release()self.video_writer = Nonedef start_camera(self):# 初始化摄像头self.camera = cv2.VideoCapture(1) #0 罗技摄像头if not self.camera.isOpened():self.image_label.setText("无法访问摄像头")returnself.is_camera_active = Trueself.update_button_states()# 启动定时器,每30毫秒更新一帧self.timer.start(30)# 设置录制视频名称# self.start_recording()def pause_camera(self):if self.is_camera_active:if self.timer.isActive():self.timer.stop()self.pause_btn.setText("继续")else:self.timer.start(30)self.pause_btn.setText("暂停")def stop_camera(self):self.is_camera_active = Falseself.timer.stop()if self.camera:self.camera.release()self.camera = None# self.record_button.setText('开始录制')if self.recording:self.recording = Falseself.status_label.setText(f'录制已停止,视频保存在: {self.save_path}')#停止录制变回开始录制self.start_btn.setText("开始录制")if self.video_writer:self.video_writer.release()self.video_writer = Noneself.image_label.setText("摄像头已停止")# 保存数据self.update_button_states()self.pause_btn.setText("暂停")def update_frame(self):if self.camera and self.camera.isOpened():ret, frame = self.camera.read()if ret:# 将OpenCV的BGR格式转换为RGBframe = cv2.flip(frame, 1)# 如果正在录制,保存帧if self.recording and self.video_writer:self.video_writer.write(frame)frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 将图像转换为Qt格式h, w, ch = frame_rgb.shapebytes_per_line = ch * wqt_image = QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888)# 缩放图像以适应标签大小scaled_pixmap = QPixmap.fromImage(qt_image).scaled(self.image_label.width(), self.image_label.height(),Qt.KeepAspectRatio)# 显示图像self.image_label.setPixmap(scaled_pixmap)else:self.image_label.setText("无法读取帧")def closeEvent(self, event):# 关闭应用程序时释放摄像头资源self.stop_camera()event.accept()if __name__ == "__main__":app = QApplication(sys.argv)window = CameraApp()window.show()sys.exit(app.exec_())