Astra SDK  v2.1.3
API

API体系结构

这张图里的箭头展示了数据流。

Astra 由几个库组成。核心库是astra_core.dll(或者libastra_core.so)。astra_core.dll提供了加载实现Streams的插件和从StreamReaders中获取数据的功能。它仅负责提供数据但不关心解析数据的格式,仅仅只是把数据当做顺序的字节数组。这给了我们在不修改astra_core.dll里的接口的前提下灵活的添加新的streams的可能。如果要增加新的stream类型,比如新的中间件,我们可以添加到现有的插件或新增一个插件来实现。

另一个库是astra.dll。这个库了解Astra SDK的插件,所以它可以把从astra_core.dll得到的Streams的字节数据解析成确定的数据类型。它也提供了一些和插件交互的方法,比如修改分辨率或者获取视场角(field of view)。应用使用Astra SDK的api时astra.dll和astra_core.dll都要用到。另外插件也会使用astra.dll来获取其他插件提供的数据。

Astra SDK 的插件提供了Astra SDK中所有Streams的数据源。其中openni_sensor.dll基于OpenNI2获取原始传感器数据,包括深度和彩色。其他插件使用这些原始数据并处理产生点云数据(PointFrame)和骨架数据(BodyFrame)等。

所有这些都在后台处理,SDK仅仅在需要应用打开对应的stream时才会计算该stream的数据,避免不必要的性能消耗。比如应用不打开BodyStream,就不会执行骨架识别算法。

还有一个库astra_core_api.dll,应用开发用不到所以不需要了解但是是插件运行必需的。以上就是Astra SDK的主要架构。

注意:本骨架模块为非商用版本,限定单次使用时长30分钟,如果需要商用化,请联系对应的商务或客服。

如何在C++中使用API

Hello World

是否想在深入研究之前体验一下我们的SDK?那就让我们动手写些代码吧!

于本教程结束前,您应该熟悉:SDK的适当初始化与终止-从传感器中读取数据-检查Astra深度相机提供的深度信息。

开始前

如果您跳过了安装SDK和构建SDK提供的示例应用程式部分,请确保您至少已将Astra下载并压缩到可以轻松访问的文件夹中。

言归正传!

第一步,我们将构建一个基础应用程序,作为逐步添加新功能的起点。

1.使用您喜欢的IDE,设置一个新的控制台程序项目并创建一个新的名为“main.cpp”的源文件。

2.将下述内容复制到您的 main.cpp 文件中:

#include <astra/astra.hpp>
#include <cstdio>
#include <iostream>
int main(int argc, char** argv)
{
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}
  • 所有的应用程序必须包含astra.hpp。它是Astra的核心,是所有基于C++的Astra应用程序所必须的。
  • 我们将使用std::cin.get()以确保我们有机会在应用程序关闭窗口前看到自己的行为结果。

Astra的初始化与终止

为了让Astra准备好执行我们的命令,我们首先必须初始化Astra,这毫无疑问是通过初始化功能键完成。当我们准备结束SDK的会话时,我们仍需给Astra一个彻底关闭的机会。这是通过调用终止功能来完成。除此之外,该功能还可以正确地关闭所连接的设备。

添加以下两行:

int main(int argc, char** argv)
{
astra::initialize();
// what will go here? you'll find out soon!
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

先确认一下

先别操之过急,让我们先花点时间来确保一切都如我们所预期。然后,编译并运行该应用程序。下一步,启动应用程序,打印一系列诊断信息到控制台,再耐心的等待按下“回车”键。一旦按下“回车”键,该应用程序便会正常退出。

注意

默认情况下,Astra会将大量诊断信息输出到控制台。如果您遇到了问题,这可能是一个寻找答案的好地方。接下来:与Astra对话。

连接到Astra

现在我们知道了如何正确初始化和终止Astra,是时候与Astra传感器进行实际通信了。为此,我们使用StreamSet类,它广泛封装了一组相关数据源的概念(例如:来自2D相机的视频和音频)。但是,就目前而言,将StreamSet视为诸如Astra和StreamSet类之类的实体设备便足够,即它是您访问实体设备功能的门户。

在初始化和终止信息管理系统之间,让我们声明一个StreamSet变量。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

现在,可以肯定的是,它看起来似乎是我们先前步骤的一个小补充,但是这一行比它看起来要重要得多。仅通过声明和构造一个StreamSet对象,您就可以指示Astra开始连接到它可以定位的第一个可用的Astra传感器的过程。酷吧?

注意

Astra提供一个使您可以连接到特定Astra传感器的额外的构造函数。现在我们已经建立连接,并且已经准备好做我们要做的事 - 透过Astra之眼来看!

检索传感器数据

是时候充分利用我们的StreamSet对象来获取一些数据了。为此,我们需要读取Astra提供的一个数据流。数据流包含来自我们相机的数据,该等数据被打包在称为“帧”的数据包中。当前,Astra支持多种类型的流,包括深度流、色彩流、手部流和点云流。

为访问来自Astra的数据流并访问到帧,我们需要一个StreamReader来接入其中一个数据流。出于本应用程序的目的,我们将专注于深度流。该流向我们提供了相机可以看到的被打包在一个帧中的任何像素的距离(以像素为单位)。

首先,让我们使用StreamSet创建一个StreamReader。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
//Creates a StreamReader
astra::StreamReader reader = streamSet.create_reader();
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

接下来,我们用上一步创建的StreamReader来启动深度流。启动深度流会告诉Astra,我们对从StreamSet中获取的深度数据感兴趣。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
//Starts the depth stream
reader.stream<astra::DepthStream>().start();
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

随着深度流的打开,让我们从其中提取最新的深度帧。要做到这一点,我们首先需要通过StreamReader来检索最新的帧,然后调用get<T>从我们的帧中获得深度帧数据。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
//Retrieves the latest frame
astra::Frame frame = reader.get_latest_frame();
//Gets the depth frame from the latest frame
const auto depthFrame = frame.get<astra::DepthFrame>();
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

剩下的唯一任务是从我们刚刚检索的深度帧中打印一些数据。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
astra::Frame frame = reader.get_latest_frame();
const auto depthFrame = frame.get<astra::DepthFrame>();
//Gets a copy of the frame index from our depth frame
const int frameIndex = depthFrame.frame_index();
//Gets a copy of the value within the first pixel of our depth frame's data
const short pixelValue = depthFrame.data()[0];
//Prints the two aforementioned values to the console
std::cout << std::endl
<< "Depth frameIndex: " << frameIndex
<< " pixelValue: " << pixelValue
<< std::endl
<< std::endl;
astra::terminate();
//Pauses execution so we can soak in our success
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

您现在可以继续运行您的应用程序来检测一切是否正常。就像之前一样,控制台窗口应该会弹出并显示诊断信息。然后您应该会看到一行包含我们获取到的帧数据。运行完成后,请按“回车”键。

您刚从Astra中获取到第一帧!在您们完成Astra速成课程之前,还有一项任务,那就是处理一系列帧。

使用StreamSet内的流

既然您已知道如何创建StreamReader并从中获取帧,那么您现在可以使用帧的数据流了。要做到这一点,我们仅需进行一个小小的更改,就可以循环调用StreamReader中的get_latest_frame函数,在此等情况下,我们将从深度流中获取前100帧,并将每个帧的第一个像素值打印到控制台。

下述代码与我们上一个示例的代码非常相似,唯一的不同是除了我们在帧处理代码周围添加了一个do while循环之外,还添加了一些变量用于存储循环次数和我们想要处理的最大帧数。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
//Stores the maximum number of frames we're going to process in the loop
const int maxFramesToProcess = 100;
//Sentinel to count the number of frames that we've processed
int count = 0;
//The frame processing loop
do {
astra::Frame frame = reader.get_latest_frame();
const auto depthFrame = frame.get<astra::DepthFrame>();
const int frameIndex = depthFrame.frame_index();
const short pixelValue = depthFrame.data()[0];
std::cout << std::endl
<< "Depth frameIndex: " << frameIndex
<< " pixelValue: " << pixelValue
<< std::endl
<< std::endl;
count++;
} while (count < maxFramesToProcess);
std::cout << "Press any key to continue...";
std::cin.get();
astra::terminate();
std::cout << "hit enter to exit program" << std::endl;
std::cin.get();
return 0;
}

编译并运行。当程序运行且Astra聚焦于您时,稍微移动一点点,并观察帧上的数据值变化。

获得成就!您刚刚完成了您的第一个Astra应用程序!如果您尚未在Astra上获得足够乐趣,请继续进行“获取流数据”。

获取流数据

数据流类型

Astra SDK附带了各种数据流类型的“库”。低层次数据流类型由传感器生成,并通过SDK传递给应用程序。通过SDK插件可从低层次数据流类型中有效地计算出更高层次的数据流类型。您可以根据您的需要选择合适的数据流。

低层次

流类型 描述
ColorStream 来自传感器的RGB像素数据。每个ColorFrame中包含的数据组包含了每个像素的每个颜色组件的0-255范围内的值。当启动InfraredStream时,切勿启动它。
DepthStream 来自传感器的深度数据。每个DepthFrame中包含的数据组包含了传感器视场内的每个像素的毫米值。
InfraredStream 来自传感器的红外数据。当启动ColorFrame时,切勿启动它。

高层次

流类型 描述
PointStream 根据深度数据计算的世界坐标(XYZ)。每个PointStream中包含的每个点帧属于Astra数据组:Vector3f元素可以更轻松地访问每个像素的x、y和z值。
HandStream 根据深度数据计算的手部节点。每一个HandFrame上,任何给定时间检测到的节点的数量可以通过HandFrame::handpoint_count函数来检索,并且可以通过HandFrame::handpoints函数来获取astra::HandFrame::HandPointList。
BodyStream 根据深度数据计算得出的人体数据。它包含19个关节的二维位置和三维位置,以及深度数据和向下取整信息的用户掩码。它最多可支持5人。如果您要使用此数据流,则需要从Orbbec获取许可证代码。如果没有有效的许可证代码,您的应用程序只能在启动后30分钟内获取人体数据。
MaskedColorStream 根据色彩和人体数据进行计算。格式为RGBA8888。它包含用户的色彩数据。我们可根据人体数据计算ColorizedBodyStream。其格式为RGB888。它使用不同的色彩来显示不同的用户。

数据获取

Astra SDK提供了两种方法来获取流数据。根据您的特定用例和应用程序的复杂性,其中的一种方法可能比另一种更适合。

轮询

获取帧数据的轮询方法是获取流数据的最直接方法,也是Hello World教程中使用的方法。为使用此方法,您只需要在StreamReader上调用get_latest_frame函数,然后在返回的帧上使用模板get <T>函数来检索特定的帧类型。本示例中,“ T”必须是有效的帧类型。当get_latest_frame 函数被阻塞,从而致使应用程序执行将暂停,直到有新的帧返回。如果您想限制SDK以等待新帧到达的时间,可以将超时作为参数传递给get_latest_frame 函数。

astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
astra::Frame frame = reader.get_latest_frame();
const auto depthFrame = frame.get<astra::DepthFrame>();
astra::terminate();
  • 使用轮询方法检索深度帧

监听

获取帧数据的监听方法需要少量的附加设置,但允许开发人员将帧的处理委托给一个或多个单独的类。Astra SDK提供了一个名为FrameListener的抽象类别,该类别实现了一个名为FrameListener::on_frame_ready 的函数。一旦帧准备好处理并引用传递该帧时,就会立即调用FrameListener::on_frame_ready 函数。

class DepthFrameListener : public astra::FrameListener
{
virtual void on_frame_ready(astra::StreamReader& reader,
astra::Frame& frame) override
{
const astra::DepthFrame depthFrame = frame.get<astra::DepthFrame>();
if (depthFrame.is_valid())
{
// do all the things
}
}
};
  • 从frame_listener中派生的监听器类的示例

定义了监听器类后,为了使用它,必须在应用程序中实例化监听器,然后使用StreamReader::add_listener函数将其添加到StreamReader中。

astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
DepthFrameListener listener;
reader.add_listener(listener);
while(true)
{
astra_update();
}
  • 监听器的示例用法

实际上,对astra_update循环应该仅用于应用程序关闭或发生另一个特定于应用程序的事件之前执行。

一旦添加监听器后,我们需要使用astra_update函数来抽取SDK的事件循环。这样一来,SDK可以检查帧是否可用,如果可用,在这种情况下,将调用DepthFrameListener :: on_frame_ready函数并引用传递最新的帧。

有关监听器的更实用示例,请继续阅读SimpleDepthReader

Simple Depth Reader

完成 Hello World教程之后,想获取更多知识吗?现在您已经掌握了AstraSDK的一些基本概念,让我们使用另一种功能来读取Astra的深度流。

在本教程结束时,您应该熟悉:- FrameListener类的用途 - 如何定义FrameListener - 使用FrameListener处理深度流。

在我们开始之前 1. 如果你还没有准备请先下载和解压最新的AstraSDK。2. 使用您喜欢的IDE,设置一个新的控制台应用程序项目,创建一个名为“ main.cpp”的新源文件。 3. 将以下内容复制到main.cpp文件中:

#include <astra/astra.hpp>
// for std::printf
#include <cstdio>
int main(int argc, char** argv)
{
//Initializes Astra SDK
astra::initialize();
//Constructs a StreamSet
astra::StreamSet streamSet;
//Creates a StreamReader
astra::StreamReader reader = streamSet.create_reader();
//Starts a depth stream
reader.stream<astra::DepthStream>().start();
// Your code will go here
//Terminates Astra SDK
astra::terminate();
return 0;
}

监听数据流

在HelloWorld教程中,我们通过循环调用StreamReader的get_latest_frame函数来处理帧流。在简单的情况下,例如我们的Hello World应用程序,此解决方案非常有效。但是,如果我们想注册大量的数据流并使用它们呢?或者,如果我们使用多个StreamSet,或者每个StreamSet可能使用多个StreamReader,又该怎么处理呢?所有这些情况中,循环中的代码都可能很快变得复杂、混乱且麻烦。

为解决该等问题,AstraSDK为我们提供了一个定义和创建FrameListeners的框架。FrameListener有一个名为on_frame_ready的函数(您猜对了!),当准备处理特定类型的新帧时,将调用该函数。因此,我们的监听器不会在StreamReader的get_latest_frame函数上循环,而是在帧准备好后立刻自动将最新的帧传递给它。好极了!

为了在我们的示例中使用FrameListener。我们需要定义一个实现FrameListener的监听器类。此监听器类将使我们能够访问来自Astra传感器的实际帧。我们将在on_frame_ready函数中获得这些帧。将以下代码复制到 # include指令下方和main函数上方:

class DepthFrameListener : public astra::FrameListener
{
public:
//Constructor parameter specifies the total number of frames we're going to process before exiting our loop
DepthFrameListener(int maxFramesToProcess)
: maxFramesToProcess_(maxFramesToProcess)
{}
//is_finished will be used in a later step to check whether we've looped the maximum number of times or not
bool is_finished() const { return isFinished_; }
private:
void on_frame_ready(astra::StreamReader& reader,
astra::Frame& frame) override
{
//Gets the depth frame data from our frame
const astra::DepthFrame depthFrame = frame.get<astra::DepthFrame>();
//Check to verify that we received a valid frame
if (depthFrame.is_valid())
{
//Prints depth frame information to the console
print_depth_frame(depthFrame);
++framesProcessed_;
}
isFinished_ = framesProcessed_ >= maxFramesToProcess_;
}
void print_depth_frame(const astra::DepthFrame& depthFrame) const
{
const int frameIndex = depthFrame.frame_index();
const short middleValue = get_middle_value(depthFrame);
std::printf("Depth frameIndex: %d value: %d \n", frameIndex, middleValue);
}
short get_middle_value(const astra::DepthFrame& depthFrame) const
{
const int width = depthFrame.width();
const int height = depthFrame.height();
//Calculates the index of the middle pixel in our depth frame's data
const size_t middleIndex = ((width * (height / 2.f)) + (width / 2.f));
const short* frameData = depthFrame.data();
//Gets the value of the middle depth frame pixel
const short middleValue = frameData[middleIndex];
return middleValue;
}
bool isFinished_{false};
int framesProcessed_{0};
int maxFramesToProcess_{0};
};

注意
唯一需要的函数是on_frame_ready函数。此类中的其他函数将用来支持我们在该函数中所做的工作。

定义DepthFrameListener后,让我们在主函数中构造监听器,并将其添加到上一步创建的StreamReader中。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
int maxFramesToProcess = 100;
//Constructs a DepthFrameListener that will loop 100 times
DepthFrameListener listener(maxFramesToProcess);
//Adds the listener to our reader
reader.add_listener(listener);
// More of your code will go here
//Removes the listener from our reader
reader.remove_listener(listener);
astra::terminate();
return 0;
}

更新监听器

我们已经运行了AstraSDK和StreamSet,并且正在监听通过StreamSet的StreamReader流入的深度帧。我们不知道帧何时会从Astra到达,因此我们需要在循环中调用astra_update来不断更新那些监听器。

int main(int argc, char** argv)
{
astra::initialize();
astra::StreamSet streamSet;
astra::StreamReader reader = streamSet.create_reader();
reader.stream<astra::DepthStream>().start();
const int maxFramesToProcess = 100;
DepthFrameListener listener(maxFramesToProcess);
reader.add_listener(listener);
//The Astra SDK update loop.
do {
astra_update();
} while (!listener.is_finished());
reader.remove_listener(listener);
astra::terminate();
return 0;
}

让我们编译并运行我们的解决方案。在您将一些深度帧信息打印到控制台后,您会发现您已经掌握了监听器以及其他Astra SDK核心功能。现在,再接再厉,发挥您的想象力,并使用Astra SDK进行各种创新!

如何在Unity中使用API

使用方法

初始化设备

AstraManager是一个MonoBehaviour单例类,只需要将AstraManager.cs这个脚本挂在场景中的GameObject上就可以自动初始化了。

你还可以直接使用AstraManager.Instance来进行初始化:

var astra = AstraManager.Instance;

监听初始化成功和失败事件

//Initialize success event listener
AstraManager.Instance.OnInitializeSuccess.AddListener(InitializeSuccessHandler);
//Initialize failed event listener
AstraManager.Instance.OnInitializeFailed.AddListener(InitializeFailedHandler);

打开数据流

//Open depth stream
AstraManager.Instance.IsDepthOn = true;
//Open color stream
AstraManager.Instance.IsColorOn = true;
//Open body stream
AstraManager.Instance.IsBodyOn = true;
//Open colorized body stream
AstraManager.Instance.IsColorizedBodyOn = true;
//Open masked color stream
AstraManager.Instance.IsMaskedColorOn = true;

监听并获取数据帧

//New depth frame event listener
AstraManager.Instance.OnNewDepthFrame.AddListener(NewDepthFrameHandler);
//New color frame event listener
AstraManager.Instance.OnNewColorFrame.AddListener(NewColorFrameHandler);
//New body frame event listener
AstraManager.Instance.OnNewBodyFrame.AddListener(NewBodyFrameHandler);
//New colorized body frame event listener
AstraManager.Instance.OnNewColorizedBodyFrame.AddListener(NewColorizedBodyFrameHandler);
//New masked color frame event listener
AstraManager.Instance.OnNewMaskedColorFrame.AddListener(NewMaskedColorFrameHandler);

获取纹理

//Get depth texture
var depthTex = AstraManager.Instance.DepthTexture;
//Get color texture
var colorTex = AstraManager.Instance.ColorTexture;
//Get colorized body texture
var colorizedBodyTex = AstraManager.Instance.ColorizedBodyTexture;
//Get masked color texture
var maskedColorTex = AstraManager.Instance.MaskedColorTexture;

获取人体及关节数据

var bodies = AstraManager.Instance.Bodies;
foreach (var body in bodies)
{
var joints = body.Joints;
if (joints != null)
{
foreach (var joint in joints)
{
if (joint.Status == Astra.JointStatus.Tracked)
{
//Do somthing with joint
}
}
}
}

关闭数据流

//Close depth stream
AstraManager.Instance.IsDepthOn = false;
//Close color stream
AstraManager.Instance.IsColorOn = false;
//Close body stream
AstraManager.Instance.IsBodyOn = false;
//Close colorized body stream
AstraManager.Instance.IsColorizedBodyOn = false;
//Close masked color stream
AstraManager.Instance.IsMaskedColorOn = false;

设置图像模式

//Get available depth modes
var depthModes = AstraManager.Instance.AvailableDepthModes;
//Set depth mode;
AstraManager.Instance.DepthMode = depthModes[0];
//Get available color modes
var colorModes = AstraManager.Instance.AvailableColorModes;
//Set color mode;
AstraManager.Instance.ColorMode = colorModes[0];

设置license

你可以在AstraManager的Inspector面板中直接设置license,或者在脚本的Awake方法中调用以下方法设置:

//Set the license number.
AstraManager.Instance.License = "<LICENSE STRING>";

提示:

更多高级功能请参考C# 接口参考

API版本变更

Astra SDK v2.1.3
Copyright (c) 2015-2020 Orbbec
https://www.orbbec3d.com
For help and support, visit us at https://3dclub.orbbec3d.com.
Orbbec Body Tracking trial time expiration
===============
If you want to use this stream, you need get license code from Orbbec. Without the
valid license code, your app can only get body data for 30 minutes from started.
If you have a license, See API notes below for orbbec_body_tracking_set_license().
For support on the trial expiration or to extend your trial, please contact
info@orbbec3d.com.
What's New
==========
v2.1.3 2021/06/10
support SELinux contexts on Unity.
fix usage of some device resolutions.
fix usage of the original samples.
add basic samples on Android.
simplify usage of the SELinux contexts on Android.
v2.1.2 2021/02/27
support astra+(pid 0x0632) and astra+ s(pid 0x0633).
support Ubuntu 20.04.
support SELinux contexts on Android and provide corresponding sample(android\AstraSDKJavaDemo\app\src\main\java\com\orbbec\obviewer2\listener\OBAstraDeviceManagerListener.java).
support list return of available resolution modes of depth image and color image supported by the current device with specify pid.
add apis to control the fans of astra+ and astra+ s.
add apis to start/stop ldp on Unity.
remove the occupation of UnityActivity on Unity.
v2.1.1 2020/04/10
add apis to start/stop recording depth stream.
add OrbbecBodyTracking.config to control distance of skeleton and segmentation.
fix blocking when app exits using sdk for vs2013.
v2.1.0 2020/03/07
support arm64-v8a on Android.
fix bugs of class CoordinateMapper in java and c#.
add AndroidCamera.setCamera method to choose the uvc camera.
support astra nh glst(pid 0x0601) and bus cl(pid 0x0610).

API 参考手册

API 参考手册