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:
- Képpontadatok (RGB/NV12/JPEG/stb. formátumban)
- A SpatialCoordinateSystem a rögzítés helyéről
- A KameraIntrinsics osztály, amely a kamera objektív üzemmódját tartalmazza
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:
- Főszál: kezeli a kameraobjektumot
- Fő szál: új kereteket kér (aszinkron)
- Fő szál: új keretek átadása a szál nyomon követéséhez
- Nyomkövetési szál: feldolgozza a rendszerképet a kulcspontok gyűjtéséhez
- Fő szál: áthelyezi a virtuális modellt a talált kulcspontoknak megfelelően
- 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:
- Egy hurok lekérése, amely több kameraképet gyűjt
- A kapcsolódó funkciópontok és a hozzájuk tartozó világsugarak megkeresése
- 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é