Table of Contents
ToF相机一帧图像处理过程
Vzense ToF(Time of Flight)相机的一帧图像从产生到传输完成的过程可以概括为如下工作环节:
- 曝光(Exposure)
- 图像处理(ISP processing)
- 网络传输(Transmition)
- SDK处理(SDK processing)
- 应用获取(Program readout)
其中曝光和图像处理是在相机内完成的,SDK处理和应用获取是在主机内完成的。具体过程如下图所示:
时间戳和帧号
时间戳
当Vzense ToF相机上电时,系统会启动一个从0开始的本地计时器,时间自动增加,不受相机工作状态的影响,计时器的时间即为相机的本地系统时间。
注意:相机系统时间默认并不是和主机时间对齐的实时时钟real time clock,如需对齐则需要相机和主机之间配置好NTP/PTP模式。请参考《 应用笔记05-Vzense 3D ToF相机的NTP功能设置方式介绍 》和《 应用笔记06-Vzense 3D ToF相机的PTP功能设置方法介绍 》。
相机在每帧曝光结束时,会给每一帧图像打一个时间戳t(Time Stamp),单位为1ms。该时间戳会和该帧图像的数据一起发送到主机端,并可通过API获得。
时间戳可以用作相机和主机系统之间的对齐和校准,特别是在触发模式下,可以在应用端知道某一帧的发生时间,避免在计算和传输过程中的不确定延时,从而和系统时间对齐。
帧号
当图像被传送到主机时,主机SDK会给每一帧图像标记一个唯一的帧号(Frame Index),帧号从0开始,按照顺序逐次递加。帧号是由SDK赋值,表示的是SDK接收到图像数据包的索引号。因此与曝光、ISP 处理和传输阶段无关,这意味着,即使在曝光、ISP 处理和传输阶段丢失了帧,帧号仍然是按顺序进行的。
SDK在获取一帧图像后,会把该帧图像暂存在缓存区等待应用读取。如果应用读取不及时,SDK会使用新帧覆盖旧帧,从而产生丢帧。 通过帧号,用户可以判断是否由于主机进程调度、CPU负载过高等原因导致数据丢帧。
如上图所示,SDK连续接收到了帧号为1,2,4的帧,因此我们就可以知道中间有帧号为3的一帧没有被正确读取到。从帧号的产生原理可以知道,如果发生丢帧,一定是主机端的应用读取发生了问题。
时间戳和帧号的获取
Vzense相机图像的时间戳和帧号都是在ScFrame结构体中
typedef struct
{
uint32_t frameIndex; //表示图像帧索引号
ScFrameType frameType; //表示图像数据类型
ScPixelFormat pixelFormat; //表示像素类型
uint8_t* pFrameData; //表示指向图像数据缓存的指针
uint32_t dataLen; //表示图像数据的长度,单位为字节
uint8_t depthRange; //表示当前帧的深度范围,仅对深度图像有效
uint16_t width; //表示图像宽度
uint16_t height; //表示图像高度
uint64_t deviceTimestamp; //表示帧在设备上生成时的时间戳,不包括帧处理和传输时间
} ScFrame;
可以通过scGetFrame接口获取。示例代码如下:
//深度图举例
if (1 == FrameReady.depth)
{
status = scGetFrame(deviceHandle, SC_DEPTH_FRAME, &depthFrame);
if (depthFrame.pFrameData != NULL)
{
cout << "scGetFrame status:" << status << " "
<< "frameType:" << depthFrame.frameType << " "
<< "deviceTimestamp:" << depthFrame.deviceTimestamp << " "
<< "frameIndex:" << depthFrame.frameIndex << endl;
}
}
图像延时
图像延时的构成
根据一帧图像的处理过程可知,相机的延时由以下部分组成:
功能所在部分 | 功能描述 | 影响因素 | |
曝光 | 相机端 | 通过曝光完成一帧图像的采集 | 1、曝光时间设置 2、HDR功能(开启HDR,耗时增加) 3、WDR功能(开启WDR,耗时增加) |
图像处理 | 相机端 | 深度图计算、IR图计算、RGB图像转换、 RGB图像编码、图像滤波、畸变还原等工作 | 1、RGB图像分辨率(分辨率越大,耗时越长) 2、深度图像分辨率(分辨率越大,耗时越长) 3、滤波开关(开启滤波,耗时增加) 4、HDR功能(开启HDR,耗时增加) 5、WDR功能(开启WDR,耗时增加) |
网络传输 | 相机和主机之间的网络设备 | 从相机传输数据到主机 | 1、网络设备性能(如网卡、网线等) 2、主机网络负载 |
SDK处理 | 主机端 | 点云转换、图像对齐、RGB图像解码等 | 1、主机性能 2、用户应用程序多线程设计 |
应用读取 | 读取 主机端 | 从SDK缓冲区读取图像 | 1、主机性能 2、用户应用程序多线程设计 |
延迟测试方法
以下内容将详细展开并举例说明Vzense相机如何进行延时数据测试。
在下述测试中,会进行两个指标的测试:单帧总延时,除曝光之外的延时(曝光结束到应用获取)。
测试步骤:
1、开启相机的NTP或PTP对时功能。
详细设置方法请参考:NTP设置方法, PTP设置方法。
如果只需要测试单帧总延时,可不进行本步骤。
注意:部分型号相机暂不支持NTP、PTP时钟同步。
2、修改sample代码,增加时间戳打印
例程DeviceSWTriggerMode非常接近我们的测试方法,只需要进行轻微修改即可。由于Windows与Linux的时间获取函数不同,在此我们分别进行介绍。
例程参考:
BaseSDK/Windows/Samples/Base/NYX650/DeviceSWTriggerMode。
(1) 、Windows
使用ftime函数获取换算毫秒级UNIX时间戳。示例代码如下:
#include <sys/timeb.h>
/*
省略部分代码
*/
//图像流开始抓取
status = scStartStream(deviceHandle);
if (status != ScStatus::SC_OK)
{
cout << "scStartStream failed status:" << status << endl;
return -1;
}
cout << "Software trigger test begins" << endl;
//启动流前的准备延迟
this_thread::sleep_for(chrono::milliseconds(3000));
//1.软触发
//2.读取下一帧图像
//3.根据准备就绪标志和图像类型获取图像信息
//4.休眠时间 1000/帧率(毫秒)
for (int i = 0; i < frameSpace; i++)
{
timeb time_start, time_end;
/*调用API函数scSoftwareTriggerOnce来触发一帧数据的发送,然后该帧数据就会被发送出去。
如果不调用将会返回超时*/
status = scSoftwareTriggerOnce(deviceHandle);
ftime(&time_start); //记录时间戳开始
if (status != ScStatus::SC_OK)
{
cout << "scSoftwareTriggerOnce failed status:" <<status<< endl;
continue;
}
//如果超过1200ms没有图像传入,该函数将会返回超时
status = scGetFrameReady(deviceHandle, 1200, &FrameReady);
if (status != ScStatus::SC_OK)
{
cout << "scGetFrameReady failed status:" << status << endl;
//this_thread::sleep_for(chrono::seconds(1));
continue;
}
//深度图举例
if (1 == FrameReady.depth)
{
status = scGetFrame(deviceHandle, SC_DEPTH_FRAME, &depthFrame);
if (depthFrame.pFrameData != NULL)
{
cout << "get Frame successful,status:" << status << " "
<< "frameTpye:" << depthFrame.frameType << " "
<< "frameIndex:" << depthFrame.frameIndex << endl
ftime(&time_end);//记录时间戳结束
uint64_t unix_end = time_end.time * 1000 + time_end.millitm;
uint64_t unix_start = time_start.time * 1000 + time_start.millitm;
uint64_t unix_exposure_end = depthFrame.deviceTimestamp;
cout << "one frame total time delay from trigger: " << unix_end - unix_start << endl;
if ((unix_end - unix_exposure_end) > 2000)
{
cout << "NTP/PTP is not work !!!" << endl;
}
else
{
cout << "time delay from exposure end: " << unix_end - unix_exposure_end << endl;
}
}
}
this_thread::sleep_for(chrono::milliseconds(1000 / frameRate));
}
(2)、Linux
使用clock_gettime函数获取换算毫秒级UNIX时间戳。示例代码如下:
#include <time.h>
/*
省略部分代码
*/
//图像流开始抓取
status = scStartStream(deviceHandle);
if (status != ScStatus::SC_OK)
{
cout << "scStartStream failed status:" << status << endl;
return -1;
}
cout << "Software trigger test begins" << endl;
//启动流前的准备延迟
this_thread::sleep_for(chrono::milliseconds(3000));
//1.软触发
//2.读取下一帧图像
//3.根据准备就绪标志和图像类型获取图像信息
//4.休眠时间 1000/帧率(毫秒)
for (int i = 0; i < frameSpace; i++)
{
timespec time_start, time_end;
/*调用API函数scSoftwareTriggerOnce来触发一帧数据的发送,然后该帧数据就会被发送出去。
如果不调用将会返回超时*/
status = scSoftwareTriggerOnce(deviceHandle);
clock_gettime(CLOCK_REALTIME, &time_start); //记录时间戳开始
if (status != ScStatus::SC_OK)
{
cout << "scSoftwareTriggerOnce failed status:" <<status<< endl;
continue;
}
//如果超过1200ms没有图像传入,该函数将会返回超时
status = scGetFrameReady(deviceHandle, 1200, &FrameReady);
if (status != ScStatus::SC_OK)
{
cout << "scGetFrameReady failed status:" << status << endl;
//this_thread::sleep_for(chrono::seconds(1));
continue;
}
//深度图举例
if (1 == FrameReady.depth)
{
status = scGetFrame(deviceHandle, SC_DEPTH_FRAME, &depthFrame);
if (depthFrame.pFrameData != NULL)
{
cout << "get Frame successful,status:" << status << " "
<< "frameTpye:" << depthFrame.frameType << " "
<< "frameIndex:" << depthFrame.frameIndex << endl
clock_gettime(CLOCK_REALTIME, &time_end);//记录时间戳结束
uint64_t unix_end = time_end.tv_sec * 1000 + time_end.tv_nsec/1000000;
uint64_t unix_start = time_start.tv_sec * 1000 + time_start.tv_nsec/1000000;
uint64_t unix_exposure_end = depthFrame.deviceTimestamp;
cout << "one frame total time delay from trigger: " << unix_end - unix_start << endl;
if ((unix_end - unix_exposure_end) > 2000)
{
cout << "NTP/PTP is not work !!!" << endl;
}
else
{
cout << "time delay from exposure end: " << unix_end - unix_exposure_end << endl;
}
}
}
this_thread::sleep_for(chrono::milliseconds(1000 / frameRate));
}
执行结果如下:
---DeviceSWTriggerMode---
Get device count: 1
serialNumber:GN6501CBCA3310172
ip:192.168.1.101
connectStatus:1
frameRate :15
Software trigger test begins
get Frame successful,status:0 frameTpye:0 frameIndex:1
one frame total time delay from trigger: 85
time delay from exposure end: 66
get Frame successful,status:0 frameTpye:0 frameIndex:2
one frame total time delay from trigger: 83
time delay from exposure end: 64
get Frame successful,status:0 frameTpye:0 frameIndex:3
one frame total time delay from trigger: 77
time delay from exposure end: 59
get Frame successful,status:0 frameTpye:0 frameIndex:4
one frame total time delay from trigger: 77
time delay from exposure end: 59
get Frame successful,status:0 frameTpye:0 frameIndex:5
one frame total time delay from trigger: 76
time delay from exposure end: 57
get Frame successful,status:0 frameTpye:0 frameIndex:6
one frame total time delay from trigger: 80
time delay from exposure end: 60
get Frame successful,status:0 frameTpye:0 frameIndex:7
one frame total time delay from trigger: 77
time delay from exposure end: 59
get Frame successful,status:0 frameTpye:0 frameIndex:8
one frame total time delay from trigger: 77
time delay from exposure end: 58
get Frame successful,status:0 frameTpye:0 frameIndex:9
one frame total time delay from trigger: 78
time delay from exposure end: 59
get Frame successful,status:0 frameTpye:0 frameIndex:10
one frame total time delay from trigger: 79
time delay from exposure end: 61
---end---
通过上述方法可以多次测试并统计当前系统的延时。
以如上打印结果为例,得知此次测试的两个测试指标。
(a)单帧总延时:80ms左右。
(b)除曝光之外的延时时间为60ms左右。
Vzense ToF相机延时测试数据
以NYX650(B22) + ScepterSDK(v1.1.4)为例,ToF曝光时间5ms, RGB分辨率640×480,开Depth2RGB对齐,获取对齐后Depth与RGB图像延时测试数据如下表所示:
平台 | 本机处理 | 网络传输 | SDK处理 | 总延时 |
RK3588 | 64ms | 11ms | 20ms | 64+11+20 = 95ms |
Nvidia Xavior NX | 64ms | 10ms | 10ms | 64+10+10 = 84ms |
DS系列产品的延迟相对于NYX会更高一些。以DS86(B22) + ScepterSDK(v1.1.4)为例,ToF曝光时间1ms, RGB分辨率640×480,开Depth2RGB对齐,获取对齐后Depth与RGB图像延时测试数据如下表所示:
平台 | 本机处理 | 网络传输 | SDK处理 | 总延时 |
RK3588 | ||||
Nvidia Xavior NX |
延时优化
如果在测试后发现,整体延时无法满足应用需求,可以从如下几个角度进行调节优化:
1、图像分辨率:图像分辨率越大,需要处理的数据量越大,因此分辨率的不同对延时影响最为巨大。建议在业务允许的情况下,选择较小的分辨率,如640×480。
2、系统平台:系统平台的性能直接影响SDK的运行效率与性能,因此尽量选择符合要求的平台,以及尽量平衡其他进程对相机SDK资源的抢占。
3、点云转换:点云转换涉及大量float运算,消耗较大,尽量减少点云转换的数量可以较大的提高效率。如只转换ROI(region of interest)区域点云,不做全图转换。
4、滤波开关:根据业务需求,可以关闭部分滤波开关,以减少资源占用与提高延时。
5、相机型号:Vzense相机有不同型号,不同型号相机因为原理不同,实现方式不同,也会存在延时差异。