You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何存储视频帧字节串并实现指定片段的高效加载?

解决方案:支持片段加载的编码帧存储方案

针对你遇到的问题——需要存储编码后的视频帧字节串,同时支持按需加载片段而非全量加载,以下是几种实用的方案:

1. HDF5 格式存储(推荐)

HDF5 原生支持可变长度字节数组的存储,且支持高效的切片读取,无需加载整个数据集。借助 h5py 库可以轻松实现:

写入示例

import h5py
import pyturbojpeg

# 假设 frames_bytes 是已编码的帧字节串列表
frames_bytes = [pyturbojpeg.encode(frame) for frame in video_frames]

with h5py.File("video_frames.h5", "w") as f:
    # 创建可变长度字节类型的 dataset
    dt = h5py.special_dtype(vlen=bytes)
    f.create_dataset("frames", data=frames_bytes, dtype=dt)

读取示例(加载指定片段)

import h5py
import pyturbojpeg

with h5py.File("video_frames.h5", "r") as f:
    # 加载第10到20帧(左闭右开)
    selected_bytes = f["frames"][10:21]
    # 解码为 numpy 数组
    selected_frames = [pyturbojpeg.decode(b) for b in selected_bytes]

优缺点:

  • 优点:成熟稳定,支持随机访问/切片,无需手动维护索引,适合大规模数据。
  • 缺点:单文件过大时可能有轻微性能损耗,需注意分文件管理。

2. 自定义分块存储 + 索引文件

自己实现数据文件与索引文件的分离,手动记录每个帧的位置信息,实现精准的片段加载:

写入逻辑

  1. 数据文件:按顺序写入每个帧的字节串,每个帧前写入固定长度的长度标识(比如4字节int)。
  2. 索引文件:记录每个帧在数据文件中的起始偏移量和字节长度,可存储为二进制文件或json/csv。
import struct

# 写入数据文件和索引
with open("video_data.bin", "wb") as data_f, open("video_index.bin", "wb") as idx_f:
    offset = 0
    for b in frames_bytes:
        # 写入帧长度(4字节大端)
        data_f.write(struct.pack(">I", len(b)))
        # 写入帧字节串
        data_f.write(b)
        # 写入索引:偏移量 + 长度
        idx_f.write(struct.pack(">II", offset, len(b)))
        offset += 4 + len(b)

读取逻辑(加载指定片段)

import struct
import pyturbojpeg

def load_frames(start_idx, end_idx):
    frames = []
    # 先读取索引
    with open("video_index.bin", "rb") as idx_f:
        idx_f.seek(start_idx * 8)  # 每个索引占8字节(两个int)
        for _ in range(end_idx - start_idx + 1):
            offset, length = struct.unpack(">II", idx_f.read(8))
            # 读取对应帧
            with open("video_data.bin", "rb") as data_f:
                data_f.seek(offset + 4)  # 跳过前面的长度标识
                frame_bytes = data_f.read(length)
                frames.append(pyturbojpeg.decode(frame_bytes))
    return frames

# 加载第5到15帧
selected_frames = load_frames(5, 15)

优缺点:

  • 优点:完全自定义,无额外依赖,性能可控。
  • 缺点:需要手动维护索引,代码量较大,扩展性弱。

3. LMDB 键值对存储

LMDB 是高性能的嵌入式键值存储,适合大规模数据的随机访问。可以用视频ID+帧序号作为键,帧字节串作为值:

写入示例

import lmdb

env = lmdb.open("video_lmdb", map_size=1024*1024*1024*10)  # 设置足够大的映射空间
with env.begin(write=True) as txn:
    for idx, b in enumerate(frames_bytes):
        key = f"video_001_frame_{idx}".encode()
        txn.put(key, b)
env.close()

读取示例(加载指定片段)

import lmdb
import pyturbojpeg

env = lmdb.open("video_lmdb", readonly=True)
selected_frames = []
with env.begin() as txn:
    for idx in range(10, 21):
        key = f"video_001_frame_{idx}".encode()
        frame_bytes = txn.get(key)
        selected_frames.append(pyturbojpeg.decode(frame_bytes))
env.close()

优缺点:

  • 优点:读写效率极高,支持并发访问,适合多进程/多线程训练场景。
  • 缺点:键值对结构对连续片段的批量读取不如HDF5直接,需遍历键。

4. SQLite 数据库存储

将帧信息存储在SQLite表中,通过SQL查询筛选需要的帧,适合需要复杂条件筛选的场景:

写入示例

import sqlite3

conn = sqlite3.connect("video_frames.db")
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS frames (video_id TEXT, frame_idx INTEGER, data BLOB)")
for idx, b in enumerate(frames_bytes):
    cursor.execute("INSERT INTO frames VALUES (?, ?, ?)", ("video_001", idx, b))
conn.commit()
conn.close()

读取示例(加载指定片段)

import sqlite3
import pyturbojpeg

conn = sqlite3.connect("video_frames.db")
cursor = conn.cursor()
cursor.execute("SELECT data FROM frames WHERE video_id=? AND frame_idx BETWEEN ? AND ?", ("video_001", 10, 20))
selected_frames = [pyturbojpeg.decode(row[0]) for row in cursor.fetchall()]
conn.close()

优缺点:

  • 优点:支持复杂SQL查询,结构化存储,易于维护。
  • 缺点:读写性能略低于前几种方案,适合中小规模数据或需要灵活查询的场景。

内容的提问来源于stack exchange,提问作者Rancho Xia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 09:31:34