请参照我们的在线文档:http://doc.percipio.xyz/cam/last/apiguides/api_description.html
因为所有SDK都是基于C++代码,所以以C++为例:
下载链接:https://www.percipio.xyz/downloadcenter/
目前图漾SDK支持:Android、ROS、C#、Python、Windows、Halcon、Linux(ubuntu以及嵌入式开发)
输出的深度图是一个16bit位深的单通道矩阵,在SDK提供的sample程序中,输出的深度图均被映射到了RGB色彩空间,所以显示出来的结果为RGB三通道8bit位深的位图,深度数据无外参,无畸变,只能提供用于转换点云数据的内参。
IR图像为CMOS输出的红外图像,部分型号的相机在输出了深度图的同时输出IR图像之后,IR图像是会被相机系统处理过的图像,如果需要看原始的IR图像,需要关闭深度图输出,这个时候,激光器也会自动关闭,如果需要打开激光器,需在主动调用SDK API关闭激光器的自动控制功能,并且设置激光机强度,才能看到正常的红外点阵图像,IR图像分为左IR图和右IR图,二者均包含内参和畸变参数,但是因为左IR和深度图是同一个空间坐标系,所以左IR无外参。
percipio不同型号的相机提供输出不同数据类型的RGB图像,一类为正常的YUV422/JPG图像,经OPENCV处理之后,最终显示为RGB位图,另外一类RGB图像是基于RAW Sensor输出的原始bayer图像,SDK提供了软件ISP处理功能,最终显示为RGB位图,RGB图提供了内参,外参数,畸变参数。
通过运行 SDK 开发包中的 lib/win/hostapp/x64 中可执行文件,或者运行 sample/build/bin/Release 目录下的可执行文件,可简单应用相机。
备注,Percipio 提供了在 SDK 基础上二次开发的看图软件 Percipio Viewer,支持用户快速浏览深度图、彩色图、红外图和点云图,以及在线调整相机的曝光参数、激光亮度等。 Percipio Viewer 安装包下载链接:https://www.percipio.xyz/downloadcenter。 使用说明请参见 Percipio Viewer 用户指南。
TYApi.h:设备操作相关API接口,包含设备操作及设备相关参数读写控制等
TYCoordinateMapper.h:坐标系转换相关API接口:
1.基于相机像素坐标系和空间点云坐标系相互转换接口;
2.基于RGB外参的深度图和RGB图像坐标系相互转换接口;
3.基于RGB外参的点云坐标系转换接口;
TYImageProc.h:主要包含滤波和矫正处理的API接口:
1.TYUndistortImage:根据畸变参数矫正图像
2.TYDepthSpeckleFilter:过滤相机输出的原始深度图中的噪点
3.TYDepthEnhenceFilter:利用多帧以及双边滤波优化深度图数据
4.TyIsp.h:bayer输出的RGB图像软ISP处理相关的API接口,相关用法可以参考SDK中的sample代码SimpleView_FetchFrame
示例:
ListDevices
该示例程序用于枚举连接到上位机上的所有深度相机。
DumpAllFeatures
该示例程序用于枚举深度相机支持的各个组件和属性,以及各个属性支持的读写等信息。
ForceDeviceIP
该示例程序用于强制设置网络深度相机的 IP 地址。
LoopDetect
该示例程序用于处理因环境不稳定等因素造成的数据连接异常。
SimpleView_FetchFrame
该示例程序用于深度相机工作在自由采集模式下连续采集图像并输出图像数据。
SimpleView_Callback
该示例程序用于深度相机工作在自由采集模式下连续采集图像,在独立的数据处理线程(防止堵塞图像数据获取)中进行 OpenCV
渲染,并输出图像数据。
SimpleView_FetchHisto
该示例程序用于获取图像亮度数据直方图。
SimpleView_MultiDevice
该示例程序用于多台深度相机同时连续采集图像并输出图像数据。
SimpleView_Point3D
该示例程序用于获取 3D 点云数据。
SimpleView_Registration
该示例程序用于获取深度相机的内参、外参、深度图和彩色图,并将深度图和彩色图对齐。
SimpleView_TriggerDelay
该示例程序用于设置硬件触发延时时间,深度相机在接收到硬件触发信号并等待特定延时之后采集图像。
SimpleView_TriggerMode0
该示例程序用于设置深度相机工作在模式 0,相机连续采集图像并以最高帧率输出图像数据。
SimpleView_TriggerMode1
该示例程序用于设置深度相机工作在模式 1,相机收到软触发指令或硬触发信号后采集图像并输出图像数据。
只有网络相机才有Resend,重传可以参考此样例
SimpleView_TriggerMode_M2S1
该示例程序用于设置主设备(相机)工作在模式 2,多台从设备(相机)工作在模式 1,以实现多台深度相机级联触发,同时采集图像。
主设备收到上位机发送的软件触发信号后,通过硬件 TRIGGER OUT
接口输出触发信号,同时触发自身采集并输出深度图;从设备收到主设备的硬件触发信号后,采集并输出深度图。
SimpleView_TriggerMode_M3S1
该示例程序用于设置主设备(相机)工作在模式 3,多台从设备(相机)工作在模式 1,以实现多台深度相机按照设置的帧率级联触发,同时采集图像。
主设备按照设置的帧率,通过硬件 TRIGGER OUT
接口输出触发信号,同时触发自身采集并输出深度图;从设备收到主设备的硬件触发信号后,采集并输出深度图。
SimpleView_TriggerMode18
该示例程序用于设置深度相机工作在模式 18,相机每接收到一次软触发指令或硬触发信号后,便按照设置的帧率,以 1+duty
的方式采集一轮图像并输出图像数据(1:出 1 次泛光;duty:出 duty 次激光)。
SimpleView_TriggerMode19
该示例程序用于设置深度相机工作在模式 19,相机接收到一次软触发或者硬触发信号后,便按照设置的帧率,以 1+duty
的方式连续采集图像并输出图像数据(1:出 1 次泛光;duty:出 duty 次激光)。
SimpleView_TriggerMode20
该示例程序用于设置深度相机工作在模式
20,根据设置的触发开始时间(start_time_us)、每两帧的时间间隔数组(offset_us_list[
])和触发次数(offset_us_count),相机定时采集(1 +offset_us_count)帧图像并输出图像数据。启用此⼯作模式要求相机先启动 PTP 对时,且 offset_us_count ≤ 50。
SimpleView_TriggerMode21
该示例程序用于设置深度相机工作在模式
21,根据设置的触发开始时间(start_time_us)、触发次数(trigger_count)和触发时间间隔(peroid_us),相机每间隔
peroid_us 采集⼀帧图像,共采集 trigger_count 帧图像并输出图像数据。此工作模式要求相机先启动 PTP 对时。
过滤和填洞、相机相关的属性设置:https://gitee.com/jiyuchen/percipio_-sample.git
网络相机重连示例:https://gitee.com/jiyuchen/percipio_-re-connect.git
下面以 SDK 示例程序 Simpleview_FetchFrame 为例详细说明图像获取流程。
TYInitLib 初始化设备对象等数据结构。
获取设备列表
初次获取设备信息时可以通过 selectDevice() 查询已连接的设备数量,并获得所有已连接的设备列表。 |
---|
TYOpenInterface 打开接口。 |
---|
TYOpenDevice 打开设备。
TYOpenDeviceWithIP 打开网络设备。
查询设备的组件状态
TYGetComponentIDs 获取设备支持的组件信息。
TYGetEnabledComponents 获取已经打开的组件信息。
TYEnableComponents 使能组件。
TYDisableComponents 关闭组件。
设备打开后,默认只有虚拟组件 TY_COMPONENT_DEVICE 是使能状态。多个组件可以通过 位或 方式同时使能。
int32_t componentIDs = TY_COMPONENT_DEPTH_CAM | TY_COMPONENT_RGB_CAM; TYEnableComponents(hDevice, componentIDs);
查询指定属性的信息:
TYGetFeatureInfo() 通过填充结构体 TY_FEATURE_INFO 来获取指定组件的指定属性的信息。如果指定组件不包含所指定的属性,则 TY_FEATURE_INFO 中 isValid 值为 false;如果该组件包含所指定的参数,则 TY_FEATURE_INFO 中 isValid 值为 true。也可以通过 TYGetIntRange 等具体参数类型的 API 接口查询指定功能参数的信息。
- 常用读写属性函数如下:
- TYGetIntRange
- TYGetInt
- TYSetInt
- TYGetFloatRange
- TYGetFloat
- TYSetFloat
- TYGetEnumEntryCount
- TYGetEnumEntryInfo
- TYGetEnum
- TYSetEnum
- TYGetBool
- TYSetBool
- TYGetStringLength
- TYGetString
- TYSetString
- TYGetStruct
- TYSetStruct
示例:
调用 TYSetEnum() 设置深度图像传感器输出数据的格式和分辨率:
LOGD("=== Configure feature, set resolution to 640x480."); ASSERT_OK(TYSetEnum(hDevice, TY_COMPONENT_DEPTH_CAM, TY_ENUM_IMAGE_MODE, TY_IMAGE_MODE_DEPTH16_640x480));
调用 API 查询当前配置下每个帧缓冲的大小。
uint32_t frameSize; ASSERT_OK( TYGetFrameBufferSize(hDevice, &frameSize) ); LOGD(" - Get size of framebuffer, %d", frameSize);
分配深度数据存储空间。
按照实际查询函数返回的帧缓冲的大小分配两组 frameBuffer 空间,并传递给底层驱动缓冲队列,作为数据获取的通道。
驱动内部维护一个缓冲队列(frameBuffer Queue),每帧数据传出时会将填充好的 frameBuffer 作 Dequeue 操作,并完全传出给用户使用。用户完成该帧图像数据处理后,需做 Enqueue 动作以返还该 frameBuffer 给驱动层缓冲队列。用户需要保证新的一帧数据到来时驱动的缓冲队列不为空,否则该帧数据将被丢弃。
LOGD(" - Allocate & enqueue buffers"); char* frameBuffer[2]; frameBuffer[0] = new char[frameSize]; frameBuffer[1] = new char[frameSize]; LOGD(" - Enqueue buffer (%p, %d)", frameBuffer[0], frameSize); ASSERT_OK( TYEnqueueBuffer(hDevice, frameBuffer[0], frameSize) ); LOGD(" - Enqueue buffer (%p, %d)", frameBuffer[1], frameSize); ASSERT_OK( TYEnqueueBuffer(hDevice, frameBuffer[1], frameSize) );
TYRegisterEventCallback
使用回调函数的方式获取图像数据时,需要注册该函数,当图像数据到达后,该回调函数会主动被执行。在使用主动获取图像模式时,需要调用该函数注册回调函数为 NULL。
LOGD(“Register event callback”); ASSERT_OK(TYRegisterEventCallback(hDevice, eventCallback, NULL))
根据实际需要配置深度相机工作模式,详情请参考 工作模式配置。
TYStartCapture
如果深度相机工作在模式 1 下,可以使用软件触发接口函数 TYSendSoftTrigger(),通过 USB 接口或者以太网接口发送指令,控制相机图像采集的时机。
TYFetchFrame
主动获取深度数据模式下,应用可调用该接口获取深度数据。注意回调函数模式下不需要调用。获取数据后,用户程序进行运算处理时,应采用独立线程,避免堵塞图像获取线程的运转。
TYStopCapture 停止图像数据采集,相机停止深度数据计算和输出。
TYCloseDevice 关闭设备,TYCloseInterface 释放占用的接口。
TYDeinitLib 释放 API 后,需要释放分配的内存资源,避免内存泄露。
参照链接在线文档:这里不再赘述
SDK中定义了一个结构:TY_CAMERA_CALIB_INFO,该结构内包含了相机出厂前标定好的标定尺寸,内参,外参,畸变参数,SDK API利用相关参数进行相关处理的时候都是使用这个结构的,可以通过使用API:TYGetStruct读取的该结构,需要注意的是,该标定参数和当前相机实际输出分辨率下的参数并不一致,SDK提供的相关API都会在内部根据实际图像分辨率自动调整参数来进行相关处理。
Fx 0 Cx
0 Fy Cy
0 0 1
Fx:归一化横向焦距
Fy:归一化纵向焦距
Cx:像素坐标系横向中心位置
Cy:像素坐标系纵向中心位置
R11 R12 R13 T1
R21 R22 R23 T2
R31 R32 R33 T3
0 0 0 1
该参数用于初始坐标系中的坐标点向新的坐标系转换
R11 R12 R13 T1分别为x在新的坐标系中x y z三轴的旋转系数及偏移量
R21 R22 R23 T2分别为y在新的坐标系中x y z三轴的旋转系数及偏移量
R31 R32 R33 T3分别为z在新的坐标系中x y z三轴的旋转系数及偏移量
一般双目相机只需要矫正RGB畸变,TOF相机需要对深度图矫正畸变(详见对齐或者点云实例)
畸变参数包含多个畸变因素的参数,参见SDK头文件定义:
typedef struct TY_CAMERA_DISTORTION
{
float data[12];///
}TY_CAMERA_DISTORTION;
1.k1 k2 k3 k4 k5 k6是基于理想模型的径向畸变系数
2.p1 p2 为切向畸变系数
3.s1 s2 s3 s4 为薄棱镜畸变系数
SDK中提供了利用该参数进行图像畸变矫正的API接口:
TY_CAPI TYUndistortImage (const TY_CAMERA_CALIB_INFO *srcCalibInfo
, const TY_IMAGE_DATA *srcImage
, const TY_CAMERA_INTRINSIC *cameraNewIntrinsic
, TY_IMAGE_DATA *dstImage
);
SDK中定义了一个结构:TY_CAMERA_CALIB_INFO,该结构内包含了相机出厂前标定好的标定尺寸,内参,外参,畸变参数,SDK API利用相关参数进行相关处理的时候都是使用这个结构的,可以通过使用API:TYGetStruct读取的该结构,需要注意的是,该标定参数和当前相机实际输出分辨率下的参数并不一致,SDK提供的相关API都会在内部根据实际图像分辨率自动调整参数来进行相关处理。
参考示例:SimpleView_Registration
该示例程序用于获取深度相机的内参、外参、深度图和彩色图,并将深度图和彩色图对齐。
对齐方式:RGBToDepth 和DepthToRGB,
一般用到深度学习 ,建议深度对应到彩色(DepthToRGB),这个会产生一个和RGB对齐的DEPTH,后面会用到新的DEPTH去取数据和转点云
// 定义一个函数 doRgbRegister(),实现对齐功能,下面是我们doRgbRegister代码:
static void doRegister(const TY_CAMERA_CALIB_INFO& depth_calib
, const TY_CAMERA_CALIB_INFO& color_calib
, const cv::Mat& depth
, const float f_scale_unit
, const cv::Mat& color
, bool needUndistort
, cv::Mat& undistort_color
, cv::Mat& out
, bool map_depth_to_color
)
{
// do undistortion
if (needUndistort) {
TY_IMAGE_DATA src;
src.width = color.cols;
src.height = color.rows;
src.size = color.size().area() * 3;
src.pixelFormat = TY_PIXEL_FORMAT_RGB;
src.buffer = color.data;
undistort_color = cv::Mat(color.size(), CV_8UC3);
TY_IMAGE_DATA dst;
dst.width = color.cols;
dst.height = color.rows;
dst.size = undistort_color.size().area() * 3;
dst.buffer = undistort_color.data;
dst.pixelFormat = TY_PIXEL_FORMAT_RGB;
ASSERT_OK(TYUndistortImage(&color_calib, &src, NULL, &dst));
}
else {
undistort_color = color;
}
// do register
if (map_depth_to_color) {
out = cv::Mat::zeros(undistort_color.size(), CV_16U);
ASSERT_OK(
TYMapDepthImageToColorCoordinate(
&depth_calib,
depth.cols, depth.rows, depth.ptr<uint16_t>(),
&color_calib,
out.cols, out.rows, out.ptr<uint16_t>(), f_scale_unit
)
);
cv::Mat temp;
//you may want to use median filter to fill holes in projected depth image
//or do something else here
cv::medianBlur(out, temp, 5);
out = temp;
}
else {
out = cv::Mat::zeros(depth.size(), CV_8UC3);
ASSERT_OK(
TYMapRGBImageToDepthCoordinate(
&depth_calib,
depth.cols, depth.rows, depth.ptr<uint16_t>(),
&color_calib,
undistort_color.cols, undistort_color.rows, undistort_color.ptr<uint8_t>(),
out.ptr<uint8_t>(), f_scale_unit
)
);
}
}
可以在TYCoordinateMapper.h文件可以找到相关像素和坐标的转换:
对齐之后RGBPix=DepthPix,经过这个函数TYMapDepthToPoint3d -》深度转点云坐标
SimpleView_Point3D
该示例程序用于获取 3D 点云数据
pcl::PointCloud<pcl::PointXYZRGB> cloud; // 生成的点云 cloud
pcl::PointXYZRGB point;
for (int m = 0; m < depth.rows; m++)
{
for (int n=0; n < depth.cols; n++)
{
point.x = p3d[(m*(depth.cols)+n)].x;
point.y = p3d[(m*(depth.cols)+n)].y;
point.z = p3d[(m*(depth.cols)+n)].z;
point.r = color_data_mat.at<cv::Vec3b>(m, n)[0];
point.g = color_data_mat.at<cv::Vec3b>(m, n)[1];
point.b =color_data_mat.at<cv::Vec3b>(m, n)[2];
cloud.points.push_back(point); // 构造xyzrgb类型点云
}
}
cloud.width = (uint32_t)cloud.points.size();
cloud.height = 1;
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("3D Viewer"));
pcl::PointCloud<pcl::PointXYZRGB>::Ptr basic_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGB>);
basic_cloud_ptr = cloud.makeShared(); // 转换为指针格式 basic_cloud_ptr
basic_cloud_ptr->is_dense = false; // 自己创建的点云,默认为dense,需要修改属性,否则removenanfrompointcloud函数无效
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGB>);
std::vector<int> mapping;
pcl::removeNaNFromPointCloud(*basic_cloud_ptr, *cloud_ptr, mapping); // 移除无效点
viewer1->removeAllPointClouds(); // 移除当前所有点云
viewer1->addPointCloud<pcl::PointXYZRGB> (cloud_ptr, "initial");
viewer1->updatePointCloud(cloud_ptr, "initial");
viewer1->spinOnce(100);
我们提供相关点云,可以给予自己需要保存自己相应格式。可以用我们的percipio-view保存点云用CloudCompare查看点云细节。
1. 深度图通过外参转换到RGB的像素坐标系之后,如果需要使用内参转换到空间点云坐标系,调用相关API的时候使用的的参数应该改为RGB参数,而不是深度相机的参数。
2. SDK sample代码SimpleView_Registration中默认的设置演示的是RGB图像反向对齐到深度相机坐标系,但是因为深度图上会有失效点,所以这些位置无法对应到RGB坐标系上面去,因此演示的对齐之后的RGB图像会是不完整的,如果要保证RGB图像的完整性,可以将代码中的宏MAP_DEPTH_TO_COLOR置为1,将深度图转换到RGB坐标系中去,这样就能得要一个转换了坐标系的深度图和矫正畸变之后完整的RGB图像。
3. Sample代码中,以SimpleView_FetchFrame为例,默认是将RGB/IR/Depth输出全部打开的,在这种情况下,设备输出的数据量会非常大,USB/网络带宽会将预览的帧率拉到很低,验证深度图的输出帧率的时候,可以将IR/RGB输出全部关闭。
4. Sample代码中显示深度图的代码会将深度图映射到RGB色彩空间,该函数对CPU的消耗比较大,特别是在debug模式下,将会对帧率影响非常大。
5. Sample代码中预览的时候调用的函数get_fps可以统计系统实时运行程序时候的帧率,相关代码可以查看文件common.hpp,将变量kMaxCounter的值改小一些可以提高帧率显示的频率。
6. Sample代码中的SimpleView_Point3D在使用cmake生成项目的时候默认是不会编译的,需要将CMakeLists.txt文件中的OPENGL_VIEW置成ON,然后使用Cmake才能生成对应的项目,另外该示例代码依赖于opengl模块,请确认系统中已经配置好了opengl。
7. 对于Sample代码中的SimpleView_Point3D,需要注意的是在代码中将深度图转化成点云之后,增加了y和z坐标的取反操作,这在代码及数据处理中并不是必要的,在这里仅仅是为了在3D视窗中显示的时候提前做一次镜像,让显示视窗看起来“更正常一点”。
8. 相机输出的深度图,最好调用一次喊出TYDepthSpeckleFilter,进行一次滤波,特别是网口相机,基于精度更好的算法,深度数据中的边沿会有一些脏点,需要经过这个函数进行滤波,让深度数据更干净一些。
9. 滤波函数TYDepthEnhenceFilter只做了3D滤波和双边滤波,函数参数中虽然有导向滤波的指针参数,但是需要设置成NULL,代码里面目前并没有实现此滤波功能。
10. 关于相机连接上的一些问题:
a) USB相机使用的USB线应该严格按照USB工业标准线长进行限制:USB2.0不能超过5米,USB3.0不能超过2米,否则会因为线长过长
的信号衰减以及电磁干扰等原因导致SDK无法打开相机或者无法通过SDK获取图像数据。
b) 网口相机需要保证相机和上位机在同一个网段,相机本身默认支持DHCP自动获取IP地址,在通过路由器网络系统的时候,保证PC也是通过路由器自动获取IP,这样二者就可以保持在同一个网段了,但是如果相机和PC是通过交换机直连的话,在windows系统下,上位机和相机都可以通过自动获取IP得到一个私网IP地址,这样二者就可以联通,但是在linux下,默认系统是无法自动获取IP的,这时需要手动设置Linux系统的IP地址,然后通过SDK的sample程序中的ForceDevicesIP强制设置相机的IP,并且和上位机IP为同一个IP网段。