【问题标题】:Ideas for Efficient Slow Motion Video Playback in Raspberry PiRaspberry Pi 中高效慢动作视频播放的想法
【发布时间】:2021-03-27 11:01:29
【问题描述】:

我是 Python 和 Raspberry Pi 的新手,我正在尝试编写一个程序来录制 8 秒视频,然后以慢动作回放。它也不必是高质量的视频,它只需要运行程序而没有很长的延迟。

几年前,我目睹了一个正是这样做的项目。它响应触发记录了 8 秒的镜头,并且只用了几秒钟就可以在显示器上以慢动作处理和播放该视频。但是,我不知道那个人是怎么做到的。

我尝试过使用 ffmpeg,但创建慢动作视频需要 90 多秒。我尝试过 OpenCV,但无济于事。我听说 MsgBox 功能也是一个选项,但我找不到该功能的完整文档。我愿意使用任何文件格式,但目前视频输出为 H264。有没有人有好的想法来创建(或只是播放)处理速度不慢的慢动作视频?

这是我用来以 25 fps 录制并以慢动作回放的当前代码,但它需要 1-2 分钟的处理时间(我希望只有几秒钟):

import os
from picamera import PiCamera
from time import sleep
import vlc, subprocess

filename = "/home/pi/vid1.h264"
camera = PiCamera()

camera.start_preview()
camera.start_recording(filename)
sleep(8)
camera.stop_recording()
camera.stop_preview()

# Converts to slow motion video
input = filename
output = "/home/pi/slowvid1.h264"
command = f"ffmpeg -i {input} -vf setpts=2*PTS {output}"
os.system(command)

subprocess.Popen(['vlc',output, '--fullscreen','--play-and-exit'])

这是我让 OpenCV 工作的问题的链接 Slow Motion Video Playback with OpenCV is not working (python)

【问题讨论】:

  • 我有点不清楚你想要什么。您不能以 30 或 60 fps 录制 8s 视频并以 10 fps 播放以使其看起来很慢吗?也许您有足够的 RAM 将帧存储在列表中,然后使用 OpenCV imshow()waitKey(100) 从列表中播放帧?请澄清您的问题。
  • 我想避免将帧存储为图片文件,但如果必须的话,我可以。我宁愿照你说的做,以 60fps 的速度拍摄 8 秒的视频,然后放慢播放速度。但我不知道该怎么做。我环顾四周,找不到 MsgBox 函数的完整文档,并且我使用过 OpenCV,但它也不起作用。这是我之前关于 OpenCV 的问题的链接 stackoverflow.com/questions/65299634/…
  • 即使有你的更新也不清楚。你有代码来捕捉 8s 的 60fps 吗?如果有,请出示。大概是 480 帧,所以你可以将这 480 帧存储在一个列表中,不是吗?然后遍历列表,显示每个图像超过 1/60 秒。
  • 压缩很慢。如果你不这样做,你就没有问题;不利的一面是,如果您不压缩视频,则需要有一个可以缓冲未压缩帧的地方,这些帧并不小。

标签: python raspberry-pi slowmotion


【解决方案1】:

这是一种方法。我使用 PiCamera 以 60 fps 的速度捕捉 MJPEG 帧 8 秒。请阅读this document 中的第 4.7 节。

我选择视频端口是因为它是最快的捕获方法,因为它不会通过对多个帧进行平均来进行复杂的去噪,这会减慢速度。

我选择了 MJPEG “Motion JPEG” 作为输出格式,因为这意味着帧在我收到它们之前在 GPU 上进行了 JPEG 压缩,这意味着我需要更少的 CPU 资源和更少的 RAM,因为帧被压缩。

当每一帧到达时,除了简单地将其附加到 RAM(内存)中的 JPEG 压缩帧列表之外,我没有做任何其他处理。

