linux实战:基于Ubuntu的专业相机

核心组件就是QTimer+OpenCV的组合方案
摄像头启停控制用QPushButton实现,帧显示必须用QLabel而不能用普通控件,视频流刷新用QTimer比多线程更简单
想快速实现摄像头控制功能,核心组件就是QTimer+OpenCV的组合方案。摄像头启停控制用QPushButton实现,帧显示必须用QLabel而不能用普通控件,视频流刷新用QTimer比多线程更简单。拍照功能整合进来作为扩展功能。代码结构上应该分三层:界面层用QMainWindow组织按钮和显示区域,逻辑层用VideoCapture和QTimer控制帧率,转换层需要处理OpenCV的BGR转Qt的RGB格式。特别注意摄像头资源释放要放在窗口关闭事件里,不然会报错。
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,QDateTimeclass 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 = Noneself.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 sliderdef 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 = Trueself.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 = Falseself.status_label.setText("状态: 已暂停")self.pause_btn.setText("继续")self.params_group.setEnabled(False)else:self.timer.start(30)self.is_camera_active = Trueself.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.0sharpness = 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 frameelif filter_type == 3:  # 冷色# 增加蓝色通道,减少红色通道frame[:, :, 0] = np.clip(frame[:, :, 0] * 1.2, 0, 255)frame[:, :, 2] = np.clip(frame[:, :, 2] * 0.8, 0, 255)return frameelif 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 framedef 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_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918376.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918376.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《深度剖析前端框架中错误边界:异常处理的基石与进阶》

错误边界作为一种特殊的组件机制,正悄然重塑着应用应对异常的底层逻辑。它并非简单的代码片段组合,而是一套贯穿组件生命周期的防护体系,其核心价值在于将局部错误的影响牢牢锁定在可控范围内,避免整个应用陷入不可挽回的崩溃状态…

6GB显存玩转SD微调!LoRA-scripts本地部署教程,一键炼出专属AI画师

一、介绍LoRA-scripts(又名 SD-Trainer),是一个专为训练低秩自适应(LoRA)模型设计的开源工具集主要应用于Stable Diffusion等AI绘图模型的微调,帮助用户高效创建定制化风格、角色或概念的轻量级模型。目前已…

探索AI的数学奇迹:Gemini 2.5 Pro如何摘得IMO金牌

🌍 引言:从人类天才到AI奇才的跨越 想象一下,一个AI模型坐在国际数学奥林匹克(IMO)的考场里,手里拿着笔(好吧,其实是处理token),面对那些让高中生们头疼不已的难题。它不是靠死记硬背,而是通过深思熟虑的推理,一步步攻克难关。这听起来像科幻小说,但2025年,这已…

MCP学习与实践

目录 1.MCP简介 1.1 MCP是什么 1.2 MCP与Agent关系: 1.3 MCP的架构 2. MCP原理 2.1 MCP 工作过程 2.2 MCP 通讯方式 2. MCP使用 2.1 cursor中增加MCP-SSE(高德地图MCP) 2.2 cursor中增加MCP-STDIO(12306-MCP) 本文详细讲解了什么是…

MySQL(187)如何使用pt-query-digest进行查询分析?

使用 pt-query-digest 工具可以帮助分析 MySQL 查询的性能,找出慢查询、频繁查询以及消耗资源较多的查询,从而为优化提供依据。以下是详细深入的使用 pt-query-digest 进行查询分析的步骤和相关示例。 一、安装 pt-query-digest pt-query-digest 是 Perc…

分享一个基于Python和Hadoop的的电信客户特征可视化分析平台 基于Spark平台的电信客服数据存储与处理系统源码

💕💕作者:计算机源码社 💕💕个人简介:本人八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Spark、hadoop、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题…

初识STL

一 、STL的诞生在C发展早期,程序员在不同的项目中需要反复编写相似的数据结构和算法。重复开发带来以下问题:代码冗余:每个项目都要重新实现基本数据结构和算法维护困难:不同人编写的代码风格不一致,难以维护效率低下&…

DDoS 防护的未来趋势:AI 如何重塑安全行业?

随着网络攻击规模和复杂性的不断升级,分布式拒绝服务(DDoS)攻击已成为企业数字化转型中的一大威胁。传统防御手段在应对智能化、动态化的攻击时逐渐显露出局限性。而人工智能(AI)技术的崛起,正为 DDoS 防护…

【每天一个知识点】深度领域对抗神经网络

Deep Domain Adversarial Neural Network(深度领域对抗神经网络,DDANN) 是一类结合 深度学习 与 领域自适应(domain adaptation) 思想的神经网络结构,主要用于不同数据域之间的知识迁移,尤其是在…

【C语言】深入理解预处理

文章目录一、预定义符号二、#define定义常量:便捷的符号替换常见用法示例:注意事项:三、#define定义宏:带参数的文本替换关键注意点:四、带有副作用的宏参数五、宏替换的规则:预处理的执行步骤重要注意&…

展锐平台(Android15)WLAN热点名称修改不生效问题分析

前言 在展锐Android V项目开发中,需要修改softAp/P2P热点名称时,发现集成GMS后直接修改framework层代码无效。具体表现为: 修改packages/modules/Wifi/WifiApConfigStore中的getDefaultApConfiguration方法编译烧录后修改不生效 问题根源在…

wsl ubuntu访问(挂载)vmware vmdk磁盘教程

之前使用VMware Workstation 虚拟机跑了个ubuntu,现在改用wsl了, 想把vmware的磁盘挂载到wsl ubuntu。一、磁盘合并我原先的vmware跑的ubuntu存在多个vmdk文件(磁盘文件),需要先将磁盘合并成一个才方便挂载。首先你电脑…

UGUI源码剖析(3):布局的“原子”——RectTransform的核心数据模型与几何学

UGUI源码剖析(第三章):布局的“原子”——RectTransform的核心数据模型与几何学 在前几章中,我们了解了UGUI的组件规范和更新调度机制。现在,我们将深入到这个系统的“几何学”核心,去剖析那个我们每天都在…

c++注意点(15)----设计模式(桥接模式与适配器模式)

一、结构型设计模式两者有点相似,都是为了做到解耦的功能。适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&…

DuoPlus支持导入文件批量配置云手机参数,还优化了批量操作和搜索功能!

作为我常用的一款还不错的跨境工具,DuoPlus云手机帮我高效完成了很多跨境工作,它的功能也在逐步完善和优化,今天来聊聊它最近新更新的一些功能。功能更新一览新增导入文件配置参数:批量初始化代理、批量修改参数支持导入文件一键配…

PLC如何实现通过MQTT协议物联网网关接入管理云平台

在工业4.0与智能制造浪潮下,企业亟需实现设备数据的高效采集与云端协同,以支撑远程监控、预测性维护等场景。工业智能网关凭借其强大的协议解析能力、边缘计算功能及安全传输机制,成为PLC接入云平台的核心解决方案。本文将从技术架构、功能模…

通过sealos工具在ubuntu 24.02上安装k8s集群

一、系统准备(1)安装openssh服务 sudo apt install openssh-server sudo systemctl start ssh sudo systemctl enable ssh(2)放通防火墙 sudo ufw allow ssh(3)开通root直接登录 vim /etc/ssh/sshd_config#…

nginx+Lua环境集成、nginx+Lua应用

nginxluaredis实践 概述 nginx、lua访问redis的三种方式: 1。 HttpRedis模块。 指令少,功能单一 ,适合简单的缓存。只支持get 、select命令。 2。 HttpRedis2Module模块。 功能强大,比较灵活。 3。 lua-resty-redis库 OpenResty。…

机器学习 K-Means聚类 无监督学习

目录 K-Means 聚类:从原理到实践的完整指南 什么是 K-Means 聚类? 应用场景举例 K-Means 算法的核心原理 K-Means 算法的步骤详解 可视化理解 K-Means 的优缺点分析 优点 缺点 如何选择合适的 K 值? 1. 肘部法(Elbow Me…

RabbitMQ面试精讲 Day 16:生产者优化策略与实践

【RabbitMQ面试精讲 Day 16】生产者优化策略与实践 开篇 欢迎来到"RabbitMQ面试精讲"系列第16天,今天我们聚焦RabbitMQ生产者优化策略与实践。在消息队列系统中,生产者的性能表现直接影响整个系统的吞吐量和可靠性。掌握生产者优化技巧不仅能…