Visão geral do SDK de tempo de execução

Esta seção fornece uma visão geral de alto nível do Object Anchors Runtime SDK, que é usado para detetar objetos usando um modelo de Object Anchors. Você obterá uma compreensão de como um objeto é representado e para que os vários componentes são usados.

Todos os tipos descritos abaixo podem ser encontrados em um dos seguintes namespaces: Microsoft.Azure.ObjectAnchors, Microsoft.Azure.ObjectAnchors.Diagnostics e Microsoft.Azure.ObjectAnchors.SpatialGraph.

Tipos

Modelo de objeto

Um ObjectModel representa a geometria de um objeto físico e codifica os parâmetros necessários para deteção e estimativa de pose. Ele deve ser criado usando o serviço Object Anchors. Em seguida, um aplicativo pode carregar o arquivo de modelo gerado usando a API Object Anchors e consultar a malha incorporada nesse modelo para visualização.

ObjectSearchArea

Um ObjectSearchArea especifica o espaço para procurar um ou vários objetos. É definido por um ID de nó gráfico espacial e limites espaciais no sistema de coordenadas representado pelo ID do nó gráfico espacial. O Object Anchors Runtime SDK suporta quatro tipos de limites, a saber, campo de visão, caixa delimitadora, esfera e um local.

AccountInformation

Uma AccountInformation armazena a ID, a Chave e o Domínio da sua conta do Azure Object Anchors.

ObjectAnchorsSession

Um ObjectAnchorsSession representa uma sessão do Azure Object Anchors que é usada para criar instâncias do ObjectObserver usadas para detetar objetos no mundo físico.

ObjectObserver

Um ObjectObserver carrega modelos de objeto, deteta suas instâncias e relata poses de 6-DoF de cada instância no sistema de coordenadas HoloLens.

Embora qualquer modelo de objeto ou instância seja criado a partir de um observador, seus tempos de vida são independentes. Um aplicativo pode descartar um observador e continuar a usar o modelo de objeto ou instância.

ObjectQuery

Um ObjectQuery informa a um observador de objetos como localizar objetos de um determinado modelo. Ele fornece os seguintes parâmetros ajustáveis, cujos valores padrão podem ser recuperados de um modelo de objeto.

MinSurfaceCobertura

A propriedade MinSurfaceCoverage indica o valor a ser considerado uma instância como detetada.

Para cada candidato a objeto, um observador calcula a proporção de superfícies sobrepostas entre o modelo de objeto transformado e a cena e, em seguida, relata esse candidato à aplicação somente quando a taxa de cobertura está acima de um determinado limite.

IsExpectedToBeStandingOnGroundPlane

A propriedade IsExpectedToBeStandingOnGroundPlane indica se se espera que o objeto de destino fique no plano de solo.

Um plano térreo é o piso horizontal mais baixo na área de pesquisa. Ele fornece uma boa restrição sobre as possíveis poses de objeto. Ligar esta bandeira guiará o observador a estimar a pose em um espaço limitado e poderá melhorar a precisão. Este parâmetro será ignorado se o modelo não for suposto ficar no plano de solo.

ExpectedMaxVerticalOrientationInDegrees

A propriedade ExpectedMaxVerticalOrientationInDegrees indica o ângulo máximo esperado em graus entre a direção ascendente de uma ocorrência de objeto e a gravidade.

Este parâmetro fornece outra restrição na direção ascendente de uma pose estimada. Por exemplo, se um objeto estiver na vertical, esse parâmetro poderá ser 0. As âncoras de objeto não devem detetar objetos diferentes do modelo. Se um modelo estiver na vertical, ele não detetará uma instância colocada de lado. Um novo modelo seria usado para layout lateral. A mesma regra se aplica à articulação.

MaxScaleChange

A propriedade MaxScaleChange indica a alteração máxima da escala do objeto (dentro de 0 ~ 1) em relação ao mapeamento espacial. A escala estimada é aplicada a vértices de objetos transformados centrados na origem e alinhados ao eixo. As escalas estimadas podem não ser a escala real entre um modelo CAD e sua representação física, mas alguns valores que permitem que o aplicativo renderize um modelo de objeto próximo ao mapeamento espacial no objeto físico.

Áreas de Pesquisa

A propriedade SearchAreas indica uma matriz de limites espaciais onde encontrar objeto(s).

O observador procurará objetos no espaço de união de todas as áreas de pesquisa especificadas em uma consulta. Nesta versão, retornaremos no máximo um objeto com maior confiança para reduzir a latência.

ObjectInstance

Um ObjectInstance representa uma posição hipotética onde uma instância de um determinado modelo poderia estar no sistema de coordenadas HoloLens. Cada instância vem com uma SurfaceCoverage propriedade para indicar o quão boa é a pose estimada.

Uma instância é criada pelo método de chamada ObjectObserver.DetectAsync e, em seguida, atualizada automaticamente em segundo plano quando ativa. Um aplicativo pode ouvir o evento de alteração de estado em uma instância específica ou alterar o modo de rastreamento para pausar/retomar a atualização. Uma instância será automaticamente removida do observador quando o rastreamento for perdido.

ObjectDiagnosticsSession

O ObjectDiagnosticSession registra diagnósticos e grava dados em um arquivo.

Um arquivo de diagnóstico inclui a nuvem de pontos de cena, o status do observador e informações sobre os modelos. Essas informações são úteis para identificar possíveis problemas de tempo de execução. Para obter mais informações, consulte as FAQs.

Uso e detalhes do SDK de tempo de execução

Esta seção deve fornecer as noções básicas de como usar o SDK de tempo de execução. Ele deve fornecer contexto suficiente para navegar pelos aplicativos de exemplo para ver como o Object Anchors é usado de forma holística.

Inicialização

Os aplicativos precisam chamar a ObjectObserver.IsSupported() API para determinar se o Object Anchors é suportado no dispositivo antes de usá-lo. Se a ObjectObserver.IsSupported() API retornar false, verifique se o aplicativo habilitou o recurso spatialPerception e\ou atualize para o sistema operacional HoloLens mais recente.

using Microsoft.Azure.ObjectAnchors;

if (!ObjectObserver.IsSupported())
{
    // Handle the error
}

// This call should grant the access we need.
ObjectObserverAccessStatus status = await ObjectObserver.RequestAccessAsync();
if (status != ObjectObserverAccessStatus.Allowed)
{
    // Handle the error
}

Em seguida, o aplicativo cria um observador de objetos e carrega os modelos necessários gerados pelo serviço de conversão de modelo Object Anchors.

using Microsoft.Azure.ObjectAnchors;

// Note that you need to provide the Id, Key and Domain for your Azure Object
// Anchors account.
Guid accountId = new Guid("[your account id]");
string accountKey = "[your account key]";
string accountDomain = "[your account domain]";

AccountInformation accountInformation = new AccountInformation(accountId, accountKey, accountDomain);
ObjectAnchorsSession session = new ObjectAnchorsSession(accountInformation);
ObjectObserver observer = session.CreateObjectObserver();

// Load a model into a byte array. The model could be a file, an embedded
// resource, or a network stream.
byte[] modelAsBytes;
ObjectModel model = await observer.LoadObjectModelAsync(modelAsBytes);

// Note that after a model is loaded, its vertices and normals are transformed
// into a centered coordinate system for the ease of computing the object pose.
// The rigid transform can be retrieved through the `OriginToCenterTransform`
// property.

O aplicativo cria uma consulta para detetar instâncias desse modelo dentro de um espaço.

#if WINDOWS_UWP || DOTNETWINRT_PRESENT
#define SPATIALCOORDINATESYSTEM_API_PRESENT
#endif

using Microsoft.Azure.ObjectAnchors;
using Microsoft.Azure.ObjectAnchors.SpatialGraph;
using Microsoft.Azure.ObjectAnchors.Unity;
using UnityEngine;

// Get the coordinate system.
SpatialGraphCoordinateSystem? coordinateSystem = null;

#if SPATIALCOORDINATESYSTEM_API_PRESENT
SpatialCoordinateSystem worldOrigin = ObjectAnchorsWorldManager.WorldOrigin;
if (worldOrigin != null)
{
    coordinateSystem = await Task.Run(() => worldOrigin.TryToSpatialGraph());
}
#endif

if (!coordinateSystem.HasValue)
{
    Debug.LogError("no coordinate system?");
    return;
}

// Get the search area.
SpatialFieldOfView fieldOfView = new SpatialFieldOfView
{
    Position = Camera.main.transform.position.ToSystem(),
    Orientation = Camera.main.transform.rotation.ToSystem(),
    FarDistance = 4.0f, // Far distance in meters of object search frustum.
    HorizontalFieldOfViewInDegrees = 75.0f, // Horizontal field of view in
                                            // degrees of object search frustum.
    AspectRatio = 1.0f // Aspect ratio (horizontal / vertical) of object search
                       // frustum.
};

ObjectSearchArea searchArea = ObjectSearchArea.FromFieldOfView(coordinateSystem.Value, fieldOfView);

// Optionally change the parameters, otherwise use the default values embedded
// in the model.
ObjectQuery query = new ObjectQuery(model);
query.MinSurfaceCoverage = 0.2f;
query.ExpectedMaxVerticalOrientationInDegrees = 1.5f;
query.MaxScaleChange = 0.1f;
query.SearchAreas.Add(searchArea);

// Detection could take a while, so we run it in a background thread.
IReadOnlyList<ObjectInstance> detectedObjects = await observer.DetectAsync(query);

Por padrão, cada instância detetada será rastreada automaticamente pelo observador. Opcionalmente, podemos manipular essas instâncias alterando seu modo de rastreamento ou ouvindo seu evento de alteração de estado.

using Microsoft.Azure.ObjectAnchors;

foreach (ObjectInstance instance in detectedObjects)
{
    // Supported modes:
    // "LowLatencyCoarsePosition"    - Consumes less CPU cycles thus fast to
    //                                 update the state.
    // "HighLatencyAccuratePosition" - Uses the device's camera and consumes more CPU
    //                                 cycles thus potentially taking longer
    //                                 time to update the state.
    // "Paused"                      - Stops to update the state until mode
    //                                 changed to low or high.
    instance.Mode = ObjectInstanceTrackingMode.LowLatencyCoarsePosition;

    // Listen to state changed event on this instance.
    instance.Changed += InstanceChangedHandler;

    // Optionally dispose an instance if not interested in it.
    // instance.Dispose();
}

No evento de alteração de estado, podemos consultar o estado mais recente ou descartar uma instância se ela perder o controle.

using Microsoft.Azure.ObjectAnchors;

void InstanceChangedHandler(object sender, ObjectInstanceChangedEventArgs args)
{
    // Try to query the current instance state.
    ObjectInstanceState state = sender.TryGetCurrentState();

    if (state != null)
    {
        // Process latest state.
        // An object pose includes rotation and translation, applied in
        // the same order to the object model in the centered coordinate system.
    }
    else
    {
        // This object instance is lost for tracking, and will never be recovered.
        // The caller can detach the Changed event handler from this instance
        // and dispose it.
    }
}

Além disso, um aplicativo pode, opcionalmente, gravar uma ou várias sessões de diagnóstico para depuração offline.

using Microsoft.Azure.ObjectAnchors;
using Microsoft.Azure.ObjectAnchors.Diagnostics;

string diagnosticsFolderPath = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
const uint maxSessionSizeInMegaBytes = uint.MaxValue;

// Recording starts on the creation of a diagnostics session.
ObjectDiagnosticsSession diagnostics = new ObjectDiagnosticsSession(observer, maxSessionSizeInMegaBytes);

// Wait for the observer to do a job.

// Application can report some **pseudo ground-truth** pose for an instance
// acquired from other means.
diagnostics.ReportActualInstanceLocation(instance, coordinateSystem, Vector3.Zero, Quaternion.Identity);

// Close a session and write the diagnostics into an archive at specified location.
await diagnostics.CloseAsync(System.IO.Path.Combine(diagnosticsFolderPath, "diagnostics.zip"));

Finalmente, liberamos recursos descartando todos os objetos.

using Microsoft.Azure.ObjectAnchors;

foreach (ObjectInstance instance in activeInstances)
{
    instance.Changed -= InstanceChangedHandler;
    instance.Dispose();
}

model.Dispose();
observer.Dispose();