当前位置: 首页 > news >正文

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

效果图
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.hskmm.com/?act=detail&tid=31629

相关文章:

  • WSL2内部挂载NFS共享文件夹
  • 2025 年电力金具厂家最新推荐排行榜:覆盖出口 / 玛钢电力金具 / 联板 / 横担等品类,权威解析优质厂家选择方向
  • 达梦定时任务更新阻塞信息到表
  • 左值,右值和移动语义
  • 2025年千斤顶厂家最新权威推荐排行榜:液压千斤顶、机械千斤顶、电动千斤顶源头厂家综合实力深度解析
  • VKD104CR是永嘉微VINKA推出低功耗2路触摸芯片该芯片具有较高的集成度
  • Cookie如何设置HTTPOnly和Secure 以防止XSS跨站脚本攻击
  • STM32学习路线!600+讲课程!软硬件兼修:裸机+RTOS+LVGL+硬件设计+项目实战 (STM32多核心开发板)
  • zerotier自建planet内网穿透详细配置教程 - IT苦行僧
  • 【2025-10-11】适应变化
  • C语言的学习——常量
  • 会充电的CANoe,高效完成即插即充(PnC)智能充电功能测试
  • Bridge 2025超详细保姆级下载安装全教程(含软件下载)
  • legit torrents, legit trackers
  • 2025年锅炉厂家最新权威推荐榜:燃气采暖锅炉/电热水锅炉/生物质锅炉/真空热水锅炉/蒸汽发生器全品类深度解析
  • c语言之对齐函数代码示例
  • PyAutoGUI库自动化测试脚本工具模拟键盘鼠标操作
  • 什么是Barriers IO
  • 2025 年床上用品厂家最新推荐排行榜:从老牌实力到新锐创新的品质之选,涵盖批发与婚嫁选购指南
  • MySQL中NULL值的5个反直觉行为,扭到了吗?
  • 2025 年射线防护服生产厂家最新推荐排行榜:覆盖 X 射线 / 医用 / 中子射线防护服及防护裙、防护屏等品类,为医疗安检领域精选优质品牌
  • 2025 最新阳澄湖大闸蟹权威推荐排行榜,揭秘优质品牌的独特魅力大闸蟹蟹卡 / 大闸蟹礼盒 / 大闸蟹礼券 / 好蟹汇大闸蟹选择指南
  • 碳中和背景下的能源数字化:MyEMS 为企业提供精准碳管理方案
  • 在 VS Code 中集成 LaTeX 环境并创建第一个文档
  • 2025 年月子会所推荐:女王臻瑷专注母婴护理 10 年,西安口碑之选的高端母婴护理服务解析
  • Ai元人文:思想的归乡——从悟空之眼到未来哪吒
  • 国产测试用例管理工具市场格局解析:四大产品如何赋能企业数字化转型
  • 桌面预测类编写,桌面%雷达,信号预测%系统构建,基于python,tk,scikit-learn机器学习算法实现,桌面预支持向量机分类算法,CSV无数据库
  • C语言的学习——梦开始的地方
  • 学习 n8n 心得