使用FFmpeg、python和opencv显示流

使用FFmpeg、python和opencv显示流,python,opencv,ffmpeg,subprocess,video-streaming,Python,Opencv,Ffmpeg,Subprocess,Video Streaming,情况: 我有一个连接到raspberry pi的basler摄像头,我正试图用FFmpg将其馈送到windows PC中的tcp端口,以监控摄像头前面发生的事情 有效的东西: 我设法在raspberry pi上设置了一个python脚本,该脚本负责记录帧、将帧馈送到管道并将其流式传输到tcp端口。从这个端口,我可以使用FFplay显示流 我的问题: FFplay非常适合快速轻松地测试您的方向是否正确,但我想“读取”流中的每一帧,进行一些处理,然后使用opencv显示流。那,我还不能做到 Mini

情况: 我有一个连接到raspberry pi的basler摄像头,我正试图用FFmpg将其馈送到windows PC中的tcp端口,以监控摄像头前面发生的事情

有效的东西: 我设法在raspberry pi上设置了一个python脚本,该脚本负责记录帧、将帧馈送到管道并将其流式传输到tcp端口。从这个端口,我可以使用FFplay显示流

我的问题: FFplay非常适合快速轻松地测试您的方向是否正确,但我想“读取”流中的每一帧,进行一些处理,然后使用opencv显示流。那,我还不能做到

Minimay Represented,这是我在raspberry pi方面使用的代码:

command = ['ffmpeg',
           '-y',
           '-i', '-',
           '-an',
           '-c:v', 'mpeg4',
           '-r', '50',
           '-f', 'rtsp',
           '-rtsp_transport',
           'tcp','rtsp://192.168.1.xxxx:5555/live.sdp']

p = subprocess.Popen(command, stdin=subprocess.PIPE) 

while camera.IsGrabbing():  # send images as stream until Ctrl-C
    grabResult = camera.RetrieveResult(100, pylon.TimeoutHandling_ThrowException)
    
    if grabResult.GrabSucceeded():
        image = grabResult.Array
        image = resize_compress(image)
        p.stdin.write(image)
    grabResult.Release() 

在我的电脑上,如果我在终端上使用以下FFplay命令,它将工作并实时显示流:

ffplay-rtsp_标志侦听rtsp://192.168.1.xxxx:5555/live.sdp?tcp

在我的电脑上,如果我使用以下python脚本,流将开始,但在
cv2.imshow
函数中失败,因为我不确定如何解码它:

import subprocess
import cv2

command = ['C:/ffmpeg/bin/ffmpeg.exe',
           '-rtsp_flags', 'listen',
           '-i', 'rtsp://192.168.1.xxxx:5555/live.sdp?tcp?', 
           '-']

p1 = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

while True:
    frame = p1.stdout.read()
    cv2.imshow('image', frame)
    cv2.waitKey(1)
有人知道我需要在这两个脚本中更改什么才能开始工作吗?


提前感谢您提供的任何提示。

您可以从
p1.stdout
读取解码帧,将其转换为NumPy数组,并对其进行重塑

  • 更改
    命令
    以获取
    原始视频
    格式和BGR像素格式的解码帧:

     command = ['C:/ffmpeg/bin/ffmpeg.exe',
                '-rtsp_flags', 'listen',
                '-i', 'rtsp://192.168.1.xxxx:5555/live.sdp?tcp?',
                '-f', 'image2pipe',    # Use image2pipe demuxer
                '-pix_fmt', 'bgr24',   # Set BGR pixel format
                '-vcodec', 'rawvideo', # Get rawvideo output format.
                '-']
    
  • p1.stdout
    读取原始视频帧:

     raw_frame = p1.stdout.read(width*height*3)
    
  • 将读取的字节转换为NumPy数组,并将其重塑为视频帧尺寸:

     frame = np.fromstring(raw_frame, np.uint8)
     frame = frame.reshape((height, width, 3))
    
现在可以显示调用cv2.imshow('image',frame)的帧

该解决方案假设您预先知道视频帧的大小(
宽度
高度

下面的代码示例包括一个使用
cv2.VideoCapture
读取
width
height
的部分,但我不确定它是否适用于您的情况(由于
'-rtsp_flags',listen'
(如果有效,您可以尝试使用OpenCV而不是FFmpeg进行捕获)

以下代码是使用公共RTSP流进行测试的完整“工作示例”:

import cv2
import numpy as np
import subprocess

# Use public RTSP Stream for testing
in_stream = 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov'

if False:
    # Read video width, height and framerate using OpenCV (use it if you don't know the size of the video frames).

    # Use public RTSP Streaming for testing:
    cap = cv2.VideoCapture(in_stream)

    framerate = cap.get(5) #frame rate

    # Get resolution of input video
    width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # Release VideoCapture - it was used just for getting video resolution
    cap.release()
else:
    # Set the size here, if video frame size is known
    width = 240
    height = 160


command = ['C:/ffmpeg/bin/ffmpeg.exe',
           #'-rtsp_flags', 'listen',  # The "listening" feature is not working (probably because the stream is from the web)
           '-rtsp_transport', 'tcp',  # Force TCP (for testing)
           '-max_delay', '30000000',  # 30 seconds (sometimes needed because the stream is from the web).
           '-i', in_stream,
           '-f', 'image2pipe',
           '-pix_fmt', 'bgr24',
           '-vcodec', 'rawvideo', '-an', '-']

# Open sub-process that gets in_stream as input and uses stdout as an output PIPE.
p1 = subprocess.Popen(command, stdout=subprocess.PIPE)

while True:
    # read width*height*3 bytes from stdout (1 frame)
    raw_frame = p1.stdout.read(width*height*3)

    if len(raw_frame) != (width*height*3):
        print('Error reading frame!!!')  # Break the loop in case of an error (too few bytes were read).
        break

    # Convert the bytes read into a NumPy array, and reshape it to video frame dimensions
    frame = np.fromstring(raw_frame, np.uint8)
    frame = frame.reshape((height, width, 3))

    # Show video frame
    cv2.imshow('image', frame)
    cv2.waitKey(1)
  
# Wait one more second and terminate the sub-process
try:
    p1.wait(1)
except (sp.TimeoutExpired):
    p1.terminate()

cv2.destroyAllWindows()
示例框架(仅供娱乐):


更新: 使用FFprobe读取宽度和高度:

如果您事先不知道视频分辨率,可以使用FFprobe获取信息

以下是使用FFprobe读取
宽度
高度
的代码示例:

import subprocess
import json

# Use public RTSP Stream for testing
in_stream = 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov'

probe_command = ['C:/ffmpeg/bin/ffprobe.exe',
                 '-loglevel', 'error',
                 '-rtsp_transport', 'tcp',  # Force TCP (for testing)]
                 '-select_streams', 'v:0',  # Select only video stream 0.
                 '-show_entries', 'stream=width,height', # Select only width and height entries
                 '-of', 'json', # Get output in JSON format
                 in_stream]

# Read video width, height using FFprobe:
p0 = subprocess.Popen(probe_command, stdout=subprocess.PIPE)
probe_str = p0.communicate()[0] # Reading content of p0.stdout (output of FFprobe) as string
p0.wait()
probe_dct = json.loads(probe_str) # Convert string from JSON format to dictonary.

# Get width and height from the dictonary
width = probe_dct['streams'][0]['width']
height = probe_dct['streams'][0]['height']

非常感谢Rotem,你的回答非常有帮助,多亏了它,我成功地解决了我的问题,同时也理解了我所面临的问题。这只是为了上下文和其他像我一样使用Balser相机的人(我的是daA1280-54uc),使用``cv2.VideoCapture(索引)“``可能有点问题,像我在第一个``代码块``中那样手动获取每一帧可能是唯一的方法。实际上,我知道我的图像的大小,因为我在流之前调整了它们的大小,但是如果其他人不知道的话他可能应该这样做,只是为了确定图像的尺寸。我编辑了这篇文章:例如,使用FFprobe阅读
宽度
高度
。你能告诉我它在你的情况下是否有效吗?我知道你不需要它,但它可能对其他人有帮助。谢谢你的更新,不幸的是它似乎不起作用在我的特殊情况下。