示例程序说明

示例程序列表

  • ListDevices:该示例程序用于枚举连接到上位机上的所有深度相机。

  • DeviceStorage:该示例程序用于读写相机的 Custom Block Storage 组件区域 (4KB) 和 ISP Block Storage 组件区域 (64KB)。

  • DumpAllFeatures:该示例程序用于枚举深度相机支持的各个组件和属性,以及各个属性支持的读写等信息。

  • ForceDeviceIP:该示例程序用于强制设置网络深度相机的 IP 地址。

  • LoopDetect:该示例程序用于处理因环境不稳定等因素造成的数据连接异常。

  • NetStatistic:该示例程序用于统计网络深度相机的丢包率。

  • SimpleView_FetchFrame:该示例程序用于深度相机工作在自由采集模式下连续采集图像并输出图像数据。

  • SimpleView_Callback:该示例程序用于深度相机工作在自由采集模式下连续采集图像,在独立的数据处理线程(防止堵塞图像数据获取)中进行 OpenCV 渲染,并输出图像数据。

  • SimpleView_FetchHisto:该示例程序用于获取图像亮度数据直方图。

  • SimpleView_MultiDevice:该示例程序用于多台深度相机同时连续采集图像并输出图像数据。

  • SimpleView_Point3D:该示例程序用于获取 3D 点云数据。

  • SimpleView_Registration:该示例程序用于获取深度相机的内参、外参、深度图和彩色图,并将深度图和彩色图对齐。

  • SimpleView_TriggerDelay:该示例程序用于设置硬件触发延时时间,深度相机在接收到硬件触发信号并等待特定延时之后采集图像。

  • SimpleView_TriggerMode0:该示例程序用于设置深度相机工作在模式 0,相机连续采集图像并以最高帧率输出图像数据。

  • SimpleView_TriggerMode1:该示例程序用于设置深度相机工作在模式 1,相机收到软触发指令或硬触发信号后采集图像并输出图像数据。

  • SimpleView_TriggerMode_M2S1:该示例程序用于设置主设备(相机)工作在模式 2,多台从设备(相机)工作在模式 1,以实现多台深度相机级联触发,同时采集图像。 主设备收到上位机发送的软件触发信号后,通过硬件 TRIG_OUT 接口输出触发信号,同时触发自身采集并输出深度图;从设备收到主设备的硬件触发信号后,采集并输出深度图。

  • SimpleView_TriggerMode_M3S1:该示例程序用于设置主设备(相机)工作在模式 3,多台从设备(相机)工作在模式 1,以实现多台深度相机按照设置的帧率级联触发,同时采集图像。 主设备按照设置的帧率,通过硬件 TRIG_OUT 接口输出触发信号,同时触发自身采集并输出深度图;从设备收到主设备的硬件触发信号后,采集并输出深度图。

  • SimpleView_SaveLoadConfig:该示例程序用于将本地的 JSON 文件写入 Storage 组件,并从 Storage 组件中加载已写入的 JSON 文件。此外,程序还支持将 JSON 文件导出到本地,以便检查和配置参数。

  • SimpleView_XYZ48:该示例程序用于解析并显示 XYZ 格式的深度图。

  • SimpleView_Point3D_XYZ48:该示例程序用于获取深度为 xyz48 格式下的 3D 点云数据。

图像获取流程

深度相机的配置和图像获取流程如下图所示。下文以 C++ SDK 例程 Simpleview_FetchFrame 为例详细说明图像获取流程。

图像获取流程图

图像获取流程图

初始化API

TYInitLib 初始化设备对象等数据结构。

// 加载库
LOGD("Init lib");
ASSERT_OK( TYInitLib() );

// 获取 SDK 版本信息
TY_VERSION_INFO ver;
ASSERT_OK( TYLibVersion(&ver) );
LOGD("     - lib version: %d.%d.%d", ver.major, ver.minor, ver.patch);

打开设备

  1. 获取设备列表

    初次获取设备信息时可以通过 selectDevice() 查询已连接的设备数量,并获得所有已连接的设备列表。

    std::vector<TY_DEVICE_BASE_INFO> selected;
    ASSERT_OK( selectDevice(TY_INTERFACE_ALL, ID, IP, 1, selected) );
    ASSERT(selected.size() > 0);
    TY_DEVICE_BASE_INFO& selectedDev = selected[0];
    
  2. 打开接口

    ASSERT_OK( TYOpenInterface(selectedDev.iface.id, &hIface) );
    
  3. 打开设备

    ASSERT_OK( TYOpenDevice(hIface, selectedDev.id, &hDevice) );
    

配置组件

  1. 查询设备的组件状态

    // 获取设备支持的组件信息
    TY_COMPONENT_ID allComps;
    ASSERT_OK( TYGetComponentIDs(hDevice, &allComps) );
    
  2. 配置组件并配置属性

    设备打开后,默认只有虚拟组件 TY_COMPONENT_DEVICE 是使能状态。

    // 查询并使能 RGB 组件+配置 RGB 组件的属性
     if(allComps & TY_COMPONENT_RGB_CAM  && color) {
         LOGD("Has RGB camera, open RGB cam");
         ASSERT_OK( TYEnableComponents(hDevice, TY_COMPONENT_RGB_CAM) );
         //create a isp handle to convert raw image(color bayer format) to rgb image
         ASSERT_OK(TYISPCreate(&hColorIspHandle));
         //Init code can be modified in common.hpp
         //NOTE: Should set RGB image format & size before init ISP
         ASSERT_OK(ColorIspInitSetting(hColorIspHandle, hDevice));
         //You can  call follow function to show  color isp supported features
    
     // 查询并使能左 IR 组件
     if (allComps & TY_COMPONENT_IR_CAM_LEFT && ir) {
         LOGD("Has IR left camera, open IR left cam");
         ASSERT_OK(TYEnableComponents(hDevice, TY_COMPONENT_IR_CAM_LEFT));
     }
    
     // 查询并使能右 IR 组件
     if (allComps & TY_COMPONENT_IR_CAM_RIGHT && ir) {
         LOGD("Has IR right camera, open IR right cam");
         ASSERT_OK(TYEnableComponents(hDevice, TY_COMPONENT_IR_CAM_RIGHT));
     }
    
     // 查询并使能深度组件+配置深度组件的属性
     LOGD("Configure components, open depth cam");
     DepthViewer depthViewer("Depth");
     if (allComps & TY_COMPONENT_DEPTH_CAM && depth) {
    
         /// 配置深度组件的属性(深度图分辨率)
         TY_IMAGE_MODE image_mode;
         ASSERT_OK(get_default_image_mode(hDevice, TY_COMPONENT_DEPTH_CAM, image_mode));
         LOGD("Select Depth Image Mode: %dx%d", TYImageWidth(image_mode), TYImageHeight(image_mode));
         ASSERT_OK(TYSetEnum(hDevice, TY_COMPONENT_DEPTH_CAM, TY_ENUM_IMAGE_MODE, image_mode));
    
         /// 使能深度组件
         ASSERT_OK(TYEnableComponents(hDevice, TY_COMPONENT_DEPTH_CAM));
    
         /// 配置深度组件的属性(scale unit)
         //depth map pixel format is uint16_t ,which default unit is  1 mm
         //the acutal depth (mm)= PixelValue * ScaleUnit
         float scale_unit = 1.;
         TYGetFloat(hDevice, TY_COMPONENT_DEPTH_CAM, TY_FLOAT_SCALE_UNIT, &scale_unit);
         depthViewer.depth_scale_unit = scale_unit;
     }
    

帧缓冲管理

备注

在进行帧缓冲管理之前,确保已通过 TYEnableComponents() 接口使能了所需的组件,并且通过 TYSetEnum() 接口设置了正确的图像格式和分辨率。这是因为帧缓冲的大小取决于这些设置,否则可能会遇到帧缓冲空间不足的问题。

// 查询当前配置下每个帧缓冲的大小
LOGD("Prepare image buffer");
uint32_t frameSize;
ASSERT_OK( TYGetFrameBufferSize(hDevice, &frameSize) );
LOGD("     - Get size of framebuffer, %d", frameSize);

// 分配帧缓冲
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

注册事件回调函数。异常事件发生的时候,会调用 TYRegisterEventCallback 注册的回调函数。以下示例的回调函数中包含重连的异常处理操作。

static bool offline = false;
void eventCallback(TY_EVENT_INFO *event_info, void *userdata)
{
   if (event_info->eventId == TY_EVENT_DEVICE_OFFLINE) {
       LOGD("=== Event Callback: Device Offline!");
       // Note:
       // Please set TY_BOOL_KEEP_ALIVE_ONOFF feature to false if you need to debug with breakpoint!
       offline = true;
   }
}

int main(int argc, char* argv[])
{
  LOGD("Register event callback");
  ASSERT_OK(TYRegisterEventCallback(hDevice, eventCallback, NULL))
  while(!exit && !offline) {
      //Fetch and process frame data
  }
  if (offline) {
     //Release resources
     TYStopCapture(hDevice);
     TYCloseDevice(hDevice);
     // Can try re-open and start device to capture image
     // or just close interface exit
  }
  return 0;
}

配置工作模式

根据实际需要配置深度相机工作模式。关于其他工作模式的配置步骤,请参考 工作模式配置

// 查询工作模式属性有无
bool hasTrigger;
ASSERT_OK(TYHasFeature(hDevice, TY_COMPONENT_DEVICE, TY_STRUCT_TRIGGER_PARAM_EX, &hasTrigger));

if (hasTrigger) {

    // 配置深度相机工作在模式 0
    LOGD("Disable trigger mode");
    TY_TRIGGER_PARAM_EX trigger;
    trigger.mode = TY_TRIGGER_MODE_OFF;
    ASSERT_OK(TYSetStruct(hDevice, TY_COMPONENT_DEVICE, TY_STRUCT_TRIGGER_PARAM_EX, &trigger, sizeof(trigger)));
}

开始图像采集

LOGD("Start capture");
ASSERT_OK( TYStartCapture(hDevice) );

获取帧数据

LOGD("While loop to fetch frame");
bool exit_main = false;
TY_FRAME_DATA frame;
int index = 0;
while(!exit_main) {
    int err = TYFetchFrame(hDevice, &frame, -1);
    if( err == TY_STATUS_OK ) {
        LOGD("Get frame %d", ++index);

        int fps = get_fps();
        if (fps > 0){
            LOGI("fps: %d", fps);
        }

        cv::Mat depth, irl, irr, color;
        parseFrame(frame, &depth, &irl, &irr, &color, hColorIspHandle);
        if(!depth.empty()){
            depthViewer.show(depth);
        }
        if(!irl.empty()){ cv::imshow("LeftIR", irl); }
        if(!irr.empty()){ cv::imshow("RightIR", irr); }
        if(!color.empty()){ cv::imshow("Color", color); }

        int key = cv::waitKey(1);
        switch(key & 0xff) {
        case 0xff:
            break;
        case 'q':
            exit_main = true;
            break;
        default:
            LOGD("Unmapped key %d", key);
        }

        TYISPUpdateDevice(hColorIspHandle);
        LOGD("Re-enqueue buffer(%p, %d)"
            , frame.userBuffer, frame.bufferSize);
        ASSERT_OK( TYEnqueueBuffer(hDevice, frame.userBuffer, frame.bufferSize) );
    }
}

停止采集

ASSERT_OK( TYStopCapture(hDevice) );

关闭设备

// 关闭设备
ASSERT_OK( TYCloseDevice(hDevice));

// 释放接口句柄
ASSERT_OK( TYCloseInterface(hIface) );
ASSERT_OK(TYISPRelease(&hColorIspHandle));

释放API

// 卸载库
ASSERT_OK( TYDeinitLib() );

// 释放分配的内存资源
delete frameBuffer[0];
delete frameBuffer[1];

实例:设置相机的 IP 地址

该部分主要介绍如何使用已编译的 ForceDeviceIP 例程为相机设置 IP 地址,以及常见的 IP 地址设置实例。

备注

IP 地址类型说明:

  • 临时 IP 地址:手动配置并临时分配给设备的 IP 地址。

  • 静态 IP 地址:手动配置并永久分配给设备的 IP 地址。

  • 动态 IP 地址:由网络中的DHCP(动态主机配置协议)服务器自动分配的 IP 地址。

IP 地址指令说明

执行指令后,相机 IP 地址会修改为指令指定的 IP 地址,即时生效;相机断电重启后,恢复原有的 IP 配置。

  • 指令: ForceDeviceIP.exe -force <MAC> <newIP> <newNetmask> <newGateway>

  • 示例: ForceDeviceIP.exe -force 68:f7:56:36:90:a3 192.168.1.160 255.255.255.0 192.168.1.1

  • <MAC>:相机机身标签上的 MAC 地址,格式为:xx:xx:xx:xx:xx:xx。

  • <newIP>: 新指定的 IP 地址。

  • <newNetmask>: 新 IP 地址对应的子网掩码。

  • <newGateway>: 新 IP 地址对应的默认网关。

IP 地址设置实例

实例1

为 Percipio 网络相机设置一个静态的 C 类 IP 地址(192.168.5.12)。

以 Windows 10 为例,操作步骤如下:

  1. 查看计算机当前的 IP 地址配置。

    使用键盘组合键 WIN+R 打开 运行 对话框,输入“cmd”,在弹出的界面中输入 ipconfig 并回车。

    修改计算机 IP 地址

    查看计算机 IP 地址

  2. 检查计算机的 IP 地址是否位于目标网段,如果不在目标网段(例如,目标网段为 192.168.5.XX,当前计算机的 IP 地址在 192.168.2.XX 网段),则需修改计算机的 IP 地址。

    打开电脑上的控制面板,选择 “网络和 Internet” > “网络和共享中心” > “更改适配器配置” > “以太网” > “Internet 协议板块4(TCP/IPv4)”,在弹出的 Internet 协议板块4(TCP/IPv4)属性 对话框中选中 “使用下面的 IP 地址” 并设置 IP 地址、子网掩码和网关:

    修改计算机 IP 地址

    修改计算机 IP 地址

  3. 进入 SDK lib\win\hostapp\x64 目录。在该目录下打开 Windows PowerShell,并执行以下命令:

    ForceDeviceIP.exe -static 06:29:39:05:DA:D1 192.168.5.12 255.255.255.0 192.168.5.1
    

    其中,06:29:39:05:DA:D1 是相机的 MAC 地址;192.168.5.12 是新设的 IP 地址;255.255.255.0 是新 IP 地址对应的子网掩码;192.168.5.1 是新 IP 地址对应的默认网关。

实例2

为 Percipio 网络相机设置一个动态 IP 地址。

以 Windows 10 为例,操作步骤如下:

  1. 进入 SDK lib\win\hostapp\x64 目录。

  2. 在该目录下打开 Windows PowerShell,并执行以下命令:

    ForceDeviceIP.exe -dynamic 06:29:39:05:DA:D1
    

    其中,06:29:39:05:DA:D1 是相机的 MAC 地址。