Jupyter远程使用摄像头、WebRTC聊天室、实时视频处理
前言
使用ipywebrtc组件,获取本地视频流并传输到远程Jupyter服务器,由服务器处理过视频后再回传到本地,最后由ipywidgets.Image
组件展示。
效果体验
建议使用Chrome浏览器
前往官方示例并启用摄像头,即可体现实际效果。
如果你想更深刻地体验示范代码,可以去Binder打开任意一个.ipynb
文件,一步步运行即可。
准备工作
需要先安装ipywebrtc
组件,有两种方法,简单的办法是直接通过pip
安装(需要jupyter版本在5.3及以上):
1
2pip install ipywebrtc
第二种是通过github安装最新预览版:
1
2
3
4
5
6git clone https://github.com/maartenbreddels/ipywebrtc cd ipywebrtc pip install -e . jupyter nbextension install --py --symlink --sys-prefix ipywebrtc jupyter nbextension enable --py --sys-prefix ipywebrtc
如果你使用的是jupyter lab,那么只需要在终端运行以下语句:
1
2jupyter labextension install jupyter-webrtc
使用方法
完成准备工作后,首先需要在Jupyter文件中引用ipywebrtc
库,然后创建一个流,可用的流请见下文组件介绍部分,这里以CameraStream
为例,使用本地前置摄像头:
1
2
3
4from ipywebrtc import CameraStream camera = CameraStream.facing_user(audio=False) camera
不出意外的话,Chrome浏览器会弹窗询问是否允许网页使用摄像头,选择允许后,就可在输出区看到摄像头拍到的视频了。
如果Chrome不弹出提示,而是显示
Error creating view for media stream: Only secure origins are allowed
,则表示浏览器认为当前网站不安全(没有使用https
连接),因此禁用了摄像头。最简单的解决方法是在Chrome快捷方式的“目标”一栏最后加上--unsafely-treat-insecure-origin-as-secure="http://host_ip:port"
并重启即可(把host_ip:port
修改为自己的服务器地址)
但这时候,视频还只是在本地显示,并没有上传到Python内核所在的服务器中(假设你使用的是远程服务器),因此也就没办法在Python上下文中获取视频内容。
所以下一步,我们需要创建一个ImageRecorder
来记录流媒体,并以图片的形式发送给服务器上的Python内核:
1
2
3
4from ipywebrtc import ImageRecorder image_recorder = ImageRecorder(stream=camera) image_recorder
运行这段代码,会显示ImageRecorder
组件,点击组件上的相机图标,即可抓拍来自stream
的画面。
之后,再通过访问image_recorder.image.value
并转换成Pillow
格式,即可在Python内获取该图片:
1
2
3
4import PIL.Image import io im = PIL.Image.open(io.BytesIO(image_recorder.image.value))
如果不需要处理,仅需要预览,则直接显示image_recorder.image
即可,它是一个ipywidgets.Image
组件,本身就有展示图片的功能。
如果需要使用opencv-python
处理图片,可以通过canvas = numpy.array(im)[..., :3]
得到cv2
能处理的图像数组,处理之后可以在matplotlib
中展示图片,但我更推荐使用ipywidgets.Image
组件:
1
2
3
4
5from ipywidgets import Image out = Image() out.value = cv2.imencode('.jpg', cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB))[1].tobytes() # canvas出处见上文说明部分 out
或者如果你不希望再引入cv2
,也可以按照官方给出的方式:
1
2
3
4
5
6
7
8from ipywidgets import Image out = Image() im_out = PIL.Image.fromarray(canvas) f = io.BytesIO() im_out.save(f, format='png') out.value = f.getvalue() out
处理视频
目前我们仅介绍了怎么抓取一张图片,处理并展示,这些都可以很容易地通过官方文档学会。那么我们怎样才能连续不停地抓取一个视频里的各个帧,对其处理后再一一展示呢?下面将介绍我总结出来的ImageRecorder
连续抓取的方法。
之所以连续抓取依然使用
ImageRecorder
而不是VideoRecorder
,是因为VideoRecorder
抓取视频必须有开始和结束,只有结束后才能对抓取的整个视频片段做处理,这达不到“实时”的要求。
我分析了作者的代码后发现,并没有类似于recorder.record()
或recorder.take_photo()
之类的函数,而ImageRecorder
前端抓取到下一帧图像后,会通知Python内核把ImageRecorder
的recording
属性设置为True
,然后在抓取一张图片后,recording
属性会自动变为False
。因此我就想尝试一种循环机制,在每次处理完上一帧图片后,自动设置recording
为True
以抓取下一帧图片。
然而经过实验,使用while
循环或for
循环都是行不通的,这大概是因为Jupyter的前端是由Javascript渲染,单纯在后端Python环境下改变属性而不通知前端,是无法继续抓取的。
这个原因仅是我的个人猜测,因为时间原因没有去细看前端文件与底层逻辑,所以理解可能不正确,欢迎在评论区指正。
最后参考了Github上martinRenou的帖子,想到了下面这种模式,来完成连续的实时视频处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import io import PIL.Image import numpy as np from ipywidgets import Image, VBox, HBox, Widget, Button from IPython.display import display from ipywebrtc import CameraStream, ImageRecorder VIDEO_WIDTH = 640 # 窗口宽度,按需调整 VIDEO_HEIGHT = 480 # 窗口高度,按需调整 camera = CameraStream(constraints= {'facing_mode': 'user', 'audio': False, 'video': { 'width': VIDEO_WIDTH, 'height': VIDEO_HEIGHT} }) # 另一种CameraStream创建方式,参考下文组件介绍部分 image_recorder = ImageRecorder(stream=camera) out = Image(width=VIDEO_WIDTH, height=VIDEO_HEIGHT) FLAG_STOP = False # 终止标记 def cap_image(_): # 处理ImageRecord抓取到的图片的过程 if FLAG_STOP: return # 停止处理 im_in = PIL.Image.open(io.BytesIO(image_recorder.image.value)) im_array = np.array(im_in)[..., :3] canvas = process(im_array) # process是处理图像数组的函数,这里没写出来,各位按处理需要自己写即可 im_out = PIL.Image.fromarray(canvas) f = io.BytesIO() im_out.save(f, format='png') out.value = f.getvalue() image_recorder.recording = True # 重新设置属性,使ImageRecorder继续抓取 # 注册抓取事件,参考我另一篇Blog:https://qxsoftware.blog.csdn.net/article/details/86708381 image_recorder.image.observe(cap_image, names=['value']) # 用于停止抓取的按钮 btn_stop = Button(description="Stop", tooltip='click this to stop webcam', button_style='danger') # btn_stop的处理函数 def close_cam(_): FLAG_STOP= True Widget.close_all() btn_stop.on_click(close_cam) # 注册单击事件 # Run this section and Press the Camera button to display demo display(VBox([HBox([camera, image_recorder, btn_stop]), out]))
在Jupyter中运行这段代码后,会出现一个本地camera预览框、一个ImageRecorder
抓取框、一个红色的Stop按钮,以及一个尚无图片的Image
组件。
点击ImageRecorder
上的相机按钮,会激活cap_image
函数,之后处理过的图片就会在Image
组件里展示,接下来直到点击Stop,这个过程都会重复进行。
只有在点击相机之后,更后面的Jupyter Cells才能正常访问image_recorder.image
,不然会报错OSError: cannot identify image file
。
这里最关键的部分,就是在
ImageRecorder.image
的observer
事件注册函数里添加的image_recorder.recording = True
语句。至于为何在注册函数外添加这句话无效,就需要研究一下前后端的联系了。
需要注意的是,注册函数cap_image
里出现的Error不会引发中断,因此如果运行后Image
组件不显示图像,可能是cap_image
发生了Error,这可以通过把cap_image
里的内容提出来放到新的Cell中去验证。
组件介绍
ipywebrtc
中,可用的流媒体有:
-
VideoStream
:可以由VideoStream.from_file(path)
获取本地视频,或由VideoStream.from_url(url)
获取网络视频 -
CameraStream
:通过本地摄像设备或者网络摄像头(webcam)获取媒体流,创建方法有两种:- 第一种:
复制代码1
2
3
4
5
6camera = CameraStream(constraints= {'facing_mode': 'user', # 'user表示前置摄像头,'environment'表示后置摄像头 'audio': False, # 是否同时获取音频(需要硬件支持) 'video': { 'width': 640, 'height': 480 } # 获取视频的宽高 })
- 第二种:
front_camera = CameraStream.facing_user(audio=False)
:前置摄像头
back_camera = CameraStream.facing_environment(audio=False)
:后置摄像头
-
AudioStream
:音频流,与VideoStream
的创建方式相同 -
WidgetStream
:通过WidgetStream(widget=target)
指定widget
,可以将任何ipywidget
实例的输出创建为媒体流
这些媒体流都继承自
MediaStream
类
除流媒体组件外,还有记录器组件,用于记录前端Javascript获取到的流并发送到Python内核,其中ImageRecorder
已经在示例里介绍得很详细,而VideoRecorder
用法与之类似,可参阅官方文档。
最后还有ipywebrtc.webrtc
中的组件,经过测试目前还有些BUG,可参考Chat视频聊天室。
更多内容(如ipyvolumn
系列)会在以后更新。
原创于CSDN,转载请注明出处:https://qxsoftware.blog.csdn.net/article/details/89513815。有任何问题或想法请在下方留下评论~~
最后
以上就是兴奋小猫咪最近收集整理的关于Jupyter远程服务器使用本地摄像头、WebRTC实现聊天室、实时视频处理Jupyter远程使用摄像头、WebRTC聊天室、实时视频处理的全部内容,更多相关Jupyter远程服务器使用本地摄像头、WebRTC实现聊天室、实时视频处理Jupyter远程使用摄像头、WebRTC聊天室、实时视频处理内容请搜索靠谱客的其他文章。
发表评论 取消回复