Megosztás a következőn keresztül:


Locatable kamera

Mielőtt elkezdené, javasoljuk, hogy tekintse meg a Locatable kamera áttekintő cikkét, amely áttekintési információkat és egy táblázatot tartalmaz a HoloLens 1 és 2 kamera részleteivel.

A MediaFrameReference használata

Ezek az utasítások akkor érvényesek, ha a MediaFrameReference osztályt használja képkeretek beolvasásához a kamerából.

Minden képkockához (akár fényképhez, akár videóhoz) tartozik egy SpatialCoordinateSystem, amely a rögzítés időpontjában a kamerában gyökerezik, és amely a MediaFrameReferenceCoordinateSystem tulajdonságával érhető el. Minden keret tartalmazza a kameralencse modelljének leírását, amely a CameraIntrinsics tulajdonságban található. Ezek az átalakítások együtt definiálnak minden képponthoz egy 3D térközben lévő sugarat, amely a képpontot előállító fotonok által megtett utat jelöli. Ezek a sugarak az alkalmazás más tartalmaihoz is kapcsolódhatnak, ha a keret koordinátarendszeréből egy másik koordinátarendszerbe (például egy helyhez kötött referenciakeretből) végzik az átalakítást.

Az egyes képkeretek a következőket biztosítják:

A HolographicFaceTracking minta bemutatja a kamera koordinátarendszere és a saját alkalmazáskoordináta-rendszerei közötti átalakítás lekérdezésének meglehetősen egyszerű módját.

A Media Foundation használata

Ha közvetlenül a Media Foundation segítségével olvassa be a képkockákat a kameráról, az egyes keretek MFSampleExtension_CameraExtrinsics attribútumával és MFSampleExtension_PinholeCameraIntrinsics attribútumával megkeresheti a kamerakereteket az alkalmazás más koordinátarendszereihez képest, ahogy az alábbi mintakódban látható:

#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 };
};

Locatable Camera Usage Scenarios

Fénykép vagy videó megjelenítése a világon, ahol rögzítették

Az Eszközkamera képkockái "Kamera a világba" átalakítással érkeznek, amellyel pontosan meg lehet jeleníteni, hogy hol volt az eszköz a kép készítésének időpontjában. Elhelyezhet például egy kis holografikus ikont ezen a helyen (CameraToWorld.MultiplyPoint(Vector3.zero)), és még egy kis nyilat is rajzolhat a kamera felé (CameraToWorld.MultiplyVector(Vector3.forward)).

Képkockasebesség

Az interaktív alkalmazások képkockasebességének megőrzése kritikus fontosságú, különösen hosszú ideig futó képfelismerő algoritmusok esetén. Ezért általában a következő mintát használjuk:

  1. Főszál: kezeli a kameraobjektumot
  2. Fő szál: új kereteket kér (aszinkron)
  3. Fő szál: új keretek átadása a szál nyomon követéséhez
  4. Nyomkövetési szál: feldolgozza a rendszerképet a kulcspontok gyűjtéséhez
  5. Fő szál: áthelyezi a virtuális modellt a talált kulcspontoknak megfelelően
  6. Főszál: ismételje meg a 2. lépésben

Egyes képjelölő rendszerek csak egyetlen képpontos helyet biztosítanak (mások biztosítják a teljes átalakítást, amely esetben erre a szakaszra nincs szükség), ami megfelel a lehetséges helyek sugarának. Ha egyetlen harmadik helyre szeretnénk jutni, több sugarat is használhatunk, és a végeredményt a hozzávetőleges metszetük alapján találjuk meg. Ehhez a következőket kell tennie:

  1. Egy hurok lekérése, amely több kameraképet gyűjt
  2. A kapcsolódó funkciópontok és a hozzájuk tartozó világsugarak megkeresése
  3. Ha rendelkezik funkciók szótárával, amelyek mindegyike több világsugárral rendelkezik, a következő kóddal oldhatja meg a sugarak metszetét:
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);
 }

Modellezett jelenet elhelyezése

Két vagy több nyomon követett címkehely esetén a modellezett jelenetet a felhasználó aktuális forgatókönyvének megfelelően helyezheti el. Ha nem tudja feltételezni a gravitációt, akkor három címkehelyre lesz szüksége. Sok esetben olyan színsémát használunk, amelyben a fehér gömbök valós idejű nyomon követett címkehelyeket, a kék gömbök pedig a modellezett címkehelyeket jelölik. Ez lehetővé teszi, hogy a felhasználó vizuálisan felmérje az igazítás minőségét. Feltételezzük, hogy az összes alkalmazásban a következő beállítás van beállítva:

  • Két vagy több modellezett címkehely
  • Egy "kalibrációs tér", amely a jelenetben a szülő a címkék
  • Kamera funkcióazonosítója
  • Viselkedés, amely áthelyezi a kalibrációs teret, hogy a modellezett címkéket a valós idejű címkékhez igazítsa (ügyelünk arra, hogy a szülőteret ne maguk a modellezett jelölők helyezzék át, mert a többi összekapcsolás egymáshoz viszonyított pozíció).
// 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;

Címkézett helyőrző vagy valós objektumok/arcok nyomon követése vagy azonosítása LED-ek vagy más felismerőkódtárak használatával

Példák:

  • Ipari robotok LED-ekkel (vagy QR-kódok lassabb mozgó objektumokhoz)
  • Objektumok azonosítása és felismerése a szobában
  • Személyek azonosítása és felismerése a szobában, például holografikus névjegykártyák elhelyezése arcok fölé

Lásd még