在浏览器播放多个视频 opencv+Nicegui

news/2025/10/15 16:00:11/文章来源:https://www.cnblogs.com/lijun-goods/p/19143595

效果图
image

代码:

from nicegui import ui, native
import cv2
import numpy as np
import base64
import time
import threading
import osclass VideoStream:"""单个视频流管理类"""def __init__(self, video_source, viewer):self.video_source = video_sourceself.viewer = viewerself.cap = Noneself.is_playing = Falseself.target_fps = 10self.timer = None# 双缓冲机制self.current_frame = Noneself.next_frame = Noneself.frame_ready = Falseself.last_frame_time = 0# 色彩校正参数self.color_correction_enabled = True# UI组件self.image = Noneself.start_btn = Noneself.stop_btn = Noneself.status_label = Noneself.color_checkbox = None# 视频信息self.is_file = isinstance(video_source, str)self.video_name = os.path.basename(video_source) if self.is_file else f'摄像头 {video_source}'# 线程安全锁self.lock = threading.RLock()def create_ui(self, parent_container):"""为单个视频流创建UI组件"""with parent_container:with ui.card().classes('w-full h-full flex flex-col'):header = ui.row().classes('justify-between items-center')with header:ui.label(self.video_name).classes('font-semibold')self.color_checkbox = ui.checkbox('色彩校正', value=True,on_change=lambda e: setattr(self, 'color_correction_enabled', e.value))# 视频显示区域with ui.column().classes('flex-1 justify-center items-center bg-gray-100'):self.image = ui.interactive_image().classes('max-w-full max-h-[200px]')# 控制区域with ui.row().classes('justify-between items-center mt-2'):control_buttons = ui.row().classes('gap-2')with control_buttons:self.start_btn = ui.button('开始', on_click=lambda: self.start())self.stop_btn = ui.button('停止', on_click=lambda: self.stop())self.stop_btn.disable()self.status_label = ui.label('就绪').classes('text-sm')def start(self):"""开始播放视频"""if self.is_playing:returnwith self.lock:self.cap = cv2.VideoCapture(self.video_source)# 配置摄像头/视频属性self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)self.cap.set(cv2.CAP_PROP_FPS, self.target_fps)if not self.cap.isOpened():if self.is_file:self.status_label.set_text(f'无法打开视频文件: {self.video_name}')else:self.status_label.set_text(f'无法打开摄像头 {self.video_source}')returnself.is_playing = Trueself.start_btn.disable()self.stop_btn.enable()self.status_label.set_text('播放中...')# 使用timer更新视频帧self.last_frame_time = time.time()self.timer = ui.timer(interval=1/self.target_fps, callback=self.update_frame)def stop(self):"""停止播放视频"""with self.lock:self.is_playing = Falseif self.timer:self.timer.cancel()self.timer = Noneif self.cap:self.cap.release()self.cap = Noneself.start_btn.enable()self.stop_btn.disable()self.status_label.set_text('已停止')# 显示黑色帧black_frame = np.zeros((480, 640, 3), dtype=np.uint8)self._update_ui(black_frame)def update_frame(self):"""更新视频帧"""if not self.is_playing or not self.cap or not self.cap.isOpened():self.stop()return# 获取一帧ret, frame = self.cap.read()# 对于视频文件,如果播放结束则循环播放if not ret:if self.is_file:# 重新打开视频文件以循环播放self.cap.release()self.cap = cv2.VideoCapture(self.video_source)ret, frame = self.cap.read()if not ret:self.status_label.set_text('无法重新加载视频文件')self.stop()returnelse:self.status_label.set_text('无法获取视频帧')self.stop()return# 使用双缓冲with self.lock:self.next_frame = frameself.frame_ready = True# 帧率控制current_time = time.time()elapsed = current_time - self.last_frame_timeif elapsed >= 1/self.target_fps:if self.frame_ready:with self.lock:self.current_frame = self.next_frameself.frame_ready = False# 更新UIself._update_ui(self.current_frame)self.last_frame_time = current_timedef _update_ui(self, frame):"""更新显示的图像"""# 调整图像大小frame = cv2.resize(frame, (640, 480))# 色彩校正处理if self.color_correction_enabled:pass# 转换为RGB格式# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)else:# 不进行色彩校正,使用原始BGR格式frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  # 仍然需要转换为RGB用于显示# 编码为JPEGencode_params = [cv2.IMWRITE_JPEG_QUALITY, 90]_, encoded_image = cv2.imencode('.jpg', frame, encode_params)# 转换为base64base64_image = base64.b64encode(encoded_image).decode('utf-8')# 更新图像self.image.set_source(f'data:image/jpeg;base64,{base64_image}')class MultiVideoViewer:"""多视频文件查看器类"""def __init__(self, video_sources=[]):# 如果未提供视频源,使用默认摄像头self.video_sources = video_sources if video_sources else [0, 1, 2, 3, 4, 5]self.streams = []self.create_ui()def create_ui(self):"""创建用户界面"""with ui.header():ui.label('多视频播放器').classes('text-2xl font-bold')with ui.row().classes('gap-2 ml-auto'):ui.button('全部开始', on_click=self.start_all)ui.button('全部停止', on_click=self.stop_all)# 主内容区 - 网格布局显示视频# 根据视频数量动态调整列数columns = 3if len(self.video_sources) == 8 or len(self.video_sources) == 9:columns = 3 if len(self.video_sources) == 9 else 4self.video_grid = ui.grid(columns=columns).classes('w-full gap-2 p-2')# 初始化视频流self.initialize_streams()def initialize_streams(self):"""初始化所有视频流"""# 清空现有流self.streams.clear()self.video_grid.clear()# 创建新的视频流for source in self.video_sources:stream = VideoStream(source, self)stream.create_ui(self.video_grid)self.streams.append(stream)def start_all(self):"""启动所有视频"""for stream in self.streams:stream.start()def stop_all(self):"""停止所有视频"""for stream in self.streams:stream.stop()# 示例:传入视频文件路径数组
# 请将以下路径替换为您实际的视频文件路径
video_files = [0,  # 视频文件1'video/2.mp4',  # 视频文件2'video/3.mp4',  # 视频文件3'video/4.mp4',  # 视频文件4'video/5.mp4',  # 视频文件5'video/6.mp4',  # 视频文件6'video/7.mp4',  # 视频文件7'video/8.mp4',  # 视频文件8'video/9.mp4',  # 视频文件9
]# 创建应用
multi_viewer = MultiVideoViewer(video_sources=video_files)# 运行应用
ui.run(reload=False, port=native.find_open_port())

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

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

