无法将NumPy数组(图像)传递给Python中的ffmpeg子进程。

huangapple go评论78阅读模式
英文:

can't pipe in numpy arrays (images) to ffmpeg subprocess in python

问题

代码在从ffmpeg部分读取时卡住了:
raw_frame = lut_process.stdout.read(width * height * 3)

当运行代码时,你得到了以下结果:

flushed
Input #0, rawvideo, from 'fd:':
  Duration: N/A, start: 0.000000, bitrate: 663552 kb/s
  Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24, 1280x720, 663552 kb/s, 30 tbr, 30 tbn
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> rawvideo (native))
Output #0, rawvideo, to 'pipe:1':
  Metadata:
    encoder         : Lavf60.3.100
  Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24(progressive), 1280x720, q=2-31, 663552 kb/s, 30 fps, 30 tbn
    Metadata:
      encoder         : Lavc60.3.100 rawvideo
frame=    0 fps=0.0 q=0.0 size=       0kB time=-577014:32:22.77 bitrate=  -0.0kbits/s speed=N/A  

"read" 永远没有被打印出来。ffmpeg停留在0fps。cv2.imshow不显示。

我尝试了将lut_process.stdin.write(frame.tobytes()) 更改为 lut_process.stdin.write(frame.tostring()),但结果相同。

我尝试在第一次写入ffmpeg之前添加3秒的暂停,以考虑可能是ffmpeg还没有准备好处理帧,但结果仍然相同。

我确信我的网络摄像头正常工作,并且我知道它的视频流是1280x720 30fps。

我成功地显示了使用OpenCV显示网络摄像头流,直接将FFmpeg输入设置为我的网络摄像头,并使用stdout.read获取输出结果,并使用OpenCV显示它。

不知道接下来应该尝试什么。

我使用的是macOS 12.6,OpenCV 4.7.0,ffmpeg 6.0,Python 3.10.11和Visual Studio Code。

任何帮助将不胜感激。

英文:

I'm trying to capture webcam video stream using opencv and pipe raw frames into ffmpeg subprocess, apply 3d .cube lut, bring back those lut applied frames into opencv and display it using cv2.imshow.

This is my code:

import cv2
import subprocess as sp
import numpy as np

lut_cmd = [
            'ffmpeg', '-f', 'rawvideo', '-pixel_format', 'bgr24', '-s', '1280x720', '-framerate', '30', '-i', '-', '-an', '-vf',
            'lut3d=file=lut/luts/lut.cube', '-f', 'rawvideo', 'pipe:1'
        ]

lut_process = sp.Popen(lut_cmd, stdin=sp.PIPE, stdout=sp.PIPE)

width = 1280
height = 720

video_capture = cv2.VideoCapture(0)

while True:
    ret, frame = video_capture.read()

    if not ret:
        break
 
    # Write raw video frame to input stream of ffmpeg sub-process.
    lut_process.stdin.write(frame.tobytes())
    lut_process.stdin.flush()
    print("flushed")

    # Read the processed frame from the ffmpeg subprocess
    raw_frame = lut_process.stdout.read(width * height * 3)
    print("read")
    frame = np.frombuffer(raw_frame, dtype=np.uint8).reshape(height, width, 3)

    cv2.imshow('Video', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

lut_process.terminate()
video_capture.release()

cv2.destroyAllWindows()

code gets stuck at reading from ffmpeg part:
raw_frame = lut_process.stdout.read(width * height * 3)

this is what i get when i run the code:

flushed
Input #0, rawvideo, from 'fd:':
  Duration: N/A, start: 0.000000, bitrate: 663552 kb/s
  Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24, 1280x720, 663552 kb/s, 30 tbr, 30 tbn
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> rawvideo (native))
Output #0, rawvideo, to 'pipe:1':
  Metadata:
    encoder         : Lavf60.3.100
  Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24(progressive), 1280x720, q=2-31, 663552 kb/s, 30 fps, 30 tbn
    Metadata:
      encoder         : Lavc60.3.100 rawvideo
frame=    0 fps=0.0 q=0.0 size=       0kB time=-577014:32:22.77 bitrate=  -0.0kbits/s speed=N/A  

"read" never gets printed. ffmpeg is stuck at 0fps. cv2.imshow doesn't show up.

I tried changing lut_process.stdin.write(frame.tobytes())
to lut_process.stdin.write(frame.tostring()), but result was same.

I tried adding 3 seconds pause before first write to ffmpeg begin, thinking maybe ffmpeg was not ready to process frames, but result was same.

I'm sure that my webcam is working, and I know it's video stream is 1280x720 30fps.

I was successful at
Displaying webcam stream just using opencv,
set FFmpeg input directly to my webcam and get output result using stdout.read, displaying it using opencv.

have no idea what should I try next.

I am using macOS 12.6, openCV 4.7.0, ffmpeg 6.0, python 3.10.11, and visual studio code.

Any help would be greatly appreciated.

答案1

得分: 0

这不是我最整洁或最整齐的代码,但我已经得到了一些工作并想与您分享 - 我可能以后会整理它。我认为问题在于ffmpeg和Python子进程不太兼容,并且存在一些缓冲和死锁的可能性。因此,我将从摄像机中读取视频帧并将其作为单独的线程馈送到ffmpeg中,然后一切正常工作。需要整理和改进错误处理以及用户按下q键退出的功能。

