芯仑的CeleX_MP相机在某一个时刻只能够输出 frame 或 event,而不能像其他DAVIS一样同时输出event和frame。但相机自带loop模式,在该模式下相机的输出将在几种模式下切换。最近写了一下Ubuntu下loop模式的读取,整理记录并做分享。转载请注明出处
Loop模式原理
下图是芯仑给的SDK文档中关于Loop模式的说明。简单总结Loop有三点:
- 首先需要 enable Loop模式,通过函数 setSensorLoopMode();
- Loop模式将在3种模式下切换,可以设定3种模式;建议设置为:完整图像模式、event图模式、光流模式;
- 三种模式都可以分别设置持续时间,涉及到完整图像的,可以设定捕捉图像的数量;涉及事件的,设置持续时间。
主动式读取(不推荐)
官方提供的参考代码中,给出了两种读取模式。一种是主动式,在while中类似于查询的方式,不断读取不同模式下的数据。核心代码如下:
p->setLoopModeEnabled(true);
p->setSensorLoopMode(CeleX5::Full_Picture_Mode, 1);
p->setSensorLoopMode(CeleX5::Event_Off_Pixel_Timestamp_Mode, 2);
p->setSensorLoopMode(CeleX5::Optical_Flow_Mode, 3);
uint8_t* pBuffer1 = new uint8_t[CELEX5_PIXELS_NUMBER];
uint8_t* pBuffer2 = new uint8_t[CELEX5_PIXELS_NUMBER];
uint8_t* pBuffer3 = new uint8_t[CELEX5_PIXELS_NUMBER];
while (true){
p->getFullPicBuffer(pBuffer1);
cv::Mat matFullPic(800, 1280, CV_8UC1, pBuffer1);
cv::imshow("full", matFullPic);
p->getEventPicBuffer(pBuffer2, CeleX5::EventBinaryPic);
cv::Mat matEventPic(800, 1280, CV_8UC1, pBuffer2);
cv::imshow("events", matEventPic);
waitKey(1);
}
然而我实际测试时发现,这种方式读取会有问题。具体表现为,frame的图像会明显抖动,event的图像会显示前些时刻的数据。例如下图,左侧为灰度图,右侧为event图。第一张显示的正常,但第二张中下一个时刻event的数据显示的是之前某个时刻的图像(灯柱还在左下方时的)。推测这种现象的原因是数据缓存问题。但并没有清除缓存的接口,所以这种方法放弃。
回调函数式读取
咨询了芯仑的技术人员,技术人员说要参考GetFrameBufferByCallback
这个文件写。同时我也参考了官方在ROS的demo下提供的回调函数式的读取代码。并且在SDK参考文档中找到了相关说明。要点如下:
- 继承一个
CeleX5DataManager
类; - 重载
onFrameDataUpdated()
函数接收各种图像帧完成的通知。
核心代码如下:
1. 继承 CeleX5DataManager类
class SensorDataObserver : public CeleX5DataManager{
public:
SensorDataObserver(CX5SensorDataServer* pServer){
m_pServer = pServer;
m_pServer->registerData(this, CeleX5DataManager::CeleX_Frame_Data);
}
~SensorDataObserver() {
m_pServer->unregisterData(this, CeleX5DataManager::CeleX_Frame_Data); }
virtual void onFrameDataUpdated(CeleX5ProcessedData* pSensorData); //overrides Observer operation
CX5SensorDataServer* m_pServer;
};
2. 重载onFrameDataUpdated函数
void SensorDataObserver::onFrameDataUpdated(CeleX5ProcessedData* pSensorData){
CeleX5::CeleX5Mode sensorMode = pSensorData->getSensorMode(); // 获取当前时刻的模式
if (CeleX5::Full_Picture_Mode == sensorMode){
// 完整图像模式,显示图像
ROS_INFO("In full picture node...");
time_t time_stamp = 0;
pCeleX5->getFullPicBuffer(pImageBuffer, time_stamp);
cv::Mat matFullPic(800, 1280, CV_8UC1, pImageBuffer);
cv::imshow("full", matFullPic);
}
else if (CeleX5::Event_Off_Pixel_Timestamp_Mode == sensorMode){
// 其他不同模式时,执行不同操作
...
}
else if (CeleX5::Optical_Flow_Mode == sensorMode){
...
}
}
3. 主函数中的配置与初始化
void main(){
pCeleX5->openSensor(CeleX5::CeleX5_MIPI);
// 启动Loop模式,并设置每个模式具体是什么
pCeleX5->setLoopModeEnabled(true);
pCeleX5->setSensorLoopMode(CeleX5::Full_Picture_Mode, 1);
pCeleX5->setSensorLoopMode(CeleX5::Event_In_Pixel_Timestamp_Mode, 2);
pCeleX5->setSensorLoopMode(CeleX5::Optical_Flow_Mode, 3);
// 每个模式的持续时长。这里的设置为:full frame采集1帧,然后event采集20ms,不采集光流
pCeleX5->setPictureNumber(1, CeleX5::Full_Picture_Mode);
pCeleX5->setEventDuration(20);
pCeleX5->setPictureNumber(0, CeleX5::Optical_Flow_Mode);
// 启动
SensorDataObserver* pSensorData = new SensorDataObserver(pCeleX5->getSensorDataServer());
while (true){
usleep(1000);
}
}
其他
完整代码下载:https://download.csdn.net/download/tfb760/15420758
赚个积分吧,感觉自己不容易。(如实在下载困难,可私信。但不保证回复及时)
微信公众号:【事件相机】,交流事件相机的相关科研与应用。欢迎大家关注