在Raspberry Pi上保存图像流的最快方法 - python

我们有一个通过USB3连接到Raspberry Pi 4的相机。相机只能提供RAW图像[2056x1542x3],我们可以以大约30 FPS的速度读取它们。在Raspberry Pi 4上,我们需要将这些图像保存到磁盘-由于空间和对SD卡的写入速度的限制,实际上以任何速率保存RAW图像(每张10 MB)都是不可行的。相反,我们要压缩这些图像,然后尽快保存它们。

我们当前的解决方案与以下代码段相似:


def save_image(frame,filename):
    cv2.imwrite(filename,frame)

...

(ret, frame) = cam.get_next_frame()
if ret == IS_SUCCESS:
    timestamp = get_timestamp()
    filename = conf["cv_image_folder"] + timestamp + ".jpg"
    save_thread = threading.Thread(target=save_image, args=(frame,filename,))
    save_thread.start()

OpenCV使用libjpeg-turbo和所有可能的硬件标志进行编译,以加快计算速度。 Raspberry Pi 4以大约5或6 FPS的速度使用全部4个内核的大约100%。非线程配置也是如此。我们在摄像机上手动设置帧速率,并监视产生的线程数(在5-6 FPS时大约3-4个并发线程)。我们选择JPEG(即使有损),因为PNG或TIFF压缩需要更长的时间来计算。

有什么办法可以改善这一点?

参考方案

我认为Dan的发现可以得到Bayer图像的发现将证明您的问题的解决方案,但是我在使用Broadcom的MMAL“ Mulitmedia抽象层”在VideoCore GPU上进行JPEG编码以减少CPU的概念上做了一些工作加载并将其放置在此处。结合Dan的建议和/或其他尝试做类似事情的人,它可能对您有用。

在GPU上运行的结果是,它可以以大约每秒4帧的速度将(相当大的)图像编码到磁盘上,而几乎不使用CPU。我观察到3个CPU内核处于空闲状态,而第四个内核则在10-15%的使用率上下浮动。

该代码仅获取一个称为"image.jpg"的图像,并将其调整为您正在使用的尺寸,然后重复对其进行编码并将其发送到磁盘100次。

#!/usr/bin/env python3

import io
import numpy as np
from picamera import mmal, mmalobj as mo
from PIL import Image
from threading import Event

# Globals
w, h = 2048, 1536      # These seem to need to be multiples of 64, or maybe 16
EncoderAvail = Event()
EncoderAvail.set()

# Set up MMAL-based JPEG encoding
encoder = mo.MMALImageEncoder()
encoder.inputs
encoder.inputs[0].format = mmal.MMAL_ENCODING_RGB24
encoder.inputs[0].framesize = (w, h)
encoder.inputs[0].commit()
encoder.outputs[0].copy_from(encoder.inputs[0])
encoder.outputs[0].format = mmal.MMAL_ENCODING_JPEG
encoder.outputs[0].commit()
encoder.outputs[0].params[mmal.MMAL_PARAMETER_JPEG_Q_FACTOR] = 90

def image_callback(port, buf):
    with open(f"frame-{frame}.jpg", 'wb') as jpeg:
        jpeg.write(buf.data)
        EncoderAvail.set()
    #return bool(buf.flags & mmal.MMAL_BUFFER_HEADER_FLAG_FRAME_END)
    return True

encoder.outputs[0].enable(image_callback)
encoder.inputs[0].enable(lambda port, buf: True)

# Encode the same image repeatedly as we are measuring the encode time not acquire time
im = Image.open('image.jpg').resize((w,h))
rgb_data = np.array(im)

for frame in range(100):
    print(f"Frame: {frame}")

    # Make red bar across top that gets longer with each frame to be sure we are getting them all
    rgb_data[0:10,:frame,0] = 255

    EncoderAvail.wait()
    buf = encoder.inputs[0].get_buffer()
    buf.data = rgb_data[:]
    EncoderAvail.clear()
    encoder.inputs[0].send_buffer(buf)

encoder.outputs[0].disable()
encoder.inputs[0].disable()

该代码主要基于Picamera MMAL素材-here。该代码可能不是最佳的-例如,我不知道如何将缓冲区的数量增加到2,这将启用双缓冲并可能产生很大的不同。我也不确定如何使其更加受事件驱动,因为目前它是相当同步的。

就像我说的那样,您可以通过更改mmal.MMAL_ENCODING_RGB24来更改它以将Bayer图像编码为JPEG,以便您仍然可以使用它。

如果有人对MMAL东西有更多的了解并能够对其进行改进,请随时获取我的代码,对其进行改进并将其添加为新的答案,并让我知道以便我们都可以学习。

我注意到,它目前似乎也不符合JPEG质量设置,因此如果有人可以改善它,请告诉我。

关键字:Raspberry Pi,GPU,JPEG编码,JPEG编码器,MMAL,Videocore GPU,Broadcom,多媒体抽象层,Raspi。

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

Python-Excel导出 - python

我有以下代码:import pandas as pd import requests from bs4 import BeautifulSoup res = requests.get("https://www.bankier.pl/gielda/notowania/akcje") soup = BeautifulSoup(res.cont…

Python:传递记录器是个好主意吗? - python

我的Web服务器的API日志如下:started started succeeded failed 那是同时收到的两个请求。很难说哪一个成功或失败。为了彼此分离请求,我为每个请求创建了一个随机数,并将其用作记录器的名称logger = logging.getLogger(random_number) 日志变成[111] started [222] start…

Python pytz时区函数返回的时区为9分钟 - python

由于某些原因,我无法从以下代码中找出原因:>>> from pytz import timezone >>> timezone('America/Chicago') 我得到:<DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD…

用大写字母拆分字符串,但忽略AAA Python Regex - python

我的正则表达式:vendor = "MyNameIsJoe. I'mWorkerInAAAinc." ven = re.split(r'(?<=[a-z])[A-Z]|[A-Z](?=[a-z])', vendor) 以大写字母分割字符串,例如:'我的名字是乔。 I'mWorkerInAAAinc”变成…