录制完成后,我只需遍历内存中的帧列表,将它们从 JPEG 转换回 OpenCV 用于图像的 Numpy 数组,然后显示它们。由于它们是以 60fps 的速度采集的,这意味着每 16 毫秒记录一个新帧。因此,如果我以 64 毫秒的延迟播放,它应该会慢 4 倍。

#!/usr/bin/env python3

import numpy as np
import cv2
import time
import picamera

class SplitFrames(object):
    def __init__(self):
        self.frame_num = 0
        # List of all the JPEG-encoded frames we receive
        self.frames = []
        # Total memory used
        self.memory = 0

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # Start of new frame
            l = len(buf)
            print(f'DEBUG: New frame of {l} bytes')
            self.frames.append(buf)
            self.frame_num += 1
            self.memory    += l
        else:
            print(f'ERROR: partial frame of {len(buf)} bytes received belonging to previous frame')

################################################################################
# Main program - alter these variables according to needs
################################################################################
targetFPS, duration = 60, 8
width, height = 1024, 768
print(f'Recording frames of {width}x{height} at {targetFPS} fps for {duration} seconds')

################################################################################
# Recording loop
################################################################################
with picamera.PiCamera(framerate=targetFPS) as camera:
    camera.resolution=(width,height)
    camera.start_preview()
    # Give the camera some warm-up time
    time.sleep(2)
    output = SplitFrames()
    start = time.time()
    camera.start_recording(output, format='mjpeg')
    camera.wait_recording(duration)
    camera.stop_recording()
    finish = time.time()

# Calculate a few statistics
fps = output.frame_num / (finish - start)
avg = int(output.memory/output.frame_num)
MB  = output.memory/(1024*1024)
print(f'Captured {output.frame_num} frames at {fps:.3f} fps, average bytes/frame={avg}, total RAM={MB:.3f} MB')

################################################################################
# Playback loop - grab frames from list and display with delay
################################################################################
for frame_num, frame in enumerate(output.frames):
    print(f'DEBUG: Playing back frame {frame_num}')
    im = cv2.imdecode(np.frombuffer(frame, dtype=np.uint8), cv2.IMREAD_COLOR)
    cv2.imshow('Slow Motion Replay', im)
    cv2.waitKey(64)

运行时,输出如下所示:

Recording frames of 1024x768 at 60 fps for 8 seconds
DEBUG: New frame of 26661 bytes
DEBUG: New frame of 33335 bytes
DEBUG: New frame of 33558 bytes
DEBUG: New frame of 34146 bytes
...
...
DEBUG: New frame of 34208 bytes
DEBUG: New frame of 34408 bytes
DEBUG: New frame of 34356 bytes
DEBUG: New frame of 34248 bytes
Captured 480 frames at 59.763 fps, average bytes/frame=36938, total RAM=16.909 MB

如您所见,它仅使用 17MB 的 RAM 来实现 8s 的 60fps @ 1024x768 像素,不到 1GB RasPi 3 的 RAM 的 2%,不到 4GB RasPi 4 的 0.5%,因此您可能会运行更长时间而不需要内存不足。

然后它会立即以四分之一的速度播放。这是录制 iPhone 高速计时器后播放的迷你动画 GIF:

注意 1:您不必使用 OpenCV 进行播放,您可以使用任何其他可以显示 JPEG 的软件包。

注意 2:您可以在重放循环中显示图像之前调整图像大小、对其进行注释或以某种方式对其进行处理。

注意 3:请注意,您可以将回放循环中的 cv2.imshow() 替换为调用 OpenCV VideoWriter() 并将视频保存到磁盘,而不是如果您愿意,可以在屏幕上显示它。

注意4:您可以在camera.start_recording() 中添加quality 参数来控制图像质量/大小。或者您可以更改为原始 RGB 或 YUV 格式。

关键词:RasPi、Raspberry Pi、RaspberryPi、图像处理、视频录制、PiCamera、high-speed、high speed、60 fps、slow-motion、slow-motion、slo-mo、buffer、内存缓冲,重放。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 1970-01-01
    • 2019-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多