Bagikan melalui


Kamera yang dapat di-locatable

Sebelum memulai di sini, kami sarankan Anda melihat artikel gambaran umum kamera Locatable kami yang berisi informasi gambaran umum dan tabel dengan detail kamera HoloLens 1 dan 2.

Menggunakan MediaFrameReference

Instruksi ini berlaku jika Anda menggunakan kelas MediaFrameReference untuk membaca bingkai gambar dari kamera.

Setiap bingkai gambar (baik foto atau video) menyertakan SpatialCoordinateSystem yang berakar pada kamera pada saat pengambilan, yang dapat diakses menggunakan properti CoordinateSystemmediaFrameReference Anda. Setiap bingkai berisi deskripsi model lensa kamera, yang dapat ditemukan di properti CameraIntrinsics . Bersama-sama, transformasi ini mendefinisikan untuk setiap piksel sinar dalam ruang 3D yang mewakili jalur yang diambil oleh foton yang menghasilkan piksel. Sinar ini dapat terkait dengan konten lain dalam aplikasi dengan mendapatkan transformasi dari sistem koordinat bingkai ke beberapa sistem koordinat lainnya (misalnya dari bingkai referensi stasioner).

Setiap bingkai gambar menyediakan hal berikut:

Sampel HolographicFaceTracking menunjukkan cara yang cukup mudah untuk mengkueri transformasi antara sistem koordinat kamera dan sistem koordinat aplikasi Anda sendiri.

Menggunakan Media Foundation

Jika Anda menggunakan Media Foundation secara langsung untuk membaca bingkai gambar dari kamera, Anda dapat menggunakan atribut MFSampleExtension_CameraExtrinsics setiap bingkai dan atribut MFSampleExtension_PinholeCameraIntrinsics untuk menemukan bingkai kamera yang relatif terhadap sistem koordinat aplikasi Anda yang lain, seperti yang ditunjukkan dalam kode sampel ini:

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

Skenario Penggunaan Kamera yang Dapat Locatable

Menampilkan foto atau video di dunia tempat foto atau video diambil

Bingkai Kamera Perangkat dilengkapi dengan transformasi "Kamera Ke Dunia", yang dapat digunakan untuk menunjukkan dengan tepat di mana perangkat berada ketika gambar diambil. Misalnya, Anda dapat memposisikan ikon holografik kecil di lokasi ini (CameraToWorld.MultiplyPoint(Vector3.zero)) dan bahkan menggambar panah kecil ke arah yang dihadapi kamera (CameraToWorld.MultiplyVector(Vector3.forward)).

Kecepatan Bingkai

Menjaga kecepatan bingkai aplikasi interaktif sangat penting, terutama saat berhadapan dengan algoritma pengenalan gambar yang berjalan lama. Untuk alasan ini, kami biasanya menggunakan pola berikut:

  1. Utas Utama: mengelola objek kamera
  2. Utas Utama: meminta bingkai baru (asinkron)
  3. Utas Utama: meneruskan bingkai baru ke rangkaian pelacakan
  4. Utas Pelacakan: memproses gambar untuk mengumpulkan titik kunci
  5. Utas Utama: memindahkan model virtual agar sesuai dengan titik kunci yang ditemukan
  6. Utas Utama: ulangi dari langkah 2

Beberapa sistem penanda gambar hanya menyediakan satu lokasi piksel (yang lain menyediakan transformasi penuh dalam hal ini bagian ini tidak akan diperlukan), yang setara dengan sinar lokasi yang mungkin. Untuk sampai ke satu lokasi ketiga, kita kemudian dapat memanfaatkan beberapa sinar dan menemukan hasil akhir dengan perkiraan persimpangan mereka. Untuk melakukan ini, Anda harus:

  1. Dapatkan perulangan akan mengumpulkan beberapa gambar kamera
  2. Temukan titik fitur terkait, dan sinar dunianya
  3. Ketika Anda memiliki kamus fitur, masing-masing dengan beberapa sinar dunia, Anda dapat menggunakan kode berikut untuk memecahkan persimpangan sinar tersebut:
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);
 }

Memosisikan adegan yang dimodelkan

Mengingat dua lokasi tag terlacak atau lebih, Anda dapat memposisikan adegan yang dimodelkan agar sesuai dengan skenario pengguna saat ini. Jika Anda tidak dapat mengasumsikan gravitasi, maka Anda akan memerlukan tiga lokasi tag. Dalam banyak kasus, kami menggunakan skema warna di mana bola putih mewakili lokasi tag terlacak real time, dan bola biru mewakili lokasi tag yang dimodelkan. Ini memungkinkan pengguna untuk mengukur kualitas penyelarasan secara visual. Kami berasumsi pengaturan berikut di semua aplikasi kami:

  • Dua atau beberapa lokasi tag yang dimodelkan
  • Satu 'ruang kalibrasi', yang dalam adegan adalah induk tag
  • Pengidentifikasi fitur kamera
  • Perilaku, yang memindahkan ruang kalibrasi untuk menyelaraskan tag yang dimodelkan dengan tag real-time (kami berhati-hati untuk memindahkan ruang induk, bukan penanda yang dimodelkan sendiri, karena koneksi lain relatif terhadap tag tersebut).
// 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;

Lacak atau Identifikasi Tagged Stationary atau Memindahkan objek/wajah dunia nyata menggunakan LED atau pustaka pengenal lainnya

Contoh:

  • Robot industri dengan LED (atau kode QR untuk objek bergerak lebih lambat)
  • Mengidentifikasi dan mengenali objek di ruangan
  • Mengidentifikasi dan mengenali orang-orang di dalam ruangan, misalnya menempatkan kartu kontak holografik di atas wajah

Lihat juga