Python 中如何在内存中优雅地提取视频帧?

48 天前
 Haku

通过网络传输过来的视频,如何在不保存为文件的情况下,用工具提取视频帧呢?

一开始以为很容易,但是找了很久发现 Opencv 和 ffmpeg 都没有直接从内存中提取的方法,都需要通过一个临时文件才能提取。而目目前需求上需要优化处理视频的时间,所以想着修改这个步骤。

2786 次点击
所在节点    Python
32 条回复
Anarchy
48 天前
@Anarchy 给个例子:
with av.open(url, options=options) as container:
video = container.streams.video[0]
for i,t in enumerate([10,15,20,25,30]):
container.seek(int(t/video.time_base)+video.start_time, backward=True, stream=video)
frame = next(container.decode(video))
frame.to_image().save(target_folder/f'frame{i}.png')
Haku
48 天前
@stebest
请问下,cv2 读取视频我只找到了 VideoCapture ,但是 VideoCapture 却只能读文件了。
还是说能用读图片的方式,用 imdecode 等从视频二进制流里面直接读取到图片吗?
Haku
48 天前
@Anarchy pyav 我知道,不过我这里环境特殊,装不上这个包所以暂时没考虑。
sweelia
48 天前
@Haku https://github.com/opencv/opencv/pull/25584 不知多久才能合入主线,有条件还是建议走 ffmpeg 的路子,控制更精细,遇到问题也有解决方案。按经验,有些推流器封装不太标准,需要野路子 hack 兼容。
stebest
48 天前
@Haku 如果只是希望在内存中方便读取,使用 tmpfile import tempfile
import cv2

my_video_bytes = download_video_in_memory()

with tempfile.NamedTemporaryFile() as temp:
temp.write(my_video_bytes)

video_stream = cv2.VideoCapture(temp.name)

# do your stuff.
stebest
48 天前
不过实际上还是会在本地创建文件,video capture 本身不支持直接从 memory 中读取,如果是自己控制数据流,用一个迭代器或者生成器把每一帧转 cv mat 就可以
stebest
48 天前
如果还是希望用 opencv video capture 处理,1.可以将视频流使用虚拟摄像头输出,用 opencv 打开即可。
2.使用流媒体协议,opencv video capture 应该也直接支持。
3.使用 videogear 的 netgear ,一个比较简单的例子,具体可以去看文档
server end

```
# import required libraries
from vidgear.gears import VideoGear
from vidgear.gears import NetGear

# open any valid video stream(for e.g `test.mp4` file)
stream = VideoGear(source="test.mp4").start()

# Define Netgear Server with default parameters
server = NetGear()

# loop over until KeyBoard Interrupted
while True:

try:

# read frames from stream
frame = stream.read()

# check for frame if Nonetype
if frame is None:
break

# {do something with the frame here}

# send frame to server
server.send(frame)

except KeyboardInterrupt:
break

# safely close video stream
stream.stop()

# safely close server
server.close()
```

client end

```
# import required libraries
from vidgear.gears import NetGear
import cv2


# define Netgear Client with `receive_mode = True` and default parameter
client = NetGear(receive_mode=True)

# loop over
while True:

# receive frames from network
frame = client.recv()

# check for received frame if Nonetype
if frame is None:
break

# {do something with the frame here}

# Show output window
cv2.imshow("Output Frame", frame)

# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break

# close output window
cv2.destroyAllWindows()

# safely close client
client.close()
```
vituralfuture
48 天前
楼上 bytesio 是一个方法,直接放到 tmpfs 里也行,tmpfs 的 backend 就是内存
ClericPy
48 天前
tmpfs 比较简单,也就是常见的 /dev/shm
SP00F
48 天前
走内存,在并发大文件的情况下。。怎么解决内存占用的问题……

走内存例如 golang 中的 gin 上传文件一样,在读取文件都是 io ,调用 ffmpeg 或者 openvc 读取文件最后不都是 io 吗(个人拙见)😳
longredzzz
48 天前
Lihanx9
42 天前
感觉没有那么复杂吧...

VideoCapture 本身支持使用 HTTP 协议的地址作为视频文件路径,如果没有特殊的请求限制,直接让 VideoCapture 自己处理流,然后直接遍历帧、或者设置 cv2.CAP_PROP_POS_FRAMES 或者 cv2.CAP_PROP_POS_MSEC 读指定帧都是可以的。不知道这种方式是不是符合需求。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1061921

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX