Vyhledatelná kamera

Než začnete, doporučujeme, abyste se podívali na článek s přehledem lokatable kamery , který obsahuje přehledové informace a tabulku s podrobnostmi o fotoaparátech HoloLens 1 a 2.

Použití MediaFrameReference

Tyto pokyny platí v případě, že ke čtení snímků obrázků z fotoaparátu používáte třídu MediaFrameReference .

Každý rámeček obrázku (fotka nebo video) obsahuje SpatialCoordinateSystem s rootem ve fotoaparátu v době zachycení, ke kterému je možné přistupovat pomocí vlastnosti CoordinateSystem vašeho MediaFrameReference. Každý snímek obsahuje popis modelu objektivu fotoaparátu, který najdete ve vlastnosti CameraIntrinsics . Tyto transformace společně definují pro každý pixel paprsek v 3D prostoru představující cestu pořízenou fotony, které vytvořily pixel. Tyto paprsky mohou být spojeny s jiným obsahem v aplikaci získáním transformace ze souřadnicového systému rámce do jiného souřadnicového systému (např. ze statického referenčního rámce).

Každý rámeček obrázku poskytuje následující:

Ukázka HolographicFaceTracking ukazuje poměrně jednoduchý způsob, jak dotazovat na transformaci mezi souřadnicovým systémem kamery a souřadnicovými systémy vaší vlastní aplikace.

Použití Media Foundation

Pokud používáte Media Foundation přímo ke čtení snímků z fotoaparátu, můžete použít atribut MFSampleExtension_CameraExtrinsics každého snímku a atribut MFSampleExtension_PinholeCameraIntrinsics k vyhledání snímků vzhledem k ostatním souřadnicovým systémům vaší aplikace, jak je znázorněno v tomto ukázkovém kódu:

#include <winrt/windows.perception.spatial.preview.h>
#include <mfapi.h>
#include <mfidl.h>
 
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Windows::Perception;
using namespace winrt::Windows::Perception::Spatial;
using namespace winrt::Windows::Perception::Spatial::Preview;
 
class CameraFrameLocator
{
public:
    struct CameraFrameLocation
    {
        SpatialCoordinateSystem CoordinateSystem;
        float4x4 CameraViewToCoordinateSystemTransform;
        MFPinholeCameraIntrinsics Intrinsics;
    };
 
    std::optional<CameraFrameLocation> TryLocateCameraFrame(IMFSample* pSample)
    {
        MFCameraExtrinsics cameraExtrinsics;
        MFPinholeCameraIntrinsics cameraIntrinsics;
        UINT32 sizeCameraExtrinsics = 0;
        UINT32 sizeCameraIntrinsics = 0;
        UINT64 sampleTimeHns = 0;
 
        // query sample for calibration and validate
        if (FAILED(pSample->GetUINT64(MFSampleExtension_DeviceTimestamp, &sampleTimeHns)) ||
            FAILED(pSample->GetBlob(MFSampleExtension_CameraExtrinsics, (UINT8*)& cameraExtrinsics, sizeof(cameraExtrinsics), &sizeCameraExtrinsics)) ||
            FAILED(pSample->GetBlob(MFSampleExtension_PinholeCameraIntrinsics, (UINT8*)& cameraIntrinsics, sizeof(cameraIntrinsics), &sizeCameraIntrinsics)) ||
            (sizeCameraExtrinsics != sizeof(cameraExtrinsics)) ||
            (sizeCameraIntrinsics != sizeof(cameraIntrinsics)) ||
            (cameraExtrinsics.TransformCount == 0))
        {
            return std::nullopt;
        }
 
        // compute extrinsic transform
        const auto& calibratedTransform = cameraExtrinsics.CalibratedTransforms[0];
        const GUID& dynamicNodeId = calibratedTransform.CalibrationId;
        const float4x4 cameraToDynamicNode =
            make_float4x4_from_quaternion(quaternion{ calibratedTransform.Orientation.x, calibratedTransform.Orientation.y, calibratedTransform.Orientation.z, calibratedTransform.Orientation.w }) *
            make_float4x4_translation(calibratedTransform.Position.x, calibratedTransform.Position.y, calibratedTransform.Position.z);
 
        // update locator cache for dynamic node
        if (dynamicNodeId != m_currentDynamicNodeId || !m_locator)
        {
            m_locator = SpatialGraphInteropPreview::CreateLocatorForNode(dynamicNodeId);
            if (!m_locator)
            {
                return std::nullopt;
            }
 
            m_frameOfReference = m_locator.CreateAttachedFrameOfReferenceAtCurrentHeading();
            m_currentDynamicNodeId = dynamicNodeId;
        }
 
        // locate dynamic node
        auto timestamp = PerceptionTimestampHelper::FromSystemRelativeTargetTime(TimeSpan{ sampleTimeHns });
        auto coordinateSystem = m_frameOfReference.GetStationaryCoordinateSystemAtTimestamp(timestamp);
        auto location = m_locator.TryLocateAtTimestamp(timestamp, coordinateSystem);
        if (!location)
        {
            return std::nullopt;
        }
 
        const float4x4 dynamicNodeToCoordinateSystem = make_float4x4_from_quaternion(location.Orientation()) * make_float4x4_translation(location.Position());
 
        return CameraFrameLocation{ coordinateSystem, cameraToDynamicNode * dynamicNodeToCoordinateSystem, cameraIntrinsics };
    }

private:
    GUID m_currentDynamicNodeId{ GUID_NULL };
    SpatialLocator m_locator{ nullptr };
    SpatialLocatorAttachedFrameOfReference m_frameOfReference{ nullptr };
};

Scénáře použití lokalizovatelné kamery

Zobrazení fotky nebo videa ve světě, kde byla pořízena

Snímky kamery zařízení se dodávají s transformací "Kamera do světa", která se dá použít k zobrazení přesného umístění zařízení, když byl snímek pořízen. Můžete například umístit malou holografickou ikonu na toto místo (CameraToWorld.MultiplyPoint(Vector3.zero)) a dokonce nakreslit malou šipku ve směru, kterým fotoaparát směřoval (CameraToWorld.MultiplyVector(Vector3.forward)).

Kmitočet

Udržování interaktivní snímkové frekvence aplikace je důležité, zejména při práci s dlouhotrvajícími algoritmy pro rozpoznávání obrázků. Z tohoto důvodu běžně používáme následující vzor:

  1. Hlavní vlákno: spravuje objekt kamery.
  2. Hlavní vlákno: požaduje nové rámce (asynchronní)
  3. Hlavní vlákno: předat nové rámce do sledování vlákna
  4. Sledovací vlákno: zpracovává obrázek ke shromažďování klíčových bodů.
  5. Hlavní vlákno: přesune virtuální model tak, aby odpovídal nalezené klíčové body.
  6. Hlavní vlákno: opakujte od kroku 2.

Některé systémy značek obrázků poskytují pouze jeden pixel umístění (jiné poskytují úplnou transformaci, v takovém případě nebude tento oddíl potřeba), což odpovídá paprsku možných umístění. Abychom se dostali na jedno třetí místo, můžeme využít více paprsků a najít konečný výsledek podle jejich přibližného průsečíku. Abyste to mohli udělat, budete muset:

  1. Získání smyčky při shromažďování více snímků z fotoaparátu
  2. Vyhledání přidružených bodů prvků a jejich paprsků světa
  3. Pokud máte slovník prvků, každý s více paprsky světa, můžete použít následující kód k řešení průniku těchto paprsků:
public static Vector3 ClosestPointBetweenRays(
   Vector3 point1, Vector3 normalizedDirection1,
   Vector3 point2, Vector3 normalizedDirection2) {
   float directionProjection = Vector3.Dot(normalizedDirection1, normalizedDirection2);
   if (directionProjection == 1) {
     return point1; // parallel lines
   }
   float projection1 = Vector3.Dot(point2 - point1, normalizedDirection1);
   float projection2 = Vector3.Dot(point2 - point1, normalizedDirection2);
   float distanceAlongLine1 = (projection1 - directionProjection * projection2) / (1 - directionProjection * directionProjection);
   float distanceAlongLine2 = (projection2 - directionProjection * projection1) / (directionProjection * directionProjection - 1);
   Vector3 pointOnLine1 = point1 + distanceAlongLine1 * normalizedDirection1;
   Vector3 pointOnLine2 = point2 + distanceAlongLine2 * normalizedDirection2;
   return Vector3.Lerp(pointOnLine2, pointOnLine1, 0.5f);
 }

Umístění modelované scény

Pokud máte dvě nebo více sledovaných umístění značek, můžete modelovanou scénu umístit tak, aby odpovídala aktuálnímu scénáři uživatele. Pokud nemůžete předpokládat závažnost, budete potřebovat tři umístění značek. V mnoha případech používáme barevné schéma, kde bílé koule představují umístění sledovaných značek v reálném čase a modré koule představují umístění modelovaných značek. To uživateli umožňuje vizuálně změřit kvalitu zarovnání. Ve všech aplikacích předpokládáme následující nastavení:

  • Dvě nebo více umístění modelovaných značek
  • Jeden "kalibrační prostor", který je ve scéně nadřazený značky
  • Identifikátor funkce fotoaparátu
  • Chování, které přesouvá prostor kalibrace tak, aby se modelované značky zarovnaly se značkami v reálném čase (dbáme na to, abychom přesunuli nadřazený prostor, ne samotné modelované značky, protože jiné spojení je relativní k nim).
// In the two tags case:
 Vector3 idealDelta = (realTags[1].EstimatedWorldPos - realTags[0].EstimatedWorldPos);
 Vector3 curDelta = (modelledTags[1].transform.position - modelledTags[0].transform.position);
 if (IsAssumeGravity) {
   idealDelta.y = 0;
   curDelta.y = 0;
 }
 Quaternion deltaRot = Quaternion.FromToRotation(curDelta, idealDelta);
 trans.rotation = Quaternion.LookRotation(deltaRot * trans.forward, trans.up);
 trans.position += realTags[0].EstimatedWorldPos - modelledTags[0].transform.position;

Sledování nebo identifikace označených statických nebo pohybujících se reálných objektů nebo tváří pomocí LED diod nebo jiných knihoven rozpoznávání

Příklady:

  • Průmyslové roboty s LED diodami (nebo kódy QR pro pomalejší pohybující se objekty)
  • Identifikace a rozpoznávání objektů v místnosti
  • Identifikace a rozpoznávání lidí v místnosti, například umístění holografických karet kontaktů přes tváře

Viz také