Locatable kamera

Buraya başlamadan önce, genel bakış bilgilerini içeren Locatable kameraya genel bakış makalemize ve HoloLens 1 ve 2 kamera ayrıntılarını içeren bir tabloya göz atmanızı öneririz.

MediaFrameReference Kullanma

Kameradan görüntü çerçevelerini okumak için MediaFrameReference sınıfını kullanıyorsanız bu yönergeler geçerlidir.

Her görüntü çerçevesi (fotoğraf veya video) yakalama sırasında kameraya kök olarak eklenmiş bir SpatialCoordinateSystem içerir ve mediaFrameReference'ınızınCoordinateSystem özelliği kullanılarak erişilebilir. Her çerçeve, CameraIntrinsics özelliğinde bulunabilen kamera lens modelinin açıklamasını içerir. Bu dönüşümler birlikte her piksel için pikseli oluşturan fotonların izlediği yolu temsil eden 3B alanda bir ışın tanımlar. Bu ışınlar, çerçevenin koordinat sisteminden başka bir koordinat sistemine (örneğin , sabit bir referans çerçevesinden) dönüştürülerek uygulamadaki diğer içerikle ilgili olabilir.

Her görüntü çerçevesi aşağıdakileri sağlar:

HolographicFaceTracking örneği, kameranın koordinat sistemi ve kendi uygulama koordinat sistemleriniz arasındaki dönüşümü sorgulamanın oldukça kolay bir yolunu gösterir.

Media Foundation kullanma

Kameradan görüntü çerçevelerini okumak için media foundation'ı doğrudan kullanıyorsanız, bu örnek kodda gösterildiği gibi, uygulamanızın diğer koordinat sistemlerine göre kamera çerçevelerini bulmak için her karenin MFSampleExtension_CameraExtrinsics özniteliğini ve MFSampleExtension_PinholeCameraIntrinsics özniteliğini kullanabilirsiniz:

#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 Kamera Kullanım Senaryoları

Fotoğrafın veya videonun çekildiği dünyada gösterme

Cihaz Kamerası çerçeveleri, görüntünün çekildiği sırada cihazın tam olarak nerede olduğunu göstermek için kullanılabilen bir "Kameradan Dünyaya" dönüştürme ile birlikte gelir. Örneğin, bu konuma küçük bir holografik simge (CameraToWorld.MultiplyPoint(Vector3.zero)) konumlandırabilir ve hatta kameranın karşı karşıya olduğu yöne küçük bir ok çizebilirsiniz (CameraToWorld.MultiplyVector(Vector3.forward)).

Kare Hızı

Etkileşimli bir uygulamanın kare hızını korumak, özellikle uzun süre çalışan görüntü tanıma algoritmalarıyla çalışırken kritik önem taşır. Bu nedenle genellikle aşağıdaki deseni kullanırız:

  1. Ana İş Parçacığı: Kamera nesnesini yönetir
  2. Ana İş Parçacığı: Yeni çerçeveler ister (zaman uyumsuz)
  3. Ana İş Parçacığı: İzleme iş parçacığına yeni çerçeveler geçirme
  4. İzleme İş Parçacığı: Önemli noktaları toplamak için görüntüyü işler
  5. Ana İş Parçacığı: Sanal modeli bulunan anahtar noktaları eşleştirecek şekilde taşır
  6. Ana İş Parçacığı: 2. adımdan tekrarlayın

Bazı görüntü işaretçisi sistemleri yalnızca tek bir piksel konumu sağlar (diğerleri bu durumda bu bölümün gerekli olmayacağı tam dönüşümü sağlar), bu da olası konumların ışını anlamına gelir. Tek bir üçüncü konuma ulaşmak için birden çok ışından yararlanabilir ve yaklaşık kesişimlerine göre nihai sonucu bulabiliriz. Bunu yapmak için şunları yapmanız gerekir:

  1. Birden çok kamera görüntüsü toplayan bir döngü elde etme
  2. İlişkili özellik noktalarını ve bunların dünya ışınlarını bulma
  3. Her biri birden çok dünya ışınını içeren bir özellik sözlüğüne sahip olduğunuzda, bu ışınların kesişimini çözmek için aşağıdaki kodu kullanabilirsiniz:
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);
 }

Modellenmiş bir sahneyi konumlandırma

İki veya daha fazla izlenen etiket konumu verildiğinde, modellenmiş bir sahneyi kullanıcının geçerli senaryosuna uyacak şekilde konumlandırabilirsiniz. Yerçekimini varsayamıyorsanız üç etiket konumuna ihtiyacınız vardır. Çoğu durumda, beyaz kürelerin gerçek zamanlı izlenen etiket konumlarını ve mavi kürelerin modellenmiş etiket konumlarını temsil ettiği bir renk düzeni kullanırız. Bu, kullanıcının hizalama kalitesini görsel olarak ölçmesine olanak tanır. Tüm uygulamalarımızda aşağıdaki kurulumun olduğunu varsayıyoruz:

  • İki veya daha fazla modellenmiş etiket konumu
  • Sahnede etiketlerin üst öğesi olan bir 'kalibrasyon alanı'.
  • Kamera özelliği tanımlayıcısı
  • Modellenmiş etiketleri gerçek zamanlı etiketlerle hizalamak için kalibrasyon alanını hareket ettiren davranış (diğer bağlantılar bunlara göre konumlar olduğundan, modellenmiş işaretçilerin kendisini değil üst boşluğu taşımaya dikkat ederiz).
// 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;

LED'leri veya diğer tanıyıcı kitaplıklarını kullanarak Etiketli sabit veya Hareketli gerçek dünya nesnelerini/yüzlerini izleme veya tanımlama

Örnekler:

  • LED'li endüstriyel robotlar (veya daha yavaş hareket eden nesneler için QR kodları)
  • Odadaki nesneleri tanımlama ve tanıma
  • Odadaki kişileri tanımlama ve tanıma, örneğin yüzlerin üzerine holografik kişi kartları yerleştirme

Ayrıca bkz.