英文:
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
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
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".
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论