#!/usr/bin/env python3

import cv2
import subprocess as sp
import numpy as np
import sys
import time
import os
import threading

width = 1280
height = 720

def pumpFFMPEG(fd):
    """Read frames from camera and pump into ffmpeg."""
    video_capture = cv2.VideoCapture(0)

    while True:
        ret, frame = video_capture.read()
        frame = cv2.resize(frame, (width, height))
        fd.write(frame.tobytes())
    video_capture.release()

lut_cmd = [
    'ffmpeg', '-nostdin', '-loglevel', 'error', '-f', 'rawvideo', '-pixel_format', 'bgr24', '-video_size', '1280x720', '-i', '-', '-framerate', '30', '-an', '-vf',
    'lut3d=file=invert.cube', '-f', 'rawvideo', 'pipe:1'
]
lut_process = sp.Popen(lut_cmd, bufsize=width*height*3, stdin=sp.PIPE, stdout=sp.PIPE)

thr = threading.Thread(target=pumpFFMPEG, args=(lut_process.stdin,))
thr.start()

while True:

    # Read the processed frame from the ffmpeg subprocess
    raw_frame = lut_process.stdout.read(width*height*3)
    frame = np.frombuffer(raw_frame, dtype=np.uint8).reshape(height, width, 3)
    cv2.imshow('Video', frame)
    cv2.waitKey(1)

cv2.destroyAllWindows()

一些传递给ffmpeg的参数可能是不必要的,您可以尝试逐个删除它们,直到它停止工作。您还可以使用stderr=sp.DEVNULL

我还制作了一个3D LUT。通常,您可以使用ImageMagick创建一个HALD CLUT,如下所示:

magick hald:8 input.png

然后,您可以将Lightroom处理应用于input.png并将其另存为output.png。接下来,您需要生成一个实现该处理的3D CLUT - 我使用了这个工具

生成用于ffmpeg的立方体LUT的命令如下:

./HALDtoCUBE3DLUT.py output.png LUT.cube

与使用Lightroom/Photoshop进行所有操作相比,我只需使用ImageMagick制作一个LUT并一次性反转它:

magick hald:8 -negate output.png
./HALDtoCUBE3DLUT.py output.png invert.cube

请注意,我提到的反转是亮度反转,即“黑色变成白色”,而不是物理反转,即“顶部变成底部”。

英文:

This is not my cleanest, or tidiest piece of code, but I have got something working and wanted to share it with you - I may clean it up later. I think the issue is that ffmpeg and Python subprocesses don't play that well together and there is some buffering going on and chances of deadlock. So, I abstracted out the reading of video frames from the camera and feeding them into ffmpeg as a separate thread and then it all works. It needs tidying up and improvements to error handling and the user pressing q to quit.

#!/usr/bin/env python3

import cv2
import subprocess as sp
import numpy as np
import sys
import time
import os
import threading

width = 1280
height = 720

def pumpFFMPEG(fd):
    """Read frames from camera and pump into ffmpeg."""
    video_capture = cv2.VideoCapture(0)

    while True:
        ret, frame = video_capture.read()
        frame = cv2.resize(frame, (width,height))
        fd.write(frame.tobytes())
    video_capture.release()
        
lut_cmd = [
            'ffmpeg', '-nostdin', '-loglevel', 'error', '-f', 'rawvideo', '-pixel_format', 'bgr24', '-video_size', '1280x720', '-i', '-', '-framerate', '30', '-an', '-vf',
            'lut3d=file=invert.cube', '-f', 'rawvideo', 'pipe:1'
        ]
lut_process = sp.Popen(lut_cmd, bufsize=width*height*3,stdin=sp.PIPE, stdout=sp.PIPE)


thr = threading.Thread(target=pumpFFMPEG, args=(lut_process.stdin,))
thr.start()

while True:
 
    # Read the processed frame from the ffmpeg subprocess
    raw_frame = lut_process.stdout.read(width*height*3)
    frame = np.frombuffer(raw_frame, dtype=np.uint8).reshape(height, width, 3)
    cv2.imshow('Video', frame)
    cv2.waitKey(1)

cv2.destroyAllWindows()

Some of the parameters to ffmpeg are maybe unnecessary, so you can try removing them one-by-one till it stops working. You may also want to use stderr=sp.DEVNULL.


I also made a 3dLUT. Normally, you would make a HALD CLUT with ImageMagick like this:

magick hald:8 input.png

无法将NumPy数组(图像)传递给Python中的ffmpeg子进程。

Then you would apply your Lightroom processing to input.png and save it as output.png. Then you need to generate a 3dCLUT that implements that processing - I did that with this.

The command to generate a cube LUT for ffmpeg would be:

./HALDtoCUBE3DLUT.py output.png LUT.cube

Rather than go through all that palaver with Lightroom/Photoshop, I just made a LUT and inverted it all in one go with ImageMagick:

magick hald:8 -negate output.png
./HALDtoCUBE3DLUT.py output.png invert.cube

无法将NumPy数组(图像)传递给Python中的ffmpeg子进程。

Note that the inversion I am referring to is brightness inversion, i.e. "black becoming white" rather than physical inversion, i.e. "top becoming bottom".

huangapple
  • 本文由 发表于 2023年6月19日 13:12:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76503768.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定