Observação

Os tutoriais misturados do Academia de realidade foram projetados com o HoloLens (1º gen) e com o fone de cabeça de imersão de realidade misturada.The Mixed Reality Academy tutorials were designed with HoloLens (1st gen) and Mixed Reality Immersive Headsets in mind. Como tal, achamos que é importante deixar esses tutoriais em vigor para os desenvolvedores que ainda estão procurando orientação no desenvolvimento para esses dispositivos.As such, we feel it is important to leave these tutorials in place for developers who are still looking for guidance in developing for those devices. Esses tutoriais não serão atualizados com os conjuntos de ferramentas e as interações mais recentes usados para o HoloLens 2.These tutorials will not be updated with the latest toolsets or interactions being used for HoloLens 2. Eles serão mantidos para continuar a trabalhar nos dispositivos com suporte.They will be maintained to continue working on the supported devices. Haverá uma nova série de tutoriais que serão publicados no futuro, que demonstrarão como desenvolver para o HoloLens 2.There will be a new series of tutorials that will be posted in the future that will demonstrate how to develop for HoloLens 2. Esse aviso será atualizado com um link para esses tutoriais quando eles forem postados.This notice will be updated with a link to those tutorials when they are posted.

Sr e Azure 310: Detecção de objetoMr and Azure 310: Object detection

Neste curso, você aprenderá a reconhecer o conteúdo Visual personalizado e sua posição espacial dentro de uma imagem fornecida, usando o Azure Visão Personalizada recursos de "detecção de objeto" em um aplicativo de realidade misturada.In this course, you will learn how to recognize custom visual content and its spatial position within a provided image, using Azure Custom Vision "Object Detection" capabilities in a mixed reality application.

Este serviço permitirá que você treine um modelo de aprendizado de máquina usando imagens de objeto.This service will allow you to train a machine learning model using object images. Em seguida, você usará o modelo treinado para reconhecer objetos semelhantes e aproximar seu local no mundo real, conforme fornecido pela captura de câmera do Microsoft HoloLens ou uma câmera conectar-se a um PC para headsets de imersão (VR).You will then use the trained model to recognize similar objects and approximate their location in the real world, as provided by the camera capture of Microsoft HoloLens or a camera connect to a PC for immersive (VR) headsets.

resultado do curso

Visão personalizada do Azure, a detecção de objeto é um serviço da Microsoft que permite aos desenvolvedores criar classificadores de imagem personalizados.Azure Custom Vision, Object Detection is a Microsoft Service which allows developers to build custom image classifiers. Esses classificadores podem ser usados com novas imagens para detectar objetos nessa nova imagem, fornecendo limites de caixa dentro da própria imagem.These classifiers can then be used with new images to detect objects within that new image, by providing Box Boundaries within the image itself. O serviço fornece um portal online simples, fácil de usar, para simplificar esse processo.The Service provides a simple, easy to use, online portal to streamline this process. Para obter mais informações, visite os links a seguir:For more information, visit the following links:

Após a conclusão deste curso, você terá um aplicativo de realidade misturada que poderá fazer o seguinte:Upon completion of this course, you will have a mixed reality application which will be able to do the following:

  1. O usuário poderá olhar em um objeto, que foi treinado usando a serviço de visão personalizada do Azure, a detecção de objetos.The user will be able to gaze at an object, which they have trained using the Azure Custom Vision Service, Object Detection.
  2. O usuário usará o gesto de toque para capturar uma imagem do que está olhando.The user will use the Tap gesture to capture an image of what they are looking at.
  3. O aplicativo enviará a imagem para a Serviço de Visão Personalizada do Azure.The app will send the image to the Azure Custom Vision Service.
  4. Haverá uma resposta do serviço que exibirá o resultado do reconhecimento como texto de espaço mundial.There will be a reply from the Service which will display the result of the recognition as world-space text. Isso será realizado por meio da utilização do acompanhamento espacial do Microsoft HoloLens, como uma maneira de entender a posição mundial do objeto reconhecido e, em seguida, usar a marca associada ao que é detectado na imagem, para fornecer o texto do rótulo.This will be accomplished through utilizing the Microsoft HoloLens' Spatial Tracking, as a way of understanding the world position of the recognized object, and then using the Tag associated with what is detected in the image, to provide the label text.

O curso também abordará manualmente o carregamento de imagens, a criação de marcas e o treinamento do serviço, para reconhecer objetos diferentes (no exemplo fornecido, uma xícara), definindo a caixa de limite na imagem que você envia.The course will also cover manually uploading images, creating tags, and training the Service, to recognize different objects (in the provided example, a cup), by setting the Boundary Box within the image you submit.

Importante

Após a criação e o uso do aplicativo, o desenvolvedor deve navegar de volta para a Serviço de Visão Personalizada do Azure e identificar as previsões feitas pelo serviço e determinar se elas estavam corretas ou não (por meio da marcação de qualquer coisa que o serviço perdeu e ajustando as caixasdelimitadoras).Following the creation and use of the app, the developer should navigate back to the Azure Custom Vision Service, and identify the predictions made by the Service, and determine whether they were correct or not (through tagging anything the Service missed, and adjusting the Bounding Boxes). Em seguida, o serviço pode ser treinado novamente, o que aumentará a probabilidade de reconhecê-los em objetos do mundo real.The Service can then be re-trained, which will increase the likelihood of it recognizing real world objects.

Este curso ensinará como obter os resultados da Serviço de Visão Personalizada do Azure, detecção de objetos, em um aplicativo de exemplo baseado em Unity.This course will teach you how to get the results from the Azure Custom Vision Service, Object Detection, into a Unity-based sample application. Será necessário aplicar esses conceitos a um aplicativo personalizado que você possa estar criando.It will be up to you to apply these concepts to a custom application you might be building.

Suporte a dispositivosDevice support

CourseCourse HoloLensHoloLens Headsets imersivosImmersive headsets
Sr e Azure 310: Detecção de objetoMR and Azure 310: Object detection ✔️✔️

Pré-requisitosPrerequisites

Observação

Este tutorial foi desenvolvido para desenvolvedores que têm experiência básica com o Unity C#e o.This tutorial is designed for developers who have basic experience with Unity and C#. Além disso, lembre-se de que os pré-requisitos e as instruções escritas neste documento representam o que foi testado e verificado no momento da gravação (julho de 2018).Please also be aware that the prerequisites and written instructions within this document represent what has been tested and verified at the time of writing (July 2018). Você está livre para usar o software mais recente, conforme listado no artigo instalar as ferramentas , embora não seja recomendável que as informações neste curso correspondam perfeitamente ao que você encontrará no software mais recente do que o que está listado abaixo.You are free to use the latest software, as listed within the install the tools article, though it should not be assumed that the information in this course will perfectly match what you will find in newer software than what is listed below.

Recomendamos o seguinte hardware e software para este curso:We recommend the following hardware and software for this course:

Antes de começarBefore you start

  1. Para evitar problemas de criação desse projeto, é altamente recomendável que você crie o projeto mencionado neste tutorial em uma pasta raiz ou quase raiz (caminhos de pasta longos podem causar problemas em tempo de compilação).To avoid encountering issues building this project, it is strongly suggested that you create the project mentioned in this tutorial in a root or near-root folder (long folder paths can cause issues at build-time).
  2. Configure e teste seu HoloLens.Set up and test your HoloLens. Se você precisar de suporte para configurar seu HoloLens, visite o artigo configuração do hololens.If you need support setting up your HoloLens, make sure to visit the HoloLens setup article.
  3. É uma boa ideia executar a calibragem e o ajuste do sensor ao começar a desenvolver um novo aplicativo do HoloLens (às vezes, ele pode ajudar a executar essas tarefas para cada usuário).It is a good idea to perform Calibration and Sensor Tuning when beginning developing a new HoloLens App (sometimes it can help to perform those tasks for each user).

Para obter ajuda sobre calibragem, siga este link para o artigo Calibration do HoloLens.For help on Calibration, please follow this link to the HoloLens Calibration article.

Para obter ajuda sobre o ajuste do sensor, siga este link para o artigo de ajuste do sensor do HoloLens.For help on Sensor Tuning, please follow this link to the HoloLens Sensor Tuning article.

Capítulo 1-o portal de Visão PersonalizadaChapter 1 - The Custom Vision Portal

Para usar o serviço de visão personalizada do Azure, você precisará configurar uma instância dele para ser disponibilizada para seu aplicativo.To use the Azure Custom Vision Service, you will need to configure an instance of it to be made available to your application.

  1. Navegue até a página principal do serviço de visão personalizada .Navigate to the Custom Vision Service main page.

  2. Clique em introdução.Click on Getting Started.

  3. Entre no portal de Visão Personalizada.Sign in to the Custom Vision Portal.

  4. Se você ainda não tiver uma conta do Azure, será necessário criar uma.If you do not already have an Azure account, you will need to create one. Se você estiver seguindo este tutorial em uma situação de sala de aula ou laboratório, peça ao instrutor ou a uma das proctors para obter ajuda para configurar sua nova conta.If you are following this tutorial in a classroom or lab situation, ask your instructor or one of the proctors for help setting up your new account.

  5. Depois de fazer logon pela primeira vez, você será solicitado com o painel termos de serviço .Once you are logged in for the first time, you will be prompted with the Terms of Service panel. Clique na caixa de seleção para concordar com os termos.Click the checkbox to agree to the terms. Em seguida, clique em concordo.Then click I agree.

  6. Tendo acordado os termos, agora você está na seção meus projetos .Having agreed to the terms, you are now in the My Projects section. Clique em novo projeto.Click on New Project.

  7. Uma guia será exibida no lado direito, o que solicitará que você especifique alguns campos para o projeto.A tab will appear on the right-hand side, which will prompt you to specify some fields for the project.

    1. Inserir um nome para seu projetoInsert a name for your project

    2. Inserir uma descrição para seu projeto (opcional)Insert a description for your project (Optional)

    3. Escolha um grupo de recursos ou crie um novo.Choose a Resource Group or create a new one. Um grupo de recursos fornece uma maneira de monitorar, controlar o acesso, provisionar e gerenciar a cobrança de uma coleção de ativos do Azure.A resource group provides a way to monitor, control access, provision and manage billing for a collection of Azure assets. É recomendável manter todos os serviços do Azure associados a um único projeto (por exemplo, esses cursos) em um grupo de recursos comum).It is recommended to keep all the Azure services associated with a single project (e.g. such as these courses) under a common resource group).

    4. Defina os tipos de projeto como detecção de objeto (versão prévia) .Set the Project Types as Object Detection (preview).

  8. Quando terminar, clique em criar projetoe você será redirecionado para a página do projeto serviço de visão personalizada.Once you are finished, click on Create project, and you will be redirected to the Custom Vision Service project page.

Capítulo 2-treinando seu Visão Personalizada projetoChapter 2 - Training your Custom Vision project

Uma vez no portal de Visão Personalizada, seu objetivo principal é treinar seu projeto para reconhecer objetos específicos em imagens.Once in the Custom Vision Portal, your primary objective is to train your project to recognize specific objects in images.

Você precisa de pelo menos quinze (15) imagens para cada objeto que você deseja que seu aplicativo reconheça.You need at least fifteen (15) images for each object that you would like your application to recognize. Você pode usar as imagens fornecidas com este curso (uma série de CUPS).You can use the images provided with this course (a series of cups).

Para treinar seu projeto de Visão Personalizada:To train your Custom Vision project:

  1. Clique no + botão ao lado de marcas.Click on the + button next to Tags.

  2. Adicione um nome para a marca que será usada para associar suas imagens ao.Add a name for the tag that will be used to associate your images with. Neste exemplo, estamos usando imagens de CUPS para reconhecimento e, portanto, nomeamos a marca para isso, Cup.In this example we are using images of cups for recognition, so have named the tag for this, Cup. Clique em salvar após concluir.Click Save once finished.

  3. Você notará que sua marca foi adicionada (talvez seja necessário recarregar sua página para que ela apareça).You will notice your Tag has been added (you may need to reload your page for it to appear).

  4. Clique em Adicionar imagens no centro da página.Click on Add images in the center of the page.

  5. Clique em procurar arquivos locaise navegue até as imagens que você deseja carregar para um objeto, com o mínimo de quinze (15).Click on Browse local files, and browse to the images you would like to upload for one object, with the minimum being fifteen (15).

    Dica

    Você pode selecionar várias imagens de cada vez para carregar.You can select several images at a time, to upload.

  6. Pressione carregar arquivos depois de selecionar todas as imagens com as quais você gostaria de treinar o projeto.Press Upload files once you have selected all the images you would like to train the project with. Os arquivos começarão a ser carregados.The files will begin uploading. Depois de confirmar o carregamento, clique em concluído.Once you have confirmation of the upload, click Done.

  7. Neste ponto, suas imagens são carregadas, mas não marcadas.At this point your images are uploaded, but not tagged.

  8. Para marcar suas imagens, use o mouse.To tag your images, use your mouse. À medida que você passa o mouse sobre a imagem, um realce de seleção o ajudará a desenhar automaticamente uma seleção em volta do objeto.As you hover over your image, a selection highlight will aid you by automatically drawing a selection around your object. Se não for preciso, você poderá desenhar o seu próprio.If it is not accurate, you can draw your own. Isso é feito mantendo o clique com o botão esquerdo do mouse e arrastando a região de seleção para abranger seu objeto.This is accomplished by holding left-click on the mouse, and dragging the selection region to encompass your object.

  9. Após a seleção do objeto na imagem, um pequeno prompt solicitará que você adicione a marca de região.Following the selection of your object within the image, a small prompt will ask for you to Add Region Tag. Selecione a marca criada anteriormente (' Cup ', no exemplo acima) ou, se você estiver adicionando mais marcas, digite-as em e clique no botão + (mais) .Select your previously created tag ('Cup', in the above example), or if you are adding more tags, type that in and click the + (plus) button.

  10. Para marcar a próxima imagem, você pode clicar na seta à direita da folha ou fechar a folha de marca (clicando no X no canto superior direito da folha) e, em seguida, na imagem seguinte.To tag the next image, you can click the arrow to the right of the blade, or close the tag blade (by clicking the X in the top-right corner of the blade) and then click the next image. Quando a próxima imagem estiver pronta, repita o mesmo procedimento.Once you have the next image ready, repeat the same procedure. Faça isso para todas as imagens que você carregou, até que todas estejam marcadas.Do this for all the images you have uploaded, until they are all tagged.

    Observação

    Você pode selecionar vários objetos na mesma imagem, como a imagem abaixo:You can select several objects in the same image, like the image below:

  11. Depois de ter marcado todos eles, clique no botão marcado , à esquerda da tela, para revelar as imagens marcadas.Once you have tagged them all, click on the tagged button, on the left of the screen, to reveal the tagged images.

  12. Agora você está pronto para treinar seu serviço.You are now ready to train your Service. Clique no botão treinar e a primeira iteração de treinamento será iniciada.Click the Train button, and the first training iteration will begin.

  13. Depois de compilado, você poderá ver dois botões chamados Make Default e predição URL.Once it is built, you will be able to see two buttons called Make default and Prediction URL. Clique em tornar padrão primeiro e, em seguida, clique em URL de previsão.Click on Make default first, then click on Prediction URL.

    Observação

    O ponto de extremidade fornecido por isso é definido para qualquer iteração marcada como padrão.The endpoint which is provided from this, is set to whichever Iteration has been marked as default. Dessa forma, se você fizer uma nova iteração e atualizá-la como padrão, não será necessário alterar seu código.As such, if you later make a new Iteration and update it as default, you will not need to change your code.

  14. Depois de clicar em URL de previsão, abra o bloco de notase copie e cole a URL (também chamada de ponto de extremidade de previsão) e a chave de previsão do serviçopara que você possa recuperá-la quando precisar mais tarde no código.Once you have clicked on Prediction URL, open Notepad, and copy and paste the URL (also called your Prediction-Endpoint) and the Service Prediction-Key, so that you can retrieve it when you need it later in the code.

Capítulo 3 – configurar o projeto do UnityChapter 3 - Set up the Unity project

A seguir está uma configuração típica para o desenvolvimento com realidade misturada e, como tal, é um bom modelo para outros projetos.The following is a typical set up for developing with mixed reality, and as such, is a good template for other projects.

  1. Abra o Unity e clique em novo.Open Unity and click New.

  2. Agora, você precisará fornecer um nome de projeto de Unity.You will now need to provide a Unity project name. Insira CustomVisionObjDetection.Insert CustomVisionObjDetection. Verifique se o tipo de projeto está definido como 3De defina o local como algum lugar apropriado para você (Lembre-se de que mais próximo aos diretórios raiz é melhor).Make sure the project type is set to 3D, and set the Location to somewhere appropriate for you (remember, closer to root directories is better). Em seguida, clique em criar projeto.Then, click Create project.

  3. Com o Unity Open, vale a pena verificar se o Editor de script padrão está definido como Visual Studio.With Unity open, it is worth checking the default Script Editor is set to Visual Studio. Vá para Editar > preferências e, em seguida, na janela novo, navegue até Ferramentas externas.Go to Edit > Preferences and then from the new window, navigate to External Tools. Altere o Editor de script externo para o Visual Studio.Change External Script Editor to Visual Studio. Feche a janela preferências .Close the Preferences window.

  4. Em seguida, vá para arquivo > configurações de compilação , alterne a plataforma para plataforma universal do Windowse, em seguida, clique no botão alternar plataforma .Next, go to File > Build Settings and switch the Platform to Universal Windows Platform, and then clicking on the Switch Platform button.

  5. Na mesma janela configurações de compilação , verifique se o seguinte está definido:In the same Build Settings window, ensure the following are set:

    1. O dispositivo de destino está definido como HoloLensTarget Device is set to HoloLens

    2. O tipo de compilação está definido como D3DBuild Type is set to D3D

    3. O SDK está definido para o mais recente instaladoSDK is set to Latest installed

    4. A versão do Visual Studio está definida para o mais recente instaladoVisual Studio Version is set to Latest installed

    5. Compilar e executar é definido como computador localBuild and Run is set to Local Machine

    6. As configurações restantes, em configurações de compilação, devem ser deixadas como padrão por enquanto.The remaining settings, in Build Settings, should be left as default for now.

  6. Na mesma janela configurações de compilação , clique no botão configurações do Player , isso abrirá o painel relacionado no espaço onde o Inspetor está localizado.In the same Build Settings window, click on the Player Settings button, this will open the related panel in the space where the Inspector is located.

  7. Nesse painel, algumas configurações precisam ser verificadas:In this panel, a few settings need to be verified:

    1. Na guia outras configurações :In the Other Settings tab:

      1. A versão de tempo de execução de script deve ser Experimental (.NET 4,6 equivalente), o que irá disparar uma necessidade de reiniciar o editor.Scripting Runtime Version should be Experimental (.NET 4.6 Equivalent), which will trigger a need to restart the Editor.

      2. O back-end de script deve ser .net.Scripting Backend should be .NET.

      3. O nível de compatibilidade da API deve ser .NET 4,6.API Compatibility Level should be .NET 4.6.

    2. Na guia configurações de publicação , em recursos, marque:Within the Publishing Settings tab, under Capabilities, check:

      1. InternetClientInternetClient

      2. IntegradaWebcam

      3. SpatialPerceptionSpatialPerception

    3. Mais adiante no painel, em configurações de XR (encontradas abaixo de configurações de publicação), suporte à realidade virtualde tique, em seguida, verifique se o SDK do Windows Mixed Reality foi adicionado.Further down the panel, in XR Settings (found below Publish Settings), tick Virtual Reality Supported, then make sure the Windows Mixed Reality SDK is added.

  8. De volta às configurações de compilação, # projetos Unity C não estão mais esmaecidos: marque a caixa de seleção ao lado disso.Back in Build Settings, Unity C# Projects is no longer greyed out: tick the checkbox next to this.

  9. Feche a janela configurações de compilação .Close the Build Settings window.

  10. No Editor, clique em Editar > configurações > do projetográficos.In the Editor, click on Edit > Project Settings > Graphics.

  11. No painel Inspetor , as configurações de gráficos serão abertas.In the Inspector Panel the Graphics Settings will be open. Role para baixo até ver uma matriz chamada sempre incluir sombreadores.Scroll down until you see an array called Always Include Shaders. Adicione um slot aumentando a variável de tamanho em um (neste exemplo, ele era 8, então fizemos 9).Add a slot by increasing the Size variable by one (in this example, it was 8 so we made it 9). Um novo slot será exibido na última posição da matriz, conforme mostrado abaixo:A new slot will appear, in the last position of the array, as shown below:

  12. No slot, clique no círculo de destino pequeno ao lado do slot para abrir uma lista de sombreadores.In the slot, click on the small target circle next to the slot to open a list of shaders. Procure por sombreadores herdados/sombreador transparente/difuso e clique duas vezes nele.Look for the Legacy Shaders/Transparent/Diffuse shader and double-click it.

Capítulo 4-importando o pacote do CustomVisionObjDetection UnityChapter 4 - Importing the CustomVisionObjDetection Unity package

Para este curso, você receberá um pacote de ativos de Unity chamado Azure-Mr-310. unitypackage.For this course you are provided with a Unity Asset Package called Azure-MR-310.unitypackage.

Dicas Todos os objetos com suporte do Unity, incluindo cenas inteiras, podem ser empacotados em um arquivo . unitypackage e exportados/importados em outros projetos.[TIP] Any objects supported by Unity, including entire scenes, can be packaged into a .unitypackage file, and exported / imported in other projects. É a maneira mais segura, e mais eficiente, de mover ativos entre diferentes projetos do Unity.It is the safest, and most efficient, way to move assets between different Unity projects.

Você pode encontrar o pacote Azure-Mr-310 que você precisa baixar aqui.You can find the Azure-MR-310 package that you need to download here.

  1. Com o painel do Unity na frente, clique em ativos no menu na parte superior da tela e, em seguida, clique em Importar pacote > pacote personalizado.With the Unity dashboard in front of you, click on Assets in the menu at the top of the screen, then click on Import Package > Custom Package.

  2. Use o seletor de arquivos para selecionar o pacote Azure-Mr-310. unitypackage e clique em abrir.Use the file picker to select the Azure-MR-310.unitypackage package and click Open. Uma lista de componentes para este ativo será exibida para você.A list of components for this asset will be displayed to you. Confirme a importação clicando no botão importar .Confirm the import by clicking the Import button.

  3. Depois de concluir a importação, você observará que as pastas do pacote agora foram adicionadas à sua pasta de ativos .Once it has finished importing, you will notice that folders from the package have now been added to your Assets folder. Esse tipo de estrutura de pastas é típico para um projeto do Unity.This kind of folder structure is typical for a Unity project.

    1. A pasta materiais contém o material usado pelo cursor olhar.The Materials folder contains the material used by the Gaze Cursor.

    2. A pasta plugins contém a dll Newtonsoft usada pelo código para desserializar a resposta da Web do serviço.The Plugins folder contains the Newtonsoft DLL used by the code to deserialize the Service web response. As duas (2) diferentes versões contidas na pasta e na subpasta são necessárias para permitir que a biblioteca seja usada e criada pelo editor de Unity e pela compilação UWP.The two (2) different versions contained in the folder, and sub-folder, are necessary to allow the library to be used and built by both the Unity Editor and the UWP build.

    3. A pasta pré-fabricados contém o pré-fabricados contido na cena.The Prefabs folder contains the prefabs contained in the scene. São eles:Those are:

      1. O GazeCursor, o cursor usado no aplicativo.The GazeCursor, the cursor used in the application. Trabalhará junto com o SpatialMapping pré-fabricado para poder ser colocado na cena sobre objetos físicos.Will work together with the SpatialMapping prefab to be able to be placed in the scene on top of physical objects.
      2. O rótulo, que é o objeto de interface do usuário usado para exibir a marca de objeto na cena quando necessário.The Label, which is the UI object used to display the object tag in the scene when required.
      3. O SpatialMapping, que é o objeto que permite que o aplicativo use criar um mapa virtual, usando o acompanhamento espacial do Microsoft HoloLens.The SpatialMapping, which is the object that enables the application to use create a virtual map, using the Microsoft HoloLens' spatial tracking.
    4. A pasta de cenas que atualmente contém a cena pré-criada para este curso.The Scenes folder which currently contains the pre-built scene for this course.

  4. Abra a pasta de cenas , no painel Projetoe clique duas vezes em ObjDetectionScenepara carregar a cena que será usada para este curso.Open the Scenes folder, in the Project Panel, and double-click on the ObjDetectionScene, to load the scene that you will use for this course.

    Observação

    Nenhum código está incluído, você escreverá o código seguindo este curso.No code is included, you will write the code by following this course.

Capítulo 5 – criar a classe CustomVisionAnalyser.Chapter 5 - Create the CustomVisionAnalyser class.

Neste ponto, você está pronto para escrever um código.At this point you are ready to write some code. Você começará com a classe CustomVisionAnalyser .You will begin with the CustomVisionAnalyser class.

Observação

As chamadas para o serviço de visão personalizada, feitas no código mostrado abaixo, são feitas usando a API REST do visão personalizada.The calls to the Custom Vision Service, made in the code shown below, are made using the Custom Vision REST API. Ao usar isso, você verá como implementar e usar essa API (útil para entender como implementar algo semelhante por conta própria).Through using this, you will see how to implement and make use of this API (useful for understanding how to implement something similar on your own). Lembre-se de que a Microsoft oferece um SDK Visão personalizada que também pode ser usado para fazer chamadas para o serviço.Be aware, that Microsoft offers a Custom Vision SDK that can also be used to make calls to the Service. Para obter mais informações, visite o artigo visão personalizada SDK.For more information visit the Custom Vision SDK article.

Essa classe é responsável por:This class is responsible for:

  • Carregando a imagem mais recente capturada como uma matriz de bytes.Loading the latest image captured as an array of bytes.

  • Enviando a matriz de bytes para a instância de serviço de visão personalizada do Azure para análise.Sending the byte array to your Azure Custom Vision Service instance for analysis.

  • Recebendo a resposta como uma cadeia de caracteres JSON.Receiving the response as a JSON string.

  • Desserializar a resposta e passar a previsão resultante para a classe SceneOrganiser , que cuidará de como a resposta deve ser exibida.Deserializing the response and passing the resulting Prediction to the SceneOrganiser class, which will take care of how the response should be displayed.

Para criar esta classe:To create this class:

  1. Clique com o botão direito do mouse na pasta ativo, localizada no painel Projeto, e clique em criar > pasta.Right-click in the Asset Folder, located in the Project Panel, then click Create > Folder. Chame os scriptsda pasta.Call the folder Scripts.

  2. Clique duas vezes na pasta recém-criada para abri-la.Double-click on the newly created folder, to open it.

  3. Clique com o botão direito do mouse dentro da pasta e clique em criar > script C# .Right-click inside the folder, then click Create > C# Script. Nomeie o script CustomVisionAnalyser.Name the script CustomVisionAnalyser.

  4. Clique duas vezes no novo script CustomVisionAnalyser para abri-lo com o Visual Studio.Double-click on the new CustomVisionAnalyser script to open it with Visual Studio.

  5. Verifique se você tem os seguintes namespaces referenciados na parte superior do arquivo:Make sure you have the following namespaces referenced at the top of the file:

    using Newtonsoft.Json;
    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    
  6. Na classe CustomVisionAnalyser , adicione as seguintes variáveis:In the CustomVisionAnalyser class, add the following variables:

        /// <summary>
        /// Unique instance of this class
        /// </summary>
        public static CustomVisionAnalyser Instance;
    
        /// <summary>
        /// Insert your prediction key here
        /// </summary>
        private string predictionKey = "- Insert your key here -";
    
        /// <summary>
        /// Insert your prediction endpoint here
        /// </summary>
        private string predictionEndpoint = "Insert your prediction endpoint here";
    
        /// <summary>
        /// Bite array of the image to submit for analysis
        /// </summary>
        [HideInInspector] public byte[] imageBytes;
    

    Observação

    Certifique-se de inserir sua chave de previsão de serviço na variável PredictionKey e seu ponto de extremidade de previsão na variável predictionEndpoint .Make sure you insert your Service Prediction-Key into the predictionKey variable and your Prediction-Endpoint into the predictionEndpoint variable. Você os copiou no bloco de notas anteriormente, no capítulo 2, etapa 14.You copied these to Notepad earlier, in Chapter 2, Step 14.

  7. O código para ativo () agora precisa ser adicionado para inicializar a variável de instância:Code for Awake() now needs to be added to initialize the Instance variable:

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Adicione a corrotina (com o método estático GetImageAsByteArray () abaixo dela), que obterá os resultados da análise da imagem, capturada pela classe ImageCapture .Add the coroutine (with the static GetImageAsByteArray() method below it), which will obtain the results of the analysis of the image, captured by the ImageCapture class.

    Observação

    Na AnalyseImageCapture corrotina, há uma chamada para a classe SceneOrganiser que você ainda deseja criar.In the AnalyseImageCapture coroutine, there is a call to the SceneOrganiser class that you are yet to create. Portanto, deixe essas linhas comentadas por enquanto.Therefore, leave those lines commented for now.

        /// <summary>
        /// Call the Computer Vision Service to submit the image.
        /// </summary>
        public IEnumerator AnalyseLastImageCaptured(string imagePath)
        {
            Debug.Log("Analyzing...");
    
            WWWForm webForm = new WWWForm();
    
            using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(predictionEndpoint, webForm))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);
    
                unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                unityWebRequest.SetRequestHeader("Prediction-Key", predictionKey);
    
                // The upload handler will help uploading the byte array with the request
                unityWebRequest.uploadHandler = new UploadHandlerRaw(imageBytes);
                unityWebRequest.uploadHandler.contentType = "application/octet-stream";
    
                // The download handler will help receiving the analysis from Azure
                unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return unityWebRequest.SendWebRequest();
    
                string jsonResponse = unityWebRequest.downloadHandler.text;
    
                Debug.Log("response: " + jsonResponse);
    
                // Create a texture. Texture size does not matter, since
                // LoadImage will replace with the incoming image size.
                //Texture2D tex = new Texture2D(1, 1);
                //tex.LoadImage(imageBytes);
                //SceneOrganiser.Instance.quadRenderer.material.SetTexture("_MainTex", tex);
    
                // The response will be in JSON format, therefore it needs to be deserialized
                //AnalysisRootObject analysisRootObject = new AnalysisRootObject();
                //analysisRootObject = JsonConvert.DeserializeObject<AnalysisRootObject>(jsonResponse);
    
                //SceneOrganiser.Instance.FinaliseLabel(analysisRootObject);
            }
        }
    
        /// <summary>
        /// Returns the contents of the specified image file as a byte array.
        /// </summary>
        static byte[] GetImageAsByteArray(string imageFilePath)
        {
            FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
    
            BinaryReader binaryReader = new BinaryReader(fileStream);
    
            return binaryReader.ReadBytes((int)fileStream.Length);
        }
    
  9. Exclua os métodos Start () e Update () , pois eles não serão usados.Delete the Start() and Update() methods, as they will not be used.

  10. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Importante

Como mencionado anteriormente, não se preocupe com o código que pode parecer ter um erro, pois você fornecerá mais classes em breve, o que corrigirá esses erros.As mentioned earlier, do not worry about code which might appear to have an error, as you will provide further classes soon, which will fix these.

Capítulo 6-criar a classe CustomVisionObjectsChapter 6 - Create the CustomVisionObjects class

A classe que você criará agora é a classe CustomVisionObjects .The class you will create now is the CustomVisionObjects class.

Esse script contém vários objetos usados por outras classes para serializar e desserializar as chamadas feitas ao Serviço de Visão Personalizada.This script contains a number of objects used by other classes to serialize and deserialize the calls made to the Custom Vision Service.

Para criar esta classe:To create this class:

  1. Clique com o botão direito do mouse na pasta scripts e clique em criar > script C# .Right-click inside the Scripts folder, then click Create > C# Script. Chame o script CustomVisionObjects.Call the script CustomVisionObjects.

  2. Clique duas vezes no novo script CustomVisionObjects para abri-lo com o Visual Studio.Double-click on the new CustomVisionObjects script to open it with Visual Studio.

  3. Verifique se você tem os seguintes namespaces referenciados na parte superior do arquivo:Make sure you have the following namespaces referenced at the top of the file:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Exclua os métodos Start () e Update () dentro da classe CustomVisionObjects , essa classe agora deve estar vazia.Delete the Start() and Update() methods inside the CustomVisionObjects class, this class should now be empty.

    Aviso

    É importante seguir atentamente a próxima instrução.It is important you follow the next instruction carefully. Se você colocar as novas declarações de classe dentro da classe CustomVisionObjects , receberá erros de compilação no capítulo 10, informando que AnalysisRootObject e BoundingBox não foram encontrados.If you put the new class declarations inside the CustomVisionObjects class, you will get compile errors in chapter 10, stating that AnalysisRootObject and BoundingBox are not found.

  5. Adicione as seguintes classes fora da classe CustomVisionObjects .Add the following classes outside the CustomVisionObjects class. Esses objetos são usados pela biblioteca Newtonsoft para serializar e desserializar os dados de resposta:These objects are used by the Newtonsoft library to serialize and deserialize the response data:

    // The objects contained in this script represent the deserialized version
    // of the objects used by this application 
    
    /// <summary>
    /// Web request object for image data
    /// </summary>
    class MultipartObject : IMultipartFormSection
    {
        public string sectionName { get; set; }
    
        public byte[] sectionData { get; set; }
    
        public string fileName { get; set; }
    
        public string contentType { get; set; }
    }
    
    /// <summary>
    /// JSON of all Tags existing within the project
    /// contains the list of Tags
    /// </summary> 
    public class Tags_RootObject
    {
        public List<TagOfProject> Tags { get; set; }
        public int TotalTaggedImages { get; set; }
        public int TotalUntaggedImages { get; set; }
    }
    
    public class TagOfProject
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public int ImageCount { get; set; }
    }
    
    /// <summary>
    /// JSON of Tag to associate to an image
    /// Contains a list of hosting the tags,
    /// since multiple tags can be associated with one image
    /// </summary> 
    public class Tag_RootObject
    {
        public List<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
        public string ImageId { get; set; }
        public string TagId { get; set; }
    }
    
    /// <summary>
    /// JSON of images submitted
    /// Contains objects that host detailed information about one or more images
    /// </summary> 
    public class ImageRootObject
    {
        public bool IsBatchSuccessful { get; set; }
        public List<SubmittedImage> Images { get; set; }
    }
    
    public class SubmittedImage
    {
        public string SourceUrl { get; set; }
        public string Status { get; set; }
        public ImageObject Image { get; set; }
    }
    
    public class ImageObject
    {
        public string Id { get; set; }
        public DateTime Created { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public string ImageUri { get; set; }
        public string ThumbnailUri { get; set; }
    }
    
    /// <summary>
    /// JSON of Service Iteration
    /// </summary> 
    public class Iteration
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsDefault { get; set; }
        public string Status { get; set; }
        public string Created { get; set; }
        public string LastModified { get; set; }
        public string TrainedAt { get; set; }
        public string ProjectId { get; set; }
        public bool Exportable { get; set; }
        public string DomainId { get; set; }
    }
    
    /// <summary>
    /// Predictions received by the Service
    /// after submitting an image for analysis
    /// Includes Bounding Box
    /// </summary>
    public class AnalysisRootObject
    {
        public string id { get; set; }
        public string project { get; set; }
        public string iteration { get; set; }
        public DateTime created { get; set; }
        public List<Prediction> predictions { get; set; }
    }
    
    public class BoundingBox
    {
        public double left { get; set; }
        public double top { get; set; }
        public double width { get; set; }
        public double height { get; set; }
    }
    
    public class Prediction
    {
        public double probability { get; set; }
        public string tagId { get; set; }
        public string tagName { get; set; }
        public BoundingBox boundingBox { get; set; }
    }
    
  6. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Capítulo 7-criar a classe SpatialMappingChapter 7 - Create the SpatialMapping class

Essa classe definirá o conflito de mapeamento espacial na cena para que seja possível detectar colisões entre objetos virtuais e objetos reais.This class will set the Spatial Mapping Collider in the scene so to be able to detect collisions between virtual objects and real objects.

Para criar esta classe:To create this class:

  1. Clique com o botão direito do mouse na pasta scripts e clique em criar > script C# .Right-click inside the Scripts folder, then click Create > C# Script. Chame o script SpatialMapping.Call the script SpatialMapping.

  2. Clique duas vezes no novo script SpatialMapping para abri-lo com o Visual Studio.Double-click on the new SpatialMapping script to open it with Visual Studio.

  3. Verifique se você tem os seguintes namespaces referenciados acima da classe SpatialMapping :Make sure you have the following namespaces referenced above the SpatialMapping class:

    using UnityEngine;
    using UnityEngine.XR.WSA;
    
  4. Em seguida, adicione as seguintes variáveis dentro da classe SpatialMapping , acima do método Start () :Then, add the following variables inside the SpatialMapping class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SpatialMapping Instance;
    
        /// <summary>
        /// Used by the GazeCursor as a property with the Raycast call
        /// </summary>
        internal static int PhysicsRaycastMask;
    
        /// <summary>
        /// The layer to use for spatial mapping collisions
        /// </summary>
        internal int physicsLayer = 31;
    
        /// <summary>
        /// Creates environment colliders to work with physics
        /// </summary>
        private SpatialMappingCollider spatialMappingCollider;
    
  5. Adicione o ativo () e o início () :Add the Awake() and Start():

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Initialize and configure the collider
            spatialMappingCollider = gameObject.GetComponent<SpatialMappingCollider>();
            spatialMappingCollider.surfaceParent = this.gameObject;
            spatialMappingCollider.freezeUpdates = false;
            spatialMappingCollider.layer = physicsLayer;
    
            // define the mask
            PhysicsRaycastMask = 1 << physicsLayer;
    
            // set the object as active one
            gameObject.SetActive(true);
        }
    
  6. Exclua o método Update () .Delete the Update() method.

  7. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Capítulo 8-criar a classe GazeCursorChapter 8 - Create the GazeCursor class

Essa classe é responsável por configurar o cursor no local correto em espaço real, fazendo uso do SpatialMappingCollider, criado no capítulo anterior.This class is responsible for setting up the cursor in the correct location in real space, by making use of the SpatialMappingCollider, created in the previous chapter.

Para criar esta classe:To create this class:

  1. Clique com o botão direito do mouse na pasta scripts e clique em criar > script C# .Right-click inside the Scripts folder, then click Create > C# Script. Chamar o script GazeCursorCall the script GazeCursor

  2. Clique duas vezes no novo script GazeCursor para abri-lo com o Visual Studio.Double-click on the new GazeCursor script to open it with Visual Studio.

  3. Verifique se você tem o seguinte namespace mencionado acima da classe GazeCursor :Make sure you have the following namespace referenced above the GazeCursor class:

    using UnityEngine;
    
  4. Em seguida, adicione a seguinte variável dentro da classe GazeCursor , acima do método Start () .Then add the following variable inside the GazeCursor class, above the Start() method.

        /// <summary>
        /// The cursor (this object) mesh renderer
        /// </summary>
        private MeshRenderer meshRenderer;
    
  5. Atualize o método Start () com o seguinte código:Update the Start() method with the following code:

        /// <summary>
        /// Runs at initialization right after the Awake method
        /// </summary>
        void Start()
        {
            // Grab the mesh renderer that is on the same object as this script.
            meshRenderer = gameObject.GetComponent<MeshRenderer>();
    
            // Set the cursor reference
            SceneOrganiser.Instance.cursor = gameObject;
            gameObject.GetComponent<Renderer>().material.color = Color.green;
    
            // If you wish to change the size of the cursor you can do so here
            gameObject.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
        }
    
  6. Atualize o método Update () com o seguinte código:Update the Update() method with the following code:

        /// <summary>
        /// Update is called once per frame
        /// </summary>
        void Update()
        {
            // Do a raycast into the world based on the user's head position and orientation.
            Vector3 headPosition = Camera.main.transform.position;
            Vector3 gazeDirection = Camera.main.transform.forward;
    
            RaycastHit gazeHitInfo;
            if (Physics.Raycast(headPosition, gazeDirection, out gazeHitInfo, 30.0f, SpatialMapping.PhysicsRaycastMask))
            {
                // If the raycast hit a hologram, display the cursor mesh.
                meshRenderer.enabled = true;
                // Move the cursor to the point where the raycast hit.
                transform.position = gazeHitInfo.point;
                // Rotate the cursor to hug the surface of the hologram.
                transform.rotation = Quaternion.FromToRotation(Vector3.up, gazeHitInfo.normal);
            }
            else
            {
                // If the raycast did not hit a hologram, hide the cursor mesh.
                meshRenderer.enabled = false;
            }
        }
    

    Observação

    Não se preocupe com o erro para a classe SceneOrganiser não ser encontrada, você a criará no próximo capítulo.Do not worry about the error for the SceneOrganiser class not being found, you will create it in the next chapter.

  7. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Capítulo 9-criar a classe SceneOrganiserChapter 9 - Create the SceneOrganiser class

Essa classe irá:This class will:

  • Configure a câmera principal anexando os componentes apropriados a ela.Set up the Main Camera by attaching the appropriate components to it.

  • Quando um objeto é detectado, ele será responsável por calcular sua posição no mundo real e colocar um rótulo de marca próximo a ele com o nome de marcaapropriado.When an object is detected, it will be responsible for calculating its position in the real world and place a Tag Label near it with the appropriate Tag Name.

Para criar esta classe:To create this class:

  1. Clique com o botão direito do mouse na pasta scripts e clique em criar > script C# .Right-click inside the Scripts folder, then click Create > C# Script. Nomeie o script SceneOrganiser.Name the script SceneOrganiser.

  2. Clique duas vezes no novo script SceneOrganiser para abri-lo com o Visual Studio.Double-click on the new SceneOrganiser script to open it with Visual Studio.

  3. Verifique se você tem os seguintes namespaces referenciados acima da classe SceneOrganiser :Make sure you have the following namespaces referenced above the SceneOrganiser class:

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
  4. Em seguida, adicione as seguintes variáveis dentro da classe SceneOrganiser , acima do método Start () :Then add the following variables inside the SceneOrganiser class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SceneOrganiser Instance;
    
        /// <summary>
        /// The cursor object attached to the Main Camera
        /// </summary>
        internal GameObject cursor;
    
        /// <summary>
        /// The label used to display the analysis on the objects in the real world
        /// </summary>
        public GameObject label;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal Transform lastLabelPlaced;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal TextMesh lastLabelPlacedText;
    
        /// <summary>
        /// Current threshold accepted for displaying the label
        /// Reduce this value to display the recognition more often
        /// </summary>
        internal float probabilityThreshold = 0.8f;
    
        /// <summary>
        /// The quad object hosting the imposed image captured
        /// </summary>
        private GameObject quad;
    
        /// <summary>
        /// Renderer of the quad object
        /// </summary>
        internal Renderer quadRenderer;
    
  5. Exclua os métodos Start () e Update () .Delete the Start() and Update() methods.

  6. Sob as variáveis, adicione o método ativo () , que irá inicializar a classe e configurar a cena.Underneath the variables, add the Awake() method, which will initialize the class and set up the scene.

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            // Use this class instance as singleton
            Instance = this;
    
            // Add the ImageCapture class to this Gameobject
            gameObject.AddComponent<ImageCapture>();
    
            // Add the CustomVisionAnalyser class to this Gameobject
            gameObject.AddComponent<CustomVisionAnalyser>();
    
            // Add the CustomVisionObjects class to this Gameobject
            gameObject.AddComponent<CustomVisionObjects>();
        }
    
  7. Adicione o método PlaceAnalysisLabel () , que criará uma instância do rótulo na cena (que, neste ponto, é invisível para o usuário).Add the PlaceAnalysisLabel() method, which will Instantiate the label in the scene (which at this point is invisible to the user). Ele também coloca o quad (também invisível) em que a imagem é colocada e se sobrepõe ao mundo real.It also places the quad (also invisible) where the image is placed, and overlaps with the real world. Isso é importante porque as coordenadas de caixa recuperadas do serviço após a análise são rastreadas de volta para esse Quad para determinar o local aproximado do objeto no mundo real.This is important because the box coordinates retrieved from the Service after analysis are traced back into this quad to determined the approximate location of the object in the real world.

        /// <summary>
        /// Instantiate a Label in the appropriate location relative to the Main Camera.
        /// </summary>
        public void PlaceAnalysisLabel()
        {
            lastLabelPlaced = Instantiate(label.transform, cursor.transform.position, transform.rotation);
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
            lastLabelPlacedText.text = "";
            lastLabelPlaced.transform.localScale = new Vector3(0.005f,0.005f,0.005f);
    
            // Create a GameObject to which the texture can be applied
            quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
            quadRenderer = quad.GetComponent<Renderer>() as Renderer;
            Material m = new Material(Shader.Find("Legacy Shaders/Transparent/Diffuse"));
            quadRenderer.material = m;
    
            // Here you can set the transparency of the quad. Useful for debugging
            float transparency = 0f;
            quadRenderer.material.color = new Color(1, 1, 1, transparency);
    
            // Set the position and scale of the quad depending on user position
            quad.transform.parent = transform;
            quad.transform.rotation = transform.rotation;
    
            // The quad is positioned slightly forward in font of the user
            quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
    
            // The quad scale as been set with the following value following experimentation,  
            // to allow the image on the quad to be as precisely imposed to the real world as possible
            quad.transform.localScale = new Vector3(3f, 1.65f, 1f);
            quad.transform.parent = null;
        }
    
  8. Adicione o método FinaliseLabel () .Add the FinaliseLabel() method. É responsável por:It is responsible for:

    • Definindo o texto do rótulo com a marca da previsão com a maior confiança.Setting the Label text with the Tag of the Prediction with the highest confidence.
    • Chamar o cálculo da caixa delimitadora no objeto Quad, posicionado anteriormente e colocar o rótulo na cena.Calling the calculation of the Bounding Box on the quad object, positioned previously, and placing the label in the scene.
    • Ajuste da profundidade do rótulo usando um Raycast em direção à caixadelimitadora, que deve colidir com o objeto no mundo real.Adjusting the label depth by using a Raycast towards the Bounding Box, which should collide against the object in the real world.
    • Redefinição do processo de captura para permitir que o usuário Capture outra imagem.Resetting the capture process to allow the user to capture another image.
        /// <summary>
        /// Set the Tags as Text of the last label created. 
        /// </summary>
        public void FinaliseLabel(AnalysisRootObject analysisObject)
        {
            if (analysisObject.predictions != null)
            {
                lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
                // Sort the predictions to locate the highest one
                List<Prediction> sortedPredictions = new List<Prediction>();
                sortedPredictions = analysisObject.predictions.OrderBy(p => p.probability).ToList();
                Prediction bestPrediction = new Prediction();
                bestPrediction = sortedPredictions[sortedPredictions.Count - 1];
    
                if (bestPrediction.probability > probabilityThreshold)
                {
                    quadRenderer = quad.GetComponent<Renderer>() as Renderer;
                    Bounds quadBounds = quadRenderer.bounds;
    
                    // Position the label as close as possible to the Bounding Box of the prediction 
                    // At this point it will not consider depth
                    lastLabelPlaced.transform.parent = quad.transform;
                    lastLabelPlaced.transform.localPosition = CalculateBoundingBoxPosition(quadBounds, bestPrediction.boundingBox);
    
                    // Set the tag text
                    lastLabelPlacedText.text = bestPrediction.tagName;
    
                    // Cast a ray from the user's head to the currently placed label, it should hit the object detected by the Service.
                    // At that point it will reposition the label where the ray HL sensor collides with the object,
                    // (using the HL spatial tracking)
                    Debug.Log("Repositioning Label");
                    Vector3 headPosition = Camera.main.transform.position;
                    RaycastHit objHitInfo;
                    Vector3 objDirection = lastLabelPlaced.position;
                    if (Physics.Raycast(headPosition, objDirection, out objHitInfo, 30.0f,   SpatialMapping.PhysicsRaycastMask))
                    {
                        lastLabelPlaced.position = objHitInfo.point;
                    }
                }
            }
            // Reset the color of the cursor
            cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the analysis process
            ImageCapture.Instance.ResetImageCapture();        
        }
    
  9. Adicione o método CalculateBoundingBoxPosition () , que hospeda vários cálculos necessários para converter as coordenadas da caixa delimitadora recuperadas do serviço e recriá-las proporcionalmente no Quad.Add the CalculateBoundingBoxPosition() method, which hosts a number of calculations necessary to translate the Bounding Box coordinates retrieved from the Service and recreate them proportionally on the quad.

        /// <summary>
        /// This method hosts a series of calculations to determine the position 
        /// of the Bounding Box on the quad created in the real world
        /// by using the Bounding Box received back alongside the Best Prediction
        /// </summary>
        public Vector3 CalculateBoundingBoxPosition(Bounds b, BoundingBox boundingBox)
        {
            Debug.Log($"BB: left {boundingBox.left}, top {boundingBox.top}, width {boundingBox.width}, height {boundingBox.height}");
    
            double centerFromLeft = boundingBox.left + (boundingBox.width / 2);
            double centerFromTop = boundingBox.top + (boundingBox.height / 2);
            Debug.Log($"BB CenterFromLeft {centerFromLeft}, CenterFromTop {centerFromTop}");
    
            double quadWidth = b.size.normalized.x;
            double quadHeight = b.size.normalized.y;
            Debug.Log($"Quad Width {b.size.normalized.x}, Quad Height {b.size.normalized.y}");
    
            double normalisedPos_X = (quadWidth * centerFromLeft) - (quadWidth/2);
            double normalisedPos_Y = (quadHeight * centerFromTop) - (quadHeight/2);
    
            return new Vector3((float)normalisedPos_X, (float)normalisedPos_Y, 0);
        }
    
  10. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

    Importante

    Antes de continuar, abra a classe CustomVisionAnalyser e, dentro do método AnalyseLastImageCaptured () , remova os comentários das seguintes linhas:Before you continue, open the CustomVisionAnalyser class, and within the AnalyseLastImageCaptured() method, uncomment the following lines:

    // Create a texture. Texture size does not matter, since 
    // LoadImage will replace with the incoming image size.
    Texture2D tex = new Texture2D(1, 1);
    tex.LoadImage(imageBytes);
    SceneOrganiser.Instance.quadRenderer.material.SetTexture("_MainTex", tex);
    
    // The response will be in JSON format, therefore it needs to be deserialized
    AnalysisRootObject analysisRootObject = new AnalysisRootObject();
    analysisRootObject = JsonConvert.DeserializeObject<AnalysisRootObject>(jsonResponse);
    
    SceneOrganiser.Instance.FinaliseLabel(analysisRootObject);
    

Observação

Não se preocupe com a mensagem "não foi possível encontrar a classe ImageCapture ", você a criará no próximo capítulo.Do not worry about the ImageCapture class 'could not be found' message, you will create it in the next chapter.

Capítulo 10 – criar a classe ImageCaptureChapter 10 - Create the ImageCapture class

A próxima classe que você vai criar é a classe ImageCapture .The next class you are going to create is the ImageCapture class.

Essa classe é responsável por:This class is responsible for:

  • Capturando uma imagem usando a câmera do HoloLens e armazenando-a na pasta do aplicativo .Capturing an image using the HoloLens camera and storing it in the App folder.
  • Lidando com gestos de toque do usuário.Handling Tap gestures from the user.

Para criar esta classe:To create this class:

  1. Vá para a pasta scripts que você criou anteriormente.Go to the Scripts folder you created previously.

  2. Clique com o botão direito do mouse dentro da pasta e clique em criar > script C# .Right-click inside the folder, then click Create > C# Script. Nomeie o script ImageCapture.Name the script ImageCapture.

  3. Clique duas vezes no novo script ImageCapture para abri-lo com o Visual Studio.Double-click on the new ImageCapture script to open it with Visual Studio.

  4. Substitua os namespaces na parte superior do arquivo pelo seguinte:Replace the namespaces at the top of the file with the following:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Em seguida, adicione as seguintes variáveis dentro da classe ImageCapture , acima do método Start () :Then add the following variables inside the ImageCapture class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture Instance;
    
        /// <summary>
        /// Keep counts of the taps for image renaming
        /// </summary>
        private int captureCount = 0;
    
        /// <summary>
        /// Photo Capture object
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// Allows gestures recognition in HoloLens
        /// </summary>
        private GestureRecognizer recognizer;
    
        /// <summary>
        /// Flagging if the capture loop is running
        /// </summary>
        internal bool captureIsActive;
    
        /// <summary>
        /// File path of current analysed photo
        /// </summary>
        internal string filePath = string.Empty;
    
  6. O código para os métodos ativo () e Iniciar () agora precisa ser adicionado:Code for Awake() and Start() methods now needs to be added:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Clean up the LocalState folder of this application from all photos stored
            DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath);
            var fileInfo = info.GetFiles();
            foreach (var file in fileInfo)
            {
                try
                {
                    file.Delete();
                }
                catch (Exception)
                {
                    Debug.LogFormat("Cannot delete file: ", file.Name);
                }
            } 
    
            // Subscribing to the Microsoft HoloLens API gesture recognizer to track user gestures
            recognizer = new GestureRecognizer();
            recognizer.SetRecognizableGestures(GestureSettings.Tap);
            recognizer.Tapped += TapHandler;
            recognizer.StartCapturingGestures();
        }
    
  7. Implemente um manipulador que será chamado quando ocorrer um gesto de toque:Implement a handler that will be called when a Tap gesture occurs:

        /// <summary>
        /// Respond to Tap Input.
        /// </summary>
        private void TapHandler(TappedEventArgs obj)
        {
            if (!captureIsActive)
            {
                captureIsActive = true;
    
                // Set the cursor color to red
                SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red;
    
                // Begin the capture loop
                Invoke("ExecuteImageCaptureAndAnalysis", 0);
            }
        }
    

    Importante

    Quando o cursor estiver verde, isso significa que a câmera está disponível para tirar a imagem.When the cursor is green, it means the camera is available to take the image. Quando o cursor estiver vermelho, significa que a câmera está ocupada.When the cursor is red, it means the camera is busy.

  8. Adicione o método que o aplicativo usa para iniciar o processo de captura de imagem e armazenar a imagem:Add the method that the application uses to start the image capture process and store the image:

        /// <summary>
        /// Begin process of image capturing and send to Azure Custom Vision Service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            // Create a label in world space using the ResultsLabel class 
            // Invisible at this point but correctly positioned where the image was taken
            SceneOrganiser.Instance.PlaceAnalysisLabel();
    
            // Set the camera resolution to be the highest possible
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending
                ((res) => res.width * res.height).First();
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            // Begin capture process, set the image format
            PhotoCapture.CreateAsync(true, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters camParameters = new CameraParameters
                {
                    hologramOpacity = 1.0f,
                    cameraResolutionWidth = targetTexture.width,
                    cameraResolutionHeight = targetTexture.height,
                    pixelFormat = CapturePixelFormat.BGRA32
                };
    
                // Capture the image from the camera and save it in the App internal folder
                captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", captureCount);
                    filePath = Path.Combine(Application.persistentDataPath, filename);          
                    captureCount++;              
                    photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);              
                });
            });
        }
    
  9. Adicione os manipuladores que serão chamados quando a foto tiver sido capturada e quando ela estiver pronta para ser analisada.Add the handlers that will be called when the photo has been captured and for when it is ready to be analyzed. O resultado é passado para o CustomVisionAnalyser para análise.The result is then passed to the CustomVisionAnalyser for analysis.

        /// <summary>
        /// Register the full execution of the Photo Capture. 
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
            try
            {
                // Call StopPhotoMode once the image has successfully captured
                photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
            }
            catch (Exception e)
            {
                Debug.LogFormat("Exception capturing photo to disk: {0}", e.Message);
            }
        }
    
        /// <summary>
        /// The camera photo mode has stopped after the capture.
        /// Begin the image analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            Debug.LogFormat("Stopped Photo Mode");
    
            // Dispose from the object in memory and request the image analysis 
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            // Call the image analysis
            StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath)); 
        }
    
        /// <summary>
        /// Stops all capture pending actions
        /// </summary>
        internal void ResetImageCapture()
        {
            captureIsActive = false;
    
            // Set the cursor color to green
            SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the capture loop if active
            CancelInvoke();
        }
    
  10. Certifique-se de salvar as alterações no Visual Studioantes de retornar ao Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Capítulo 11-Configurando os scripts na cenaChapter 11 - Setting up the scripts in the scene

Agora que você escreveu todo o código necessário para esse projeto, é hora de configurar os scripts na cena e, no pré-fabricados, para que eles se comportem corretamente.Now that you have written all of the code necessary for this project, is time to set up the scripts in the scene, and on the prefabs, for them to behave correctly.

  1. No Editor do Unity, no painel hierarquia, selecione a câmera principal.Within the Unity Editor, in the Hierarchy Panel, select the Main Camera.

  2. No painel Inspetor, com a câmera principal selecionada, clique em Adicionar componente, em seguida, procure o script SceneOrganiser e clique duas vezes para adicioná-lo.In the Inspector Panel, with the Main Camera selected, click on Add Component, then search for SceneOrganiser script and double-click, to add it.

  3. No painel Projeto, abra a pasta pré-fabricados, arraste o rótulo pré-fabricado para a área de entrada destino de referência vazia do rótulo , no script SceneOrganiser que você acabou de adicionar à câmera principal, conforme mostrado na a imagem abaixo:In the Project Panel, open the Prefabs folder, drag the Label prefab into the Label empty reference target input area, in the SceneOrganiser script that you have just added to the Main Camera, as shown in the image below:

  4. No painel hierarquia, selecione o filho GazeCursor da câmera principal.In the Hierarchy Panel, select the GazeCursor child of the Main Camera.

  5. No painel Inspetor, com o GazeCursor selecionado, clique em Adicionar componente, em seguida, pesquise por GazeCursor script e clique duas vezes para adicioná-lo.In the Inspector Panel, with the GazeCursor selected, click on Add Component, then search for GazeCursor script and double-click, to add it.

  6. Novamente, no painel hierarquia, selecione o filho SpatialMapping da câmera principal.Again, in the Hierarchy Panel, select the SpatialMapping child of the Main Camera.

  7. No painel Inspetor, com o SpatialMapping selecionado, clique em Adicionar componente, em seguida, pesquise por SpatialMapping script e clique duas vezes para adicioná-lo.In the Inspector Panel, with the SpatialMapping selected, click on Add Component, then search for SpatialMapping script and double-click, to add it.

Os scripts restantes que você não tiver definido serão adicionados pelo código no script SceneOrganiser , durante o tempo de execução.The remaining scripts thats you have not set will be added by the code in the SceneOrganiser script, during runtime.

Capítulo 12 – antes de CompilarChapter 12 - Before building

Para executar um teste completo de seu aplicativo, você precisará Sideload-lo no Microsoft HoloLens.To perform a thorough test of your application you will need to sideload it onto your Microsoft HoloLens.

Antes de fazer isso, verifique se:Before you do, ensure that:

  • Todas as configurações mencionadas no capítulo 3 estão definidas corretamente.All the settings mentioned in the Chapter 3 are set correctly.

  • O script SceneOrganiser é anexado ao objeto da câmera principal .The script SceneOrganiser is attached to the Main Camera object.

  • O script GazeCursor é anexado ao objeto GazeCursor .The script GazeCursor is attached to the GazeCursor object.

  • O script SpatialMapping é anexado ao objeto SpatialMapping .The script SpatialMapping is attached to the SpatialMapping object.

  • No capítulo 5, etapa 6:In Chapter 5, Step 6:

    • Certifique-se de inserir sua chave de previsão de serviço na variável predictionKey .Make sure you insert your Service Prediction Key into the predictionKey variable.
    • Você inseriu o ponto de extremidade de previsão na classe predictionEndpoint .You have inserted your Prediction Endpoint into the predictionEndpoint class.

Capítulo 13-criar a solução UWP e Sideload seu aplicativoChapter 13 - Build the UWP solution and sideload your application

Agora você está pronto para compilar seu aplicativo como uma solução UWP que você poderá implantar no Microsoft HoloLens.You are now ready to build you application as a UWP Solution that you will be able to deploy on to the Microsoft HoloLens. Para iniciar o processo de compilação:To begin the build process:

  1. Vá para arquivo > configurações de Build.Go to File > Build Settings.

  2. Projetos em tique# Unity C.Tick Unity C# Projects.

  3. Clique em Adicionar cenas de abertura.Click on Add Open Scenes. Isso adicionará a cena aberta no momento à compilação.This will add the currently open scene to the build.

  4. Clique em Compilar.Click Build. O Unity iniciará uma janela Explorador de arquivos , onde você precisará criar e, em seguida, selecionar uma pasta na qual o aplicativo será compilado.Unity will launch a File Explorer window, where you need to create and then select a folder to build the app into. Crie essa pasta agora e nomeie-a como aplicativo.Create that folder now, and name it App. Em seguida, com a pasta de aplicativo selecionada, clique em Selecionar pasta.Then with the App folder selected, click Select Folder.

  5. O Unity começará a criar seu projeto na pasta do aplicativo .Unity will begin building your project to the App folder.

  6. Depois que o Unity terminar a compilação (pode levar algum tempo), ele abrirá uma janela do Explorador de arquivos no local de sua compilação (verifique sua barra de tarefas, pois ela nem sempre aparecerá acima das janelas, mas o notificará sobre a adição de uma nova janela).Once Unity has finished building (it might take some time), it will open a File Explorer window at the location of your build (check your task bar, as it may not always appear above your windows, but will notify you of the addition of a new window).

  7. Para implantar o no Microsoft HoloLens, você precisará do endereço IP desse dispositivo (para implantação remota) e para garantir que ele também tenha o modo de desenvolvedor definido.To deploy on to Microsoft HoloLens, you will need the IP Address of that device (for Remote Deploy), and to ensure that it also has Developer Mode set. Para fazer isso:To do this:

    1. Enquanto estiver desgastando seu HoloLens, abra as configurações.Whilst wearing your HoloLens, open the Settings.

    2. Vá para rede & > Opções avançadas de Internet Wi-Fi > Go to Network & Internet > Wi-Fi > Advanced Options

    3. Anote o endereço IPv4 .Note the IPv4 address.

    4. Em seguida, navegue de volta para configuraçõese, em seguida, para Atualizar & segurança > para desenvolvedoresNext, navigate back to Settings, and then to Update & Security > For Developers

    5. Defina o modo de desenvolvedor em.Set Developer Mode On.

  8. Navegue até sua nova compilação do Unity (a pasta do aplicativo ) e abra o arquivo de solução com o Visual Studio.Navigate to your new Unity build (the App folder) and open the solution file with Visual Studio.

  9. Na configuração da solução, selecione depurar.In the Solution Configuration select Debug.

  10. Na plataforma da solução, selecione x86, computador remoto.In the Solution Platform, select x86, Remote Machine. Você será solicitado a inserir o endereço IP de um dispositivo remoto (o Microsoft HoloLens, neste caso, que você anotou).You will be prompted to insert the IP address of a remote device (the Microsoft HoloLens, in this case, which you noted).

  11. Vá para o menu Compilar e clique em implantar solução para Sideload o aplicativo em seu HoloLens.Go to the Build menu and click on Deploy Solution to sideload the application to your HoloLens.

  12. Seu aplicativo agora deve aparecer na lista de aplicativos instalados no Microsoft HoloLens, pronto para ser iniciado!Your app should now appear in the list of installed apps on your Microsoft HoloLens, ready to be launched!

Para usar o aplicativo:To use the application:

  • Examine um objeto, que você tem treinado com sua serviço de visão personalizada do Azure, detecção de objetose use o gesto de toque.Look at an object, which you have trained with your Azure Custom Vision Service, Object Detection, and use the Tap gesture.
  • Se o objeto for detectado com êxito, um texto de rótulo de espaço mundial será exibido com o nome da marca.If the object is successfully detected, a world-space Label Text will appear with the tag name.

Importante

Sempre que você captura uma foto e a envia para o serviço, pode voltar para a página de serviço e treinar novamente o serviço com as imagens recentemente capturadas.Every time you capture a photo and send it to the Service, you can go back to the Service page and retrain the Service with the newly captured images. No início, você provavelmente também precisará corrigir as caixas delimitadores para ser mais preciso e treinar novamente o serviço.At the beginning, you will probably also have to correct the Bounding Boxes to be more accurate and retrain the Service.

Observação

O texto do rótulo colocado pode não aparecer próximo ao objeto quando os sensores do Microsoft HoloLens e/ou o SpatialTrackingComponent no Unity falharem em colocar os colisor apropriados, em relação aos objetos do mundo real.The Label Text placed might not appear near the object when the Microsoft HoloLens sensors and/or the SpatialTrackingComponent in Unity fails to place the appropriate colliders, relative to the real world objects. Tente usar o aplicativo em uma superfície diferente, se esse for o caso.Try to use the application on a different surface if that is the case.

Seu Visão Personalizada, aplicativo de detecção de objetoYour Custom Vision, Object Detection application

Parabéns, você criou um aplicativo de realidade misturada que aproveita o Visão Personalizada do Azure, a API de detecção de objeto, que pode reconhecer um objeto de uma imagem e, em seguida, fornecer uma posição aproximada para esse objeto no espaço 3D.Congratulations, you built a mixed reality app that leverages the Azure Custom Vision, Object Detection API, which can recognize an object from an image, and then provide an approximate position for that object in 3D space.

Exercícios de bônusBonus exercises

Exercício 1Exercise 1

Adicionando ao rótulo de texto, use um cubo semitransparente para encapsular o objeto real em uma caixadelimitadora 3D.Adding to the Text Label, use a semi-transparent cube to wrap the real object in a 3D Bounding Box.

Exercício 2Exercise 2

Treine sua Serviço de Visão Personalizada para reconhecer mais objetos.Train your Custom Vision Service to recognize more objects.

Exercício 3Exercise 3

Tocar um som quando um objeto for reconhecido.Play a sound when an object is recognized.

Exercício 4Exercise 4

Use a API para treinar novamente seu serviço com as mesmas imagens que seu aplicativo está analisando, para tornar o serviço mais preciso (faça a previsão e o treinamento simultaneamente).Use the API to re-train your Service with the same images your app is analyzing, so to make the Service more accurate (do both prediction and training simultaneously).