相关文章

WSL2内部挂载NFS共享文件夹

启动NFS服务端 sudo systemctl start nfs-server 编辑/etc/exports可以新增共享目录,下面是例子 # /etc/exports: the access control list for filesystems which may be exported # to NFS clients. See exports(…

2025 年电力金具厂家最新推荐排行榜:覆盖出口 / 玛钢电力金具 / 联板 / 横担等品类,权威解析优质厂家选择方向

引言当前电力行业快速发展,电力金具作为电力输送系统的核心部件,其质量直接影响电力供应的安全与稳定。但市场上电力金具品牌繁杂,既有深耕多年的老牌企业,也有新兴厂商,产品质量、工艺水平、服务能力差异显著,采…

达梦定时任务更新阻塞信息到表

数据库阻塞,一些原因,查不到历史的阻塞信息。配置存储过程,把一些阻塞信息更新到表,然后配置定时任务,定时更新--STEP1:创建辅助记录表: CREATE TABLE TRX_WAIT("STATTIME" TIMESTAMP,"SS" …

左值,右值和移动语义

左值、右值和移动语义 左值和右值 C++的表达式分为左值表达式和右值表达式,右值又分为纯右值和将亡值。 左值和右值的定义:指代非临时对象且不可移动的表达式被称为左值表达式;当对象被用左值时,被使用的是对象的身…

2025年千斤顶厂家最新权威推荐排行榜:液压千斤顶、机械千斤顶、电动千斤顶源头厂家综合实力深度解析

2025年千斤顶厂家最新权威推荐排行榜:液压千斤顶、机械千斤顶、电动千斤顶源头厂家综合实力深度解析行业背景与发展趋势千斤顶作为工业领域不可或缺的起重设备,其技术发展与制造业升级息息相关。随着基础设施建设的持…

VKD104CR是永嘉微VINKA推出低功耗2路触摸芯片该芯片具有较高的集成度

VKD104CR是2通道触摸检测芯片,功耗低、工作电压范围宽以及稳定的触摸检测效果可以广泛的满足不同应用的需求,此触摸检测芯片是专为取代传统按键而设计,内建稳压电路,提供稳定电压给触摸检测电路使用,触摸检测PAD的…

Cookie如何设置HTTPOnly和Secure 以防止XSS跨站脚本攻击

设置HttpOnly和Secure标志于Cookie中是增强Web应用安全性的重要措施。这两个标志帮助防止跨站脚本攻击(XSS)和中间人攻击(MitM)。下面是关于如何设置这些标志的具体步骤: PHP: 在发送cookie时使用setcookie()函数…

STM32学习路线!600+讲课程!软硬件兼修:裸机+RTOS+LVGL+硬件设计+项目实战 (STM32多核心开发板)

私信小编,备注“103开发板”,免费领取教程/源码/项目资料~ 在智能设备深度整合与万物互联加速推进的当下,STM32正从单一功能控制走向复杂系统集成。从智能家居的精准传感控制到工业物联网的实时数据采集,从AIoT终端…

Vue中展示字符串,有换行符的怎么换行展示

Vue中展示字符串,有换行符的怎么换行展示1 简介 1)字符串带了换行符 "你好\r\nhello\r\nworld" 2)使用{{}}直接展示{{rowdetail.lastWeekContent}} 3)展示效果发现没有换行 2 处理 1)使用<pre&g…

zerotier自建planet内网穿透详细配置教程 - IT苦行僧

zerotier自建planet内网穿透详细配置教程 配置过程,遇到不懂的问题,可以直接复制代码或者报错信息给AI,帮助你答疑解惑。 前言 本文使用Zerotier,这个软件官方也提供免费通道,但是很不稳定,所以本文是自建Planet…

【2025-10-11】适应变化

20:00劳作是眼能看见的爱。如果你进行劳作时不是满怀着爱,而是带着厌恶心里,还不如丢下工作,到庙门去等待高高兴兴劳作者们的周济——纪伯伦人性最厌讨的事物之一就是变化。但是,人类的发展只能依靠变化。所以,克…

C语言的学习——常量

常量的输出方式有四种 1.第一种是输出整数整数的输出需要用d来表示占位符 2.第二种是输出小数小数的输出需要用f来表示占位符 3.第三种是输出字符字符的输出需要用c来表示占位符,同时也需要用引号来引注输出的内容 4.…

会充电的CANoe,高效完成即插即充(PnC)智能充电功能测试

ISO 15118-2标准中描述的PnC功能,可以实现插枪即充电,识别、计费信息、充电参数都通过高级别通信在EV和EVSE之间自动交换。简化了电动汽车的充电过程,提高了用户体验,为电动汽车行业带来了更智能、更便捷的充电解决…

Bridge 2025超详细保姆级下载安装全教程(含软件下载)

目录一、Adobe Bridge 2025 软件介绍二、Adobe Bridge 2025 下载方式三、Adobe Bridge 2025 详细安装步骤步骤 1:解压 Bridge 2025 安装包步骤 2:以管理员身份运行安装程序步骤 3:选择是否修改安装路径步骤 4:自定…

legit torrents, legit trackers

http://www.legittorrents.info/ is dead.but that site recommends the following ones:PlaneShift Revision3 gameupdates.org vodo.net Pure Pwnage Cube 2: Sauerbraten Linuxtracker Public Domain Torrents

python 数学计算 32位精度

from decimal import Decimal, getcontext# Set precision high enough to handle 32 decimal places getcontext().prec = 50# Calculate step by step result = Decimal(20) / Decimal(6) / Decimal(3) / Decimal(3)…

2025年锅炉厂家最新权威推荐榜:燃气采暖锅炉/电热水锅炉/生物质锅炉/真空热水锅炉/蒸汽发生器全品类深度解析

2025年锅炉厂家最新权威推荐榜:燃气采暖锅炉/电热水锅炉/生物质锅炉/真空热水锅炉/蒸汽发生器全品类深度解析行业背景与发展趋势锅炉作为工业生产与民用供热领域的核心设备,其技术发展与市场需求正经历深刻变革。随着…

容器技术k8s - 指南

容器技术k8s - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "C…

c语言之对齐函数代码示例

#define ALIGNMENT 8// 内存对齐:将size向上对齐到最近的ALIGNMENT倍数 static size_t align_size(size_t size) {if(size==0)return ALIGNMENT;// 对齐公式:(size + 对齐数 - 1) & ~(对齐数 - 1)return (size +…

PyAutoGUI库自动化测试脚本工具模拟键盘鼠标操作

PyAutoGUI主要用于模拟鼠标和键盘操作,支持Windows、macOS和Linux桌面环境。 其所有用法尽在此处:import pyautogui import timedef moveDemo():print("鼠标位置:",pyautogui.position()) # 显示当前鼠标…