将原始OpenCV图像传输到FFmpeg

29 投票
5 回答
45284 浏览
提问于 2025-04-16 16:37

这里有一个比较简单的例子,教你如何用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
  libavformat   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
Output #0, avi, to 'out.avi':
    Stream #0.0: Video: flv, yuv420p, 640x480, q=2-31, 19660 kb/s, 90k tbn, 30 tbc
[image2pipe @ 0x1508640]max_analyze_duration reached
[image2pipe @ 0x1508640]Estimating duration from bitrate, this may be inaccurate
Input #0, image2pipe, from 'pipe:':
  Duration: N/A, bitrate: N/A
    Stream #0.0: Video: 0x0000, bgr8, 25 fps, 25 tbr, 25 tbn, 25 tbc
swScaler: 0x0 -> 640x480 is invalid scaling dimension
  • 捕获的画面分辨率确实是640x480。
  • 我很确定OpenCV图像类型(IplImage)的像素顺序是GBR,每个通道一个字节。至少,从摄像头传来的数据是这样的。

我对ffmpeg不太熟悉,有没有人成功做到过这个?

5 个回答

2

我不确定这是不是只在Mac OS上或者只在python3中出现的问题,但我发现我需要把这个框架转换成字符串,这样才能让我正常使用,像这样:

sys.stdout.write(str(frame.tostring()))
4

我可能来得有点晚,但我强大的 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文档,了解更多高级应用和功能。

希望这对你有帮助!

38

经过一番折腾,我终于搞定了,使用了FFmpeg的rawvideo解复用器

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 "#transcode{vcodec=h264,vb=200,fps=30,width=320,height=240}:std{access=http{mime=video/x-flv},mux=ffmpeg{mux=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

撰写回答