03 录制与回放
任务目标
把实时事件流存成文件,以后再离线回放。这是数据集采集、算法离线调试、问题复现的基础。Toolkit 用 EVT2/EVT3 格式作为标准事件文件格式(见 数据格式)。
涉及类
Shimeta::io::EventWriter:把事件写入 RAW 文件。open(file, w, h, fmt, start_ts)→writeRaw(...)/writeEvents(...)→flush()/close()。Shimeta::io::EventReader:回放读取(readAllEvents),见 指引 02。Shimeta::io::HybridWriter:EVS + APS 混合录制(EVS 存 RAW、APS 存 AVI)。
平台注意:USB 的 Frame.evs 通常为 EVT2;S100/X5 的 MipiHvs 为 apx003 RAW8。S100 的 APS 为 NV12,X5 的 APS 为 Gray8,混合录制时应按 Frame.format 处理图像。
录制:相机 → 文件
USB 后端 Frame.evs 已是 EVT2 字节,用 writeRaw 直写(不重新编码,最省):
#include <shimetapi/hv/camera.h>
#include <shimetapi/hv/device_config.h>
#include <shimetapi/io/event_writer.h>
#include <chrono>
#include <iostream>
#include <thread>
int main() {
Shimeta::hv::Camera cam;
Shimeta::hv::DeviceConfig cfg;
cfg.backend = Shimeta::hv::Backend::Usb;
cfg.vendor_id = 0x1d6b;
cfg.product_id = 0x0105;
cfg.event_fmt = Shimeta::hv::EventFormat::Evt2;
cam.Init(cfg);
if (!cam.StartStream()) return 1;
Shimeta::io::EventWriter writer;
writer.open("events.raw", 768, 608, Shimeta::io::RawFormat::Evt2, 0);
auto end = std::chrono::steady_clock::now() + std::chrono::seconds(5);
Shimeta::Frame f;
while (std::chrono::steady_clock::now() < end && cam.GetFrame(f, 1000)) {
writer.writeRaw(f.evs.data, f.evs.size); // Frame.evs 直写(不编码)
}
writer.flush();
writer.close();
cam.StopStream();
cam.Destroy();
std::cout << "录制完成" << std::endl;
return 0;
}writeRaw vs writeEvents
writeRaw(data, len):原始字节透传(Frame.evs直写),不再编码 —— 最快;要求写入的字节本身已是目标格式。writeEvents(events):用Evt2Encoder把vector<EventCD>编码后写入 —— 用于手里是解码后事件、想录成 EVT2 的场景;writtenEventCount()反映这条路径累计写入的事件数。
采集结束务必 flush(),close() 也会自动刷新。
回放:文件 → 处理
用 EventReader 读出 RAW 文件再处理(详见 指引 02):
Shimeta::io::EventReader reader;
reader.open("events.raw");
std::vector<Shimeta::EventCD> events;
reader.readAllEvents(events); // 与实时采集拿到相同的 EventCDv2.0
EventReader目前只提供readAllEvents(全量读入),暂无流式分批读取;超大文件需注意内存。
可视化回放(暂停、变速、帧生成)可用 Toolkit 自带的 viewer 示例(仓库 samples/cpp/viewer),见 Toolkit — 样例。
EVS + APS 混合录制:HybridWriter
要同时把 EVS 存为 RAW、APS 存为 AVI,用 HybridWriter:
#include <shimetapi/io/hybrid_writer.h>
Shimeta::io::HybridWriter hw;
hw.open("events.raw", "aps.avi", 768, 608, Shimeta::io::RawFormat::Evt2, 30.0);
Shimeta::Frame f;
while (cam.GetFrame(f, 1000)) {
hw.writeFrame(f); // EVS 走 writeRaw,APS 按 Frame.format 写入 AVI
}
hw.close();混合回放:HybridReader
HybridWriter 录制的 .raw + .avi 用 HybridReader 读回(与 Camera 一样返回原始字节,应用自行解码):
#include <shimetapi/io/hybrid_reader.h>
#include <shimetapi/codec/evt2_codec.h>
Shimeta::io::HybridReader hr;
hr.open("events.raw", "aps.avi"); // 任一路径留空则跳过该侧
Shimeta::codec::Evt2Decoder dec;
Shimeta::Frame f;
while (hr.readApsFrame(f)) {
// f.aps = APS 原始字节:S100/USB 通常为 NV12,X5 为 Gray8
// f.ts.aps_ts_ns = 该帧处理后的纳秒时间戳
}
// EVS 侧逐包读(跳过 EVT3 文本头):
while (hr.readEvsPacket(f)) {
std::vector<Shimeta::EventCD> events;
dec.Decode(f.evs.data, f.evs.size, events);
}
hr.close();MIPI 注意
Backend::MipiHvs 的 Frame.evs 是 apx003 RAW8 子帧流(不是 EVT2)。直接 writeRaw 得到的是 RAW8 文件,不能被 EventReader 当 EVT2 回放。两条路:
- 要 EVT2 文件:先
MipiRaw8Decoder解码成EventCD,再writer.writeEvents(events)(编码为 EVT2); - 保留原始 RAW8:
writeRaw存盘,离线再用工具转换。
S100 与 X5 的 RAW8 事件录制和回放路径相同;只需替换构建目标和部署目录(out/s100/build 或 out/x5/build),不要把 RAW8 文件误标为 EVT2。
进阶
- 完整 API:Toolkit C++ API
- 回放的数据怎么处理:指引 04 去噪、指引 05 可视化
- EVT2 / RAW8 / CSV 格式差异:数据格式
