16

How can I pipe openCV images to ffmpeg (running ffmpeg as a subprocess)? (I am using spyder/anaconda)

I am reading frames from a video file and do some processing on each frame.

import cv2   
cap = cv2.VideoCapture(self.avi_path)
img = cap.read()
gray = cv2.cvtColor(img[1], cv2.COLOR_BGR2GRAY)
bgDiv=gray/vidMed #background division

then, to pipe the processed frame to ffmpeg, I found this command in a related question:

sys.stdout.write( bgDiv.tostring() )

next, I am trying to run ffmpeg as a subprocess:

cmd='ffmpeg.exe -f rawvideo -pix_fmt gray -s 2048x2048 -r 30 -i - -an -f avi -r 30 foo.avi'
sp.call(cmd,shell=True)

(this also from the mentioned post) However, this fills my IPython console with cryptic hieroglyphs and then crashes it. any advice?

ultimately, I would like to pipe out 4 streams and have ffmpeg encode those 4 streams in parallel.

2
  • Is there a reason why you wouldn't want to use OpenCV VideoWriter interface to write out your video? I don't think you can pipe out 4 streams at once.
    – mirosval
    Commented Dec 9, 2015 at 7:26
  • 1
    I was hoping this would be faster for saving the video. I noticed that my openCV read-write example above using VideoWriter took about twice as long as a pure ffmpeg encoding of the same input video (granted, the ffmpeg encoding didn't include the background division - which is why I gave openCV a try in the first place). I was hoping to use named pipes...
    – jlarsch
    Commented Dec 9, 2015 at 7:57

3 Answers 3

25

I had similar problem once. I opened an issue on Github, turns out it may be a platform issue.

Related to your question, you can as well pipe OpenCV images to FFMPEG. Here's a sample code:

# This script copies the video frame by frame
import cv2
import subprocess as sp

input_file = 'input_file_name.mp4'
output_file = 'output_file_name.mp4'

cap = cv2.VideoCapture(input_file)
ret, frame = cap.read()
height, width, ch = frame.shape

ffmpeg = 'FFMPEG'
dimension = '{}x{}'.format(width, height)
f_format = 'bgr24' # remember OpenCV uses bgr format
fps = str(cap.get(cv2.CAP_PROP_FPS))

command = [ffmpeg,
        '-y',
        '-f', 'rawvideo',
        '-vcodec','rawvideo',
        '-s', dimension,
        '-pix_fmt', 'bgr24',
        '-r', fps,
        '-i', '-',
        '-an',
        '-vcodec', 'mpeg4',
        '-b:v', '5000k',
        output_file ]

proc = sp.Popen(command, stdin=sp.PIPE, stderr=sp.PIPE)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    proc.stdin.write(frame.tostring())

cap.release()
proc.stdin.close()
proc.stderr.close()
proc.wait()
9
  • I am getting an error: pipe.proc.stdin.write(frame.tostring()) AttributeError: 'Popen' object has no attribute 'proc
    – jlarsch
    Commented Dec 9, 2015 at 10:24
  • When I change your code to pipe.stdin.write(frame.tostring()), I get another error: pipe.stdin.write(frame.tostring()) IOError: [Errno 22] Invalid argumen
    – jlarsch
    Commented Dec 9, 2015 at 10:30
  • This is python 2.7 code. What version is your python? Commented Dec 9, 2015 at 11:06
  • I had tried these changes already. With this code I am getting: IOError: [Errno 22] Invalid argument.
    – jlarsch
    Commented Dec 9, 2015 at 11:57
  • 1
    For future users who stumble upon this answer. While @Ekrem Doğan answer works for Python 2, similar code can be found for Python 3 with better comments and explanation [here][1]. [1]: stackoverflow.com/a/61281547/2127561 Commented Feb 17, 2022 at 14:11
7

I'm Kind of late, But my powerful VidGear Python Library automates the process of pipelining OpenCV frames into FFmpeg on any platform with its WriteGear API's Compression Mode. OP, You can implement your answer as follows:

# import libraries
from vidgear.gears import WriteGear
import cv2

output_params = {"-s":"2048x2048", "-r":30} #define 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

Source: https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/usage/#using-compression-mode-with-opencv

You can check out VidGear Docs for more advanced applications and features.

Hope that helps!

9
  • Even a bit later... op is going opposite direction. Can your api send cv input to a Youtube live stream? Example of the WriteGear usage?
    – dbmitch
    Commented Jun 29, 2020 at 4:46
  • @dbmitch Thanks, answer updated. Can you send cv input to a Youtube live stream with FFmpeg directly? If yes, then WriteGear can do it, if not, than the answer is no.
    – abhiTronix
    Commented Jun 29, 2020 at 6:22
  • Okay - I'll give it a shot - but what would I gain with WriteGear if I can do it
    – dbmitch
    Commented Jun 29, 2020 at 21:19
  • effortlessness and flexibility.
    – abhiTronix
    Commented Jun 30, 2020 at 3:09
  • A good example would be better - WriteGear requires an output filename - how would you use to send it to a stream? No examples on your github. Can you use your ffmpeg command with a cv2 stream?
    – dbmitch
    Commented Jun 30, 2020 at 18:38
0

You can use this pkg. ffmpegcv has Reader and Writer in ffmpeg backbone, similar to cv2.

#!pip install ffmpegcv
import ffmpegcv
vfile_in = 'A.mp4'
vfile_out = 'A_h264.mp4'
vidin = ffmpegcv.VideoCapture(vfile_in)
w, h = vidin.width, vidin.height
vidout = ffmpegcv.VideoWriter(vfile_out, 'h264_nvenc', vidin.fps, (w, h))

for frame in vidin:
    vidout.write(frame)

vidin.release()
vidout.release()

Not the answer you're looking for? Browse other questions tagged or ask your own question.