python
复制代码
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton,
QVBoxLayout, QWidget, QSlider, QGroupBox,
QGridLayout, QComboBox, QMessageBox,QHBoxLayout)
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import Qt, QTimer,QDateTime
class CameraController(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("专业级摄像头控制器")
self.setGeometry(100, 100, 800, 600)
# 主组件
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.layout = QVBoxLayout()
self.central_widget.setLayout(self.layout)
# 摄像头显示区域
self.video_label = QLabel()
self.video_label.setAlignment(Qt.AlignCenter)
self.video_label.setMinimumSize(640, 480)
self.layout.addWidget(self.video_label)
# 控制按钮
self.control_layout = QVBoxLayout()
# 相机控制按钮
self.btn_layout = QHBoxLayout()
self.start_btn = QPushButton("启动摄像头")
self.pause_btn = QPushButton("暂停")
self.pause_btn.setEnabled(False)
self.capture_btn = QPushButton("拍照")
self.capture_btn.setEnabled(False)
self.close_btn = QPushButton("退出")
self.btn_layout.addWidget(self.start_btn)
self.btn_layout.addWidget(self.pause_btn)
self.btn_layout.addWidget(self.capture_btn)
self.btn_layout.addWidget(self.close_btn)
self.control_layout.addLayout(self.btn_layout)
# 参数控制面板
self.create_parameter_controls()
self.layout.addLayout(self.control_layout)
# 摄像头及计时器
self.camera = None
self.timer = QTimer()
self.is_camera_active = False
# 连接信号
self.start_btn.clicked.connect(self.start_camera)
self.pause_btn.clicked.connect(self.pause_camera)
self.capture_btn.clicked.connect(self.capture_frame)
self.close_btn.clicked.connect(self.close_app)
# 状态提示
self.status_label = QLabel("状态: 未启动")
self.layout.addWidget(self.status_label)
def create_parameter_controls(self):
"""创建相机参数控制面板"""
# 参数控制组
self.params_group = QGroupBox("相机参数调节")
params_layout = QGridLayout()
# 亮度控制
self.brightness_slider = self.create_slider("亮度", 0, 255, 128)
params_layout.addWidget(QLabel("亮度:"), 0, 0)
params_layout.addWidget(self.brightness_slider, 0, 1)
# 对比度控制
self.contrast_slider = self.create_slider("对比度", 0, 100, 50)
params_layout.addWidget(QLabel("对比度:"), 1, 0)
params_layout.addWidget(self.contrast_slider, 1, 1)
# 饱和度控制
self.saturation_slider = self.create_slider("饱和度", 0, 100, 60)
params_layout.addWidget(QLabel("饱和度:"), 2, 0)
params_layout.addWidget(self.saturation_slider, 2, 1)
# 锐度控制
self.sharpness_slider = self.create_slider("锐度", 0, 100, 50)
params_layout.addWidget(QLabel("锐度:"), 3, 0)
params_layout.addWidget(self.sharpness_slider, 3, 1)
# 曝光控制
self.exposure_slider = self.create_slider("曝光", -7, 7, 0)
params_layout.addWidget(QLabel("曝光:"), 4, 0)
params_layout.addWidget(self.exposure_slider, 4, 1)
# 分辨率选择
self.resolution_combo = QComboBox()
self.resolution_combo.addItems(["640x480", "1280x720", "1920x1080"])
params_layout.addWidget(QLabel("分辨率:"), 0, 2)
params_layout.addWidget(self.resolution_combo, 0, 3)
self.resolution_combo.currentIndexChanged.connect(self.change_resolution)
# 白平衡预设
self.wb_preset_combo = QComboBox()
self.wb_preset_combo.addItems(["自动", "日光", "阴天", "钨丝灯", "荧光灯"])
params_layout.addWidget(QLabel("白平衡:"), 1, 2)
params_layout.addWidget(self.wb_preset_combo, 1, 3)
# 滤镜效果
self.filter_combo = QComboBox()
self.filter_combo.addItems(["无", "灰度", "暖色", "冷色", "复古"])
params_layout.addWidget(QLabel("滤镜:"), 2, 2)
params_layout.addWidget(self.filter_combo, 2, 3)
self.params_group.setLayout(params_layout)
self.control_layout.addWidget(self.params_group)
# 初始不可用,相机启动后启用
self.params_group.setEnabled(False)
def create_slider(self, name, min_val, max_val, default):
"""创建带标签的滑块控件"""
slider = QSlider(Qt.Horizontal)
slider.setMinimum(min_val)
slider.setMaximum(max_val)
slider.setValue(default)
slider.setToolTip(f"{name}调节")
slider.valueChanged.connect(self.adjust_camera_params)
return slider
def start_camera(self):
"""启动摄像头"""
# 尝试打开摄像头
self.camera = cv2.VideoCapture(0)
if not self.camera.isOpened():
QMessageBox.critical(self, "错误", "无法打开摄像头")
return
# 设置初始分辨率
res = self.resolution_combo.currentText()
width, height = map(int, res.split('x'))
self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
# 设置初始参数
self.adjust_camera_params()
# 设置自动曝光
self.camera.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)
# 启动定时器读取帧
self.timer.timeout.connect(self.update_frame)
self.timer.start(30) # ≈33 FPS
# 更新界面状态
self.is_camera_active = True
self.status_label.setText("状态: 运行中")
self.start_btn.setEnabled(False)
self.pause_btn.setEnabled(True)
self.capture_btn.setEnabled(True)
self.params_group.setEnabled(True)
def pause_camera(self):
"""暂停摄像头"""
if self.is_camera_active:
self.timer.stop()
self.is_camera_active = False
self.status_label.setText("状态: 已暂停")
self.pause_btn.setText("继续")
self.params_group.setEnabled(False)
else:
self.timer.start(30)
self.is_camera_active = True
self.status_label.setText("状态: 运行中")
self.pause_btn.setText("暂停")
self.params_group.setEnabled(True)
def capture_frame(self):
"""捕获当前帧并保存为文件"""
if self.is_camera_active:
_, frame = self.camera.read()
if frame is not None:
# 应用当前滤镜
frame = self.apply_filter(frame)
# 保存为文件
filename = f"capture_{QDateTime.currentDateTime().toString('yyyyMMdd_hhmmss')}.png"
cv2.imwrite(filename, frame)
QMessageBox.information(self, "拍照成功", f"已保存为 {filename}")
def change_resolution(self):
"""改变分辨率"""
if self.is_camera_active and self.camera is not None:
res = self.resolution_combo.currentText()
width, height = map(int, res.split('x'))
# 停止定时器
self.timer.stop()
# 设置分辨率
self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
# 重启定时器
self.timer.start(30)
def adjust_camera_params(self):
"""实时调整相机参数"""
if self.is_camera_active and self.camera is not None:
# 获取参数值
brightness = self.brightness_slider.value()
contrast = self.contrast_slider.value() / 50.0 # 0-2范围
saturation = self.saturation_slider.value() / 50.0
sharpness = self.sharpness_slider.value()
exposure = self.exposure_slider.value()
# 设置相机属性
self.camera.set(cv2.CAP_PROP_BRIGHTNESS, brightness)
self.camera.set(cv2.CAP_PROP_CONTRAST, contrast)
self.camera.set(cv2.CAP_PROP_SATURATION, saturation)
self.camera.set(cv2.CAP_PROP_SHARPNESS, sharpness)
self.camera.set(cv2.CAP_PROP_EXPOSURE, exposure)
def apply_filter(self, frame):
"""应用选择的滤镜效果"""
filter_type = self.filter_combo.currentIndex()
if filter_type == 1: # 灰度
return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
elif filter_type == 2: # 暖色
# 增加红色通道,减少蓝色通道
frame[:, :, 2] = np.clip(frame[:, :, 2] * 1.2, 0, 255)
frame[:, :, 0] = np.clip(frame[:, :, 0] * 0.8, 0, 255)
return frame
elif filter_type == 3: # 冷色
# 增加蓝色通道,减少红色通道
frame[:, :, 0] = np.clip(frame[:, :, 0] * 1.2, 0, 255)
frame[:, :, 2] = np.clip(frame[:, :, 2] * 0.8, 0, 255)
return frame
elif filter_type == 4: # 复古
# 添加棕褐色调
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = np.array(frame, dtype=np.float32) * np.array([1.0, 0.7, 0.4], dtype=np.float32)
frame = np.clip(frame, 0, 255)
return cv2.cvtColor(frame.astype(np.uint8), cv2.COLOR_RGB2BGR)
return frame
def update_frame(self):
"""更新摄像头帧显示"""
if self.is_camera_active and self.camera is not None:
ret, frame = self.camera.read()
if ret:
# 应用当前滤镜
frame = self.apply_filter(frame)
# 应用白平衡预设 (此处简化为颜色调整)
wb_preset = self.wb_preset_combo.currentIndex()
if wb_preset > 0:
# 简化的白平衡调整
if wb_preset == 1: # 日光
frame[:, :, 2] = np.clip(frame[:, :, 2] * 1.1, 0, 255)
elif wb_preset == 2: # 阴天
frame[:, :, 0] = np.clip(frame[:, :, 0] * 1.2, 0, 255)
elif wb_preset == 3: # 钨丝灯
frame[:, :, 2] = np.clip(frame[:, :, 2] * 1.3, 0, 255)
elif wb_preset == 4: # 荧光灯
frame[:, :, 1] = np.clip(frame[:, :, 1] * 1.2, 0, 255)
# 转换为QImage并显示
if len(frame.shape) == 2: # 灰度图像
q_img = QImage(frame.data, frame.shape[1], frame.shape[0],
frame.strides[0], QImage.Format_Grayscale8)
else: # 彩色图像
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
q_img = QImage(frame.data, frame.shape[1], frame.shape[0],
frame.strides[0], QImage.Format_RGB888)
self.video_label.setPixmap(QPixmap.fromImage(q_img))
def close_app(self):
"""安全关闭应用"""
if self.camera is not None:
self.camera.release()
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = CameraController()
window.show()
sys.exit(app.exec_())