【问题标题】:Python OpenCV cap.read() unpacks compressed frames?Python OpenCV cap.read() 解压缩压缩帧?
【发布时间】:2020-02-20 22:13:41
【问题描述】:

你好 Stack 社区,

我正在从 IP 摄像机流中读取帧并将它们存储在一个列表中,以便稍后创建一个视频文件。 我正在使用 python OpenCV 库,它运行良好,但是.. 从 IP 摄像机发送的帧应该具有 h264 压缩,但是当我检查帧的大小时,对于 4K 流,它们是 25 MB。我很快就耗尽了内存。 这不是代码,但类似:

import cv2

cap = cv2.VideoCapture(0)
list = []

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0)
        list.append(frame)

cap.release()

out = cv2.VideoWriter('output.avi', -1, 20.0, (640,480))
for frm in list:
    out.write(frm)
out.release()

cv2.destroyAllWindows()

似乎ret, frame = cap.read() 解压了框架? 这会在每个循环中产生额外的处理,并且对于我使用脚本的意图来说是不必要的,有没有办法在不解包的情况下检索帧?

为我可能的无知提前道歉。

【问题讨论】:

  • 也许你应该使用其他工具来获取流并将其直接保存到文件中 - 不使用 Python - 即。 ffmpeg, vlc - Capture RTSP stream from IP Camera and store
  • Python 对我来说是必不可少的;/
  • 使用其他工具获取所有图像,然后使用 Python 处理此本地视频。如果您学习如何使用ffmpegvlc,那么您可以使用Python 使用ffmpegvlc - 即。 ffmpeg-python, python-vlc.

标签: python opencv compression mp4


【解决方案1】:

我构建了一个测试样本,用于使用ffmpeg-python 将 h264 流读入内存。

示例从文件中读取数据(我没有用于测试它的相机)。
我还测试了从 RTSP 流中读取的代码。

这是代码(请阅读 cmets):

import ffmpeg
import threading
import io

in_filename = 'test_vid.264' # Input file for testing (".264" or ".h264" is a convention for elementary h264 video stream file)

## Build synthetic video, for testing:
################################################
# ffmpeg -y -r 10 -f lavfi -i testsrc=size=192x108:rate=1 -c:v libx264 -crf 23 -t 50 test_vid.264

width, height = 192, 108

(
    ffmpeg
    .input('testsrc=size={}x{}:rate=1'.format(width, height), f='lavfi')
    .output(in_filename, vcodec='libx264', crf=23, t=50)
    .overwrite_output()
    .run()
)
################################################


# Use ffprobe to get video frames resolution
###############################################
# p = ffmpeg.probe(in_filename, select_streams='v');
# width = p['streams'][0]['width']
# height = p['streams'][0]['height']
###############################################


# Stream the video as array of bytes (simulate the stream from the camera for testing)
###############################################
## https://github.com/kkroening/ffmpeg-python/blob/master/examples/README.md
#sreaming_process = (
#    ffmpeg
#    .input(in_filename)
#    .video # Video only (no audio).
#    .output('pipe:', format='h264')
#    .run_async(pipe_stdout=True) # Run asynchronous, and stream to stdout
#)
###############################################


# Read from stdout in chunks of 16K bytes
def reader():
    chunk_len_in_byte = 16384  # I don't know what is the optimal chunk size
    in_bytes = chunk_len_in_byte

    # Read until number of bytes read are less than chunk_len_in_byte
    # Also stop after 10000 chucks (just for testing)
    chunks_counter = 0
    while (chunks_counter < 10000):
        in_bytes = process.stdout.read(chunk_len_in_byte) # Read 16KBytes from PIPE.
        stream.write(in_bytes) # Write data to In-memory bytes streams
        chunks_counter += 1
        if len(in_bytes) < chunk_len_in_byte:
            break


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

# Execute ffmpeg as asynchronous sub-process.
# The input is in_filename, and the output is a PIPE.
# Note: you should replace the input from file to camera (I might forgot an argument that tells ffmpeg to expect h264 input stream).
process = (
    ffmpeg
    .input(in_filename) #.input(in_stream)
    .video
    .output('pipe:', format='h264')
    .run_async(pipe_stdin=True, pipe_stdout=True)
)

# Open In-memory bytes streams
stream = io.BytesIO()

thread = threading.Thread(target=reader)
thread.start()

# Join thread, and wait for processes to end.
thread.join()

try:
    process.wait(timeout=5)
except sp.TimeoutExpired:
    process.kill()  # Kill subprocess in case of a timeout (there might be a timeout because input stream still lives).

#sreaming_process.wait()  # sreaming_process is used 

stream.seek(0) #Seek to beginning of stream.

# Write result to "in_vid.264" file for testing (the file is playable).
with open("in_vid.264", "wb") as f:
    f.write(stream.getvalue())

如果您觉得它有用,我可能会在代码前添加更多背景说明。

请让我知道代码是否适用于相机,以及您必须修改的内容。

【讨论】:

  • 非常感谢您的意见,我会尝试看看如何在我的脚本中实现这一点。为了了解更多,它与我目前使用的 openCV 方法有何不同?我看到 ffmpeg 似乎是人们推荐的。
  • OpenCV 将每一帧解码为(通常)BGR 格式。对于 4K 流,每个解码帧为 3840*2160*3 字节(为了存储它,您需要对其进行编码 [压缩])。使用 FFmpeg,您可以选择抓取大约 100 到 1000 的编码(压缩)h264 流平均要小几倍。您可以存储编码的视频流(在磁盘或 RAM 中),然后再解码(“逐帧”解码,仅当您需要解码的帧时 - 例如显示视频时)。跨度>
  • 非常感谢,这正是我所需要的。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多