将原始 OpenCV 图像通过管道传输到 FFmpeg
Posted
技术标签:
【中文标题】将原始 OpenCV 图像通过管道传输到 FFmpeg【英文标题】:Pipe raw OpenCV images to FFmpeg 【发布时间】:2011-08-15 01:46:30 【问题描述】:这是一个使用 OpenCV 的 python 绑定读取网络摄像头的相当简单的示例:
'''capture.py'''
import cv, sys
cap = cv.CaptureFromCAM(0) # 0 is for /dev/video0
while True :
if not cv.GrabFrame(cap) : break
frame = cv.RetrieveFrame(cap)
sys.stdout.write( frame.tostring() )
现在我想将输出通过管道传输到 ffmpeg,如下所示:
$ python capture.py | ffmpeg -f image2pipe -pix_fmt bgr8 -i - -s 640x480 foo.avi
遗憾的是,我无法完全正确地使用 ffmpeg 魔法咒语,并且失败了
libavutil 50.15。 1 / 50.15。 1 libavcodec 52.72。 2 / 52.72。 2 libav 格式 52.64。 2 / 52.64。 2 libavdevice 52. 2. 0 / 52. 2. 0 libavfilter 1.19. 0 / 1.19。 0 libswscale 0.11。 0 / 0.11。 0 libpostproc 51. 2. 0 / 51. 2. 0 将#0 avi 输出到“out.avi”: 流 #0.0:视频:flv、yuv420p、640x480、q=2-31、19660 kb/s、90k tbn、30 tbc [image2pipe @ 0x1508640]max_analyze_duration 已达到 [image2pipe @ 0x1508640]从比特率估计持续时间,这可能不准确 输入 #0,image2pipe,来自 'pipe:': 持续时间:不适用,比特率:不适用 流 #0.0:视频:0x0000、bgr8、25 fps、25 tbr、25 tbn、25 tbc swScaler: 0x0 -> 640x480 是无效的缩放尺寸 捕获的帧肯定是 640x480。 我很确定 OpenCV 图像类型 (IplImage) 的像素顺序是 GBR,每个通道一个字节。至少,这似乎是从相机中出来的。我不是 ffmpeg 大师。有没有人成功做到这一点?
【问题讨论】:
天哪!将输出通过管道传输到 ffmpeg 的想法非常聪明! 我将sys.stdout.write( frame.tostring() )
替换为 sys.stdout.buffer.write(cv2.imencode(".jpg", frame)[1].tobytes())
以使其正常工作。
【参考方案1】:
费了一番功夫,但我用 FFmpeg rawvideo demuxer 搞定了:
python capture.py | ffmpeg -f rawvideo -pixel_format bgr24 -video_size 640x480 -framerate 30 -i - foo.avi
由于原始视频中没有标头指定假定的视频参数,因此用户必须指定它们才能正确解码数据:
-framerate
设置输入视频帧率。默认值为 25。
-pixel_format
设置输入视频像素格式。默认值为 yuv420p。
-video_size
设置输入视频大小。没有默认值,因此必须明确指定此值。
对于高级用户来说,这里有一些额外的东西。同样的事情,但使用 VLC 将实时输出流式传输到网络,Flash 格式:
python capture.py | cvlc --demux=rawvideo --rawvid-fps=30 --rawvid-width=320 --rawvid-height=240 --rawvid-chroma=RV24 - --sout "#transcodevcodec=h264,vb=200,fps=30,width=320,height=240:stdaccess=httpmime=video/x-flv,mux=ffmpegmux=flv,dst=:8081/stream.flv"
编辑: 使用 ffmpeg 和 ffserver 创建 webm 流
python capture.py | ffmpeg -f rawvideo -pixel_format rgb24 -video_size 640x480 -framerate 25 -i - http://localhost:8090/feed1.ffm
【讨论】:
是否有其他人无法让 ffmpeg 获取输出帧率(在这种情况下为“-r 30”)?无论我做什么,我的速度都是 60fps。由于相机硬件的原因,输入帧速率为 30fps,这使得慢动作视频成为可能。汪汪。 总的来说,VLC 似乎比 ffmpeg/ffserver 组合更稳定。 ffserver 一直在我身上发生段错误。 让我们continue this discussion in chat 嗨 @dopplesoldner ,当我尝试 ffmpeg 到 Web 获取问题时,如果您查看该问题,我将非常感激。输入 #0,rawvideo,来自 'pipe:':持续时间:N/A,开始:0.000000,比特率:184320 kb/s Stream #0:0:视频:rawvideo (RGB[24] / 0x18424752), rgb24, 640x480, 184320 kb/s, 25 tbr, 25 tbn, 25 tbc [NULL @ 0xa38e40] 找不到适合“localhost:8090/feed1.ffm”localhost:8090/feed1.ffm 的输出格式:无效参数【参考方案2】:我有点晚了,但是我强大的VidGear
Python 库在任何平台上自动将 OpenCV 帧流水线化到 FFmpeg 的过程。这是一个基本的python示例:
# import libraries
from vidgear.gears import WriteGear
import cv2
output_params = "-vcodec":"libx264", "-crf": 0, "-preset": "fast" #define (Codec,CRF,preset) FFmpeg tweak parameters for writer
stream = cv2.VideoCapture(0) #Open live webcam video stream on first index(i.e. 0) device
writer = WriteGear(output_filename = 'Output.mp4', compression_mode = True, logging = True, **output_params) #Define writer with output filename 'Output.mp4'
# infinite loop
while True:
(grabbed, frame) = stream.read()
# read frames
# check if frame empty
if not is grabbed:
#if True break the infinite loop
break
# do something with frame here
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# write a modified frame to writer
writer.write(gray)
# Show output window
cv2.imshow("Output Frame", frame)
key = cv2.waitKey(1) & 0xFF
# check for 'q' key-press
if key == ord("q"):
#if 'q' key-pressed break out
break
cv2.destroyAllWindows()
# close output window
stream.release()
# safely close video stream
writer.close()
# safely close writer
来源:https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/usage/#using-compression-mode-with-opencv
您可以查看VidGear Docs 了解更多高级应用程序和功能。
希望有帮助!
【讨论】:
【参考方案3】:不确定这是特定于 Mac OS 还是特定于 python3,但我需要将框架转换为字符串才能使其对我有用,如下所示:
sys.stdout.write(str(frame.tostring()))
【讨论】:
只是评论质量【参考方案4】:我花了一个小时才弄清楚默认情况下,Windows 管道不是二进制的。这会导致一些字节(特别是换行符)被修改/省略,并且生成的视频正在缓慢移动,因为帧大小不是恒定的。
要解决这个问题,修改后的 python 文件:
"""
videoCapture.py
"""
import cv2, sys
import time
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
cap = cv2.VideoCapture(0) # 0 is for /dev/video0
while True :
ret, frm = cap.read()
sys.stdout.write( frm.tostring() )
要测试传输原始视频是否成功,请使用 ffplay。确保您指定的帧速率高于来自管道的帧速率,否则视频将开始滞后
python videoCapture.py | ffplay -f rawvideo -pix_fmt bgr24 -s 640x480 -framerate 40 -i -
【讨论】:
谢谢@hgbae,我尝试了python 3.8的解决方案,我不得不使用“sys.stdout.buffer.write”,否则它会给出“无法写入字节,预期字符串”错误. ffplay 命令也很有帮助。给来这里的人做个简短的说明:“-s”是一个重要参数,它是您的视频分辨率,如果没有适当的值,视频将出现别名。以上是关于将原始 OpenCV 图像通过管道传输到 FFmpeg的主要内容,如果未能解决你的问题,请参考以下文章
node.js - 将图像缓存到文件系统并将图像通过管道传输到响应