将原始 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的主要内容,如果未能解决你的问题,请参考以下文章

将图像从 python 传输到 C++ 并返回

将原始 PCM 流通过管道传输到 libsndfile

node.js - 将图像缓存到文件系统并将图像通过管道传输到响应

Python OpenCV 图像到字节字符串以进行 json 传输

压缩原始图像缓冲区

如何通过管道传输到文件(NodeJS)