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
英文地址,請注明出處:http://en.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生產者優化策略與實踐。在消息隊列系統中,生產者的性能表現直接影響整個系統的吞吐量和可靠性。掌握生產者優化技巧不僅能…