Unity3D开发技术研究- 商汤SenseAR案例

一、案例说明

SenseAR开发者平台能够提供实时跟踪与建图、尺度估计、多平面检测、光照估计、手势检测、稠密重建功能。

(1)实时跟踪与建图:能够实现6DOF跟踪,融合视觉和IMU信息,实时定位手机的位姿信息和输出周围环境的地图信息。
(2)尺度估计: 尺度估计把虚拟物体模型以真实的大小准确的放在真实场景中。
(3)多平面检测:快速检测水平面和竖直平面(如地面和墙面)的大小和位置。
(4)光照估计:估计当前环境的光照情况 。
(5)手势检测:包括手势的2D/3D关键点、手势姿态类型等信息的检测、以及手势在RGB图和深度图上分割结果输出,支持基于手的AR互动交互。
(6)稠密重建:融合深度与RGB信息,建立稠密环境网格,实现实时环境稠密重建。
(7)图像识别与跟踪:借助增强图像功能,帮助识别并标记环境中的一系列2D目标图像,并在摄像头移到图像外时仍可标记该图像位置。
(8)云锚点:利用云端技术使位于同一现实场景中的多台设备可加载同一个锚点,并渲染到各自的场景中,在该锚点上进行AR体验和交互。
(9)人脸识别与跟踪:基于RGB信息,获取增强现实中的人脸模型,实现人脸的实时识别与跟踪。
(10)三维物体识别与跟踪:基于RGB信息,在线建立三维物体模型,并实现三维物体模型的实时识别与跟踪。

二、框架视图

三、手势识别

工程
一: 初始化
主要步骤:
1、初始化增强现实对象。
初始化过程中,需要创建ARWorld这个对象。ARWorld是整个增强现实世界的管理对象,通过ARWorld可以获取手势算法运行过程中的手势等信息,同时也可以通过ARWorld获取当前画面帧对象,通过画面帧对象,用户可以获取当前帧的相机位姿等信息。

ARWorld mARWorld = new ARWorld

2、算法配置
通过ARConfig可以对ARWorld进行配置,主要配置开启的算法、开启模式等重要信息。比如要以RGB后置摄像头模式开启手势识别算法,则可以这样配置。

ARConfig config = new ARConfig(mARWorld);
mCurrentAlgorithmMode = ARConfig.AlgorithmMode.ENABLED;       config.setAlgorithmMode(ARConfig.AlgorithmType.HAND_GESRURE,ARConfig.AlgorithmMode.ENABLED);
mARWorld.configure(config);

手势识别配置参考代码如下:

  private ARWorld mARWorld;
  private ARMap mMap;

  protected void onCreate(Bundle savedInstanceState) {
    //其他代码省略
   Exception exception = null;
        String message = null;
        try {
            mARWorld = new ARWorld(/* context= */ this);
        } catch (UnavailableApkTooOldException e) {
            message = "Please Install AR Library";
            exception = e;
        } catch (UnavailableSdkTooOldException e) {
            message = "Please update this app";
            exception = e;
        } catch (UnavailableDeviceNotCompatibleException e) {
            message = "This device does not support AR";
            exception = e;
        }

        if (message != null) {
            Log.e(TAG, "Exception creating session", exception);
            return;
        }

        // Create default config and check if supported.
        ARConfig config = new ARConfig(mARWorld);
        if (!mARWorld.isSupported(config)) {
            Toast.makeText(this, "This device does not support AR", Toast.LENGTH_SHORT).show();
        }
        //使用RGB相机
        mCurrentStreamMode = ARConfig.StreamMode.BACK_RGB;
        //使用RGBD摄像头
        //mCurrentStreamMode = ARConfig.StreamMode.BACK_RGBD;
        config.setStreamMode(mCurrentStreamMode);
        config.setAlgorithmMode(ARConfig.AlgorithmType.HAND_GESRURE, ARConfig.AlgorithmMode.ENABLED);
        mARWorld.configure(config);
        mMap = mARWorld.acquireMap();

}

二: 获取手势信息
1、通过上面的配置,开启了手势识别算法,要获取手势信息,只需在GLSurfaceView.Renderer的onDrawFrame里每帧调用ARWorld类的update函数,更新帧以及帧对应的各类算法运行信息。

ARFrame frame = mSession.update();

2、通过增强现实地图对象ARMap获取正在追踪的手势列表。

List handGestures = (List)       mMap.getAllNodes(ARHandGestureNode.class);

3、目前版本的手势只支持单手,所以从List中,取出第一个ARHandGestureNode对象,就是要获取的手势的对象,ARHandGestureNode对象描述了详细的手势信息。

      if (handGestures.size() > 0) {
                ARHandGestureNode handGesture = handGestures.get(0);
          //如果要获取手势的类型信息,则通过调用手势对象handGesture的 //getHandGestureType()方法获取,要获取其他信息,则通过此对象的相对应的方法获取
   ARHandGestureNode.HandGestureType handGestureType = handGesture.getHandGestureType();
    }

整个获取手势信息的参考代码如下:

 public void onDrawFrame(GL10 gl) {
            ARFrame frame = mARWorld.update();
            //获取正在跟踪的手势列表
            List handGestures = (List) mMap.getAllNodes(ARHandGestureNode.class);
            if (handGestures.size() > 0) {
                ARHandGestureNode handGesture = handGestures.get(0);
                //获取手势类型
                ARHandGestureNode.HandGestureType handGestureType = handGesture.getHandGestureType();
                //获取手势是左手还是有手
                ARHandGestureNode.HandSide handSide = handGesture.getHandSide();
                //获取手势的朝向(手心,手背,侧手)
                ARHandGestureNode.HandTowards handTowards = handGesture.getHandTowards();
                //获取手势框
                Rect handRect = handGesture.getHandRect();
                //获取2d手指关键点的数量
                int point2dCount = handGesture.getLandMark2DCount();
                //获取手指关键点2D坐标(x,y)
                Vector3f[] points2d = handGesture.getGetLandMark2DPoints();
                //获取3d手指关键点的数量
                int point3dCount = handGesture.getLandMark3DCount();
                //获取手指关键点3D坐标(x,y,z)
                Vector3f[] points3d = handGesture.getGetLandMark3DPoints();
                //获取手掌中心法向量
                Vector3f plamCenter = handGesture.getPalmCenter();
                //获取手掌朝向法向量
                Vector3f plamNormal = handGesture.getPalmNormal();
          }
    //其他代码已省略
}

三、云锚点共享

通过云锚点,可以让位于同一现实场景中的多台设备加载同一个锚点,并渲染到各自的场景中,在该锚点上进行AR体验和交互。
云锚点可以让位于同一现实场景的设备多次进入同一个AR场景。
云锚点的解析和托管通过商汤服务器完成,为了体验该AR效果,必须保证客户端设备连接互联网,可以正常访问商汤服务器。
托管锚点
锚点数据保存在商汤服务器,上传的原始数据无法通过任何方式获取。
在托管时,周围的环境特征也会被上传,该数据在上传前已被处理为点云数据,不会辨别用户当前的地理位置和物理环境。
锚点在托管成功后的168个小时内,均可以通过云锚点ID进行解析。
解析锚点
解析锚点可以同步多台设备在相同物理环境中的坐标系框架。
解析锚点时会将您当前物理环境中的点云信息上传至服务器作为解析依据,解析后得到当前设备坐标系下的云锚点坐标。
解析锚点的请求数据在解析完成后就会被抛弃。

共享锚点ID
示例应用使用普通的服务器在设备之间共享云锚点ID。您可以在自己的应用中使用不同的解决方案。
示例所需的应用层服务器代码在SDK中提供,请按以下步骤部署:
1、在SenseAR SDK for Android中找到cloud_anchor_Demo_Server文件,并且确保您的计算机可以运行python3命令。
2、打开终端并执行以下指令python3 server.py,您可以新开一个终端运行python3 client.py进行测试。
3、在工程ARCloudClient.java文件中,配置您电脑的IP。

package com.standardar.ar.core.examples.c.arcloud;

public class ARCloudClient {
    private static final String AR_CLOUD_ADDR = "YOUR IP ADDRESS";
    private static final int AR_CLOUD_PORT = 9092;

    //...
}

添加应用key/secret
使用锚点云服务,必须要在应用中添加一个key和secret密钥。
1、获取key/secret,参见快速入门。
2、在Android Studio中,将key/secret添加至您的项目中。

void CloudAnchorApplication::OnResume() {
    //...
    const ArStatus status = ArSession_resume(ar_session_);
    arWorldSetKeyAndSecret(ar_session_, "YOUR KEY", "YOUR SECRET");
    //...
}

工程
开启云锚点功能
云锚点功能默认是关闭的状态,要开启云锚点功能需要调用arConfigSetAlgorithmMode开启云锚点功能。

CHECK(arWorldCreate(env, context, &ar_session_) == ARRESULT_SUCCESS);
CHECK(ar_session_);

ARConfig *ar_config = nullptr;
arConfigCreate(&ar_config);
arConfigSetAlgorithmMode(ar_config, ARALGORITHM_TYPE_SLAM, ARALGORITHM_MODE_ENABLE);
arConfigSetAlgorithmMode(ar_config, ARALGORITHM_TYPE_PLANE_DETECTION, ARALGORITHM_MODE_ENABLE);
arConfigSetAlgorithmMode(ar_config, ARALGORITHM_TYPE_LIGHT_ILLUMINATION, ARALGORITHM_MODE_ENABLE);
arConfigSetAlgorithmMode(ar_config, ARALGORITHM_TYPE_CLOUD_ANCHOR, ARALGORITHM_MODE_ENABLE);

CHECK(arWorldConfigure(ar_session_, ar_config) == ARRESULT_SUCCESS);

设置key/secret
要使用云锚点服务,需要经过云服务授权,进行key/secret认证。

CHECK(arWorldResume(ar_session_) == ARRESULT_SUCCESS);
arWorldSetKeyAndSecret(ar_session_, "YOUR KEY", "YOUR SECRET");

托管锚点
将本地锚点托管至云端使之成为云锚点,可供多台设备解析并共享。arWorldHostAnchor获取的云锚点对象可能是未就绪的状态,当状态为

ARCLOUD_ANCHOR_STATE_SUCCESS方可使用,未就绪时获取到的ID为空。

//anchor is local anchor, m_cloud_anchor is cloud anchor.
if(arWorldHostAnchor(ar_session_, anchor, &m_cloud_anchor) == ARRESULT_SUCCESS) {
    m_cloud_anchor_in_progress = true;
}

锚点解析
通过云锚点ID为线索进行解析云锚点,arWorldResolveAnchor方法获取的云锚点对象可能是未就绪的状态,当状态为ARCLOUD_ANCHOR_STATE_SUCCESS方可使用,未就绪时获取的位姿为不可用状态。

if(arWorldResolveAnchor(ar_session_, anchor_id, &m_cloud_anchor) == ARRESULT_SUCCESS){
    m_cloud_anchor_in_progress = true;
}

获取云锚点状态
当进行了托管锚点或解析锚点操作后,需要不断地查询云锚点状态,
ARCLOUD_ANCHOR_STATE_TASK_IN_PROGRESS表示锚点还在托管/解析中,需再等待;
ARCLOUD_ANCHOR_STATE_SUCCESS表示托管/解析成功,可以进行后续操作;
ARCLOUD_ANCHOR_STATE_ERROR_XXX表示在托管/解析过程中发生了错误,需释放该锚点,重新进行托管/解析操作。

if(m_cloud_anchor_in_progress){
    arAnchorGetCloudState(ar_session_, m_cloud_anchor, &m_cloud_state);
    switch (m_cloud_state){
        // code
    }
}

获取云锚点ID
获取云锚点ID的操作一般在锚点托管成功后进行,将云端分配的云锚点ID进行分享,供其他用户进行解析该锚点。

if(m_cloud_anchor_in_progress){
    arAnchorGetCloudState(ar_session_, m_cloud_anchor, &m_cloud_state);
    switch (m_cloud_state){
        case ARCLOUD_ANCHOR_STATE_SUCCESS: {
            // ...
            arAnchorGetCloudAnchorId(ar_session_, m_cloud_anchor, m_anchor_id, 64);
            break;
        }
        // ...
    }
}

云锚点流程图


四、三维物体识别与跟踪

三维物体跟踪与识别主要是分为创建模型和物体识别与跟踪两部分。SenseAR通过对真实环境中的三维物体进行实时扫描,在线创建并保存扫描后的模型文件。当物体出现在相机视野中,SenseAR将通过加载物体模型文件,检测该三维物体,用户可通过移动手机实时检测和追踪该物体在真实环境中的位置,从而实现AR效果与三维物体的空间一致性。

功能描述

  • SenseAR可以扫描环境中的三维物体并保存为模型文件。
  • SenseAR可以检测和跟踪环境中的三维物体,提供物体的位置以及朝向等参数。
  • 识别与跟踪的效果与扫描结果有关,扫描越充分,保存的点云数量越多,识别与跟踪的效果越好。
  • 所有功能都是在设备本地完成的,无须远程访问连接网络。
  • 扫描APK已经提供,下载地址ObjectC.apk

操作流程

1.放置Bounding box:初始化后点击右上角scan按钮,之后点击屏幕即可放置Bounding Box;
2.Bounding box调整

  • 调整大小:两个手指分离或者聚合实现;

  • 调整形状:双击bounding box的某一个面,手指离开屏幕再点击屏幕拖拉,实现增加或者减小垂直于该面的线段长度;

  • 调整位置:单击后松开再点击实现平行于该面的拖移;

3.Bounding box染色:点击屏幕下方中间scan按钮开始扫描,移动手机将bounding box所有面染色,完成所有的染色后点击finish按钮,至此,完成扫描过程;
4.识别跟踪:点击右上角的track按钮,实现手机画面再次出现该三维物体的时候,能够识别跟踪该物体的功能,识别跟踪成功的标志是手机中显示之前扫描过程中的andriod小人;

最佳操作

关于选择三维模型

  • 建议使用表面有丰富纹理,纹理区分度高,不透明且无明显反光的刚性物体。如果有部分区域纹理不明显可能会导致在这个区域无法识别。
  • 建议扫描过程中光线较明亮。
  • 建议使用过程中背景纹理不要过于丰富。
  • 模型扫描过程中如果保存的点云数量较少时,会导致后续检测跟踪过程效果变差。
  • 确定三维包围框将物体包围住,包围框在长高宽三个方向大小合适。
  • 在扫描过程中,手机移动需要速度较慢且无较大抖动。

关于优化识别和跟踪

  • 跟踪环境中的三维物体与扫描时相比外观无明显变化。
  • 物件所在环境中跟踪与扫描时相比无明显的光线变化。
  • 在跟踪过程中相机运动不要过快。
  • 最大识别面积为1/9。(图像在相机画面中的占比)

加载参考物体

首先需要创建新的ARReferenceObjectDatabase

ARReferenceObjectDatabase objectDatabase = new ARReferenceObjectDatabase(mWorld);

然后可以选择以下方式添加参考图像。

添加扫描工具生成的物体模型文件

使用arReferenceObjectDatabaseAddObject向数据库中添加物体模型:

std::string sampleObjectName[1] = {"1.obj"};
std::string file_path = "/sdcard/1.obj";
char* object_buffer;
int object_buffer_length;
if(util::LoadFile(file_path, &object_buffer, &object_buffer_length)) {
    if(object_buffer && object_buffer_length > 0) {
        int index = 0;
        ARResult status = arReferenceObjectDatabaseAddObject(ar_world_,                                                        ar_object_tracking_database,                                                          sampleObjectName[0].c_str(), (uint8_t *) object_buffer,                                                          object_buffer_length,&index);
        CHECK(status == ARRESULT_SUCCESS);
        delete[] object_buffer;
    }
}
break;

注意事项

目前只支持单物体跟踪,一次只能加载一个模型文件。

启用物体追踪

通过ARConfig配置所需的参考物体数据库,SenseAR开始识别跟踪物体:

arConfigSetAlgorithmMode(ar_config, ARALGORITHM_TYPE_OBJECT_TRACKING, ARALGORITHM_MODE_ENABLE);
ARReferenceObjectDatabase* ar_object_database = CreateObjectTrackingDatabase();
arConfigSetReferenceObjectDatabase(ar_world_, ar_config, ar_object_database);
arReferenceObjectDatabaseDestroy(ar_object_database);
CHECK(arWorldConfigure(ar_world_, ar_config) == ARRESULT_SUCCESS);

在会话期间,SenseAR将通过摄像头图像中的特征点匹配参考物体数据库来寻找物体。
要获取匹配的物体,请在您的帧更新循环中轮询有无更新的ARObjectNode

ARNodeList *updated_object_list = nullptr;
arNodeListCreate(ar_world_, &updated_object_list);
CHECK(updated_object_list != nullptr);
arMapGetAllNodes(ar_world_, ar_map_, ARNODE_TYPE_OBJECT, updated_object_list);

int32_t object_list_size;
arNodeListGetSize(ar_world_, updated_object_list, &object_list_size);
float light_intensity = 0.8f;

for (int i = 0; i < object_list_size; ++i) {
    ARNode *ar_node = nullptr;
    arNodeListAcquireItem(ar_world_, updated_object_list, i,
                          &ar_node);
    ARObjectNode *object = ARNodeAsARObjectNode(ar_node);

    ARTrackingState tracking_state;
    arNodeGetTrackingState(ar_world_, ar_node, &tracking_state);

    int object_index;
    arObjectNodeGetIndex(ar_world_, object, &object_index);
    switch (tracking_state) {
        case ARTRACKING_STATE_STOPPED:
            break;
        case ARTRACKING_STATE_SUCCESS: {
            util::ScopedArPose scopedArPose(ar_world_);
            arObjectNodeGetCenterPose(ar_world_, object,
                                      scopedArPose.GetArPose());
            ARPose *pose = scopedArPose.GetArPose();

            // Render Andy objects.
            glm::mat4 model_mat(1.0f);
            arPoseGetMatrix(pose, glm::value_ptr(model_mat));
            andy_renderer_.Draw(projection_mat, view_mat, model_mat, light_intensity);
            break;
        }
        default:
            break;
    }
}

arNodeListDestroy(updated_object_list);
updated_object_list = nullptr;

你可能感兴趣的:(Unity3D开发技术研究- 商汤SenseAR案例)