Locatable camera
Voordat u hier aan de slag gaat, raden we u aan ons artikel Locatable camera-overzicht te bekijken, dat overzichtsinformatie en een tabel met HoloLens 1- en 2-cameradetails bevat.
MediaFrameReference gebruiken
Deze instructies zijn van toepassing als u de klasse MediaFrameReference gebruikt om afbeeldingsframes van de camera te lezen.
Elk afbeeldingsframe (foto of video) bevat een SpatialCoordinateSystem dat is geroot op de camera op het moment van de opname, dat toegankelijk is met behulp van de eigenschap CoordinateSystem van uw MediaFrameReference. Elk frame bevat een beschrijving van het cameralensmodel, dat te vinden is in de eigenschap CameraIntrinsics . Samen definiëren deze transformaties voor elke pixel een straal in 3D-ruimte die het pad vertegenwoordigt dat is genomen door de fotonen die de pixel hebben geproduceerd. Deze stralen kunnen worden gerelateerd aan andere inhoud in de app door de transformatie te verkrijgen van het coördinatensysteem van het frame naar een ander coördinatensysteem (bijvoorbeeld van een stationair referentiekader).
Elk afbeeldingsframe biedt het volgende:
- Pixelgegevens (in RGB/NV12/JPEG/etc. indeling)
- Een SpatialCoordinateSystem vanaf de locatie van de opname
- Een CameraIntrinsics-klasse met de lensmodus van de camera
Het HolographicFaceTracking-voorbeeld toont de vrij eenvoudige manier om te zoeken naar de transformatie tussen het coördinaatsysteem van de camera en uw eigen toepassingscoördinaatsystemen.
Media Foundation gebruiken
Als u Media Foundation rechtstreeks gebruikt om afbeeldingsframes van de camera te lezen, kunt u het kenmerk MFSampleExtension_CameraExtrinsics en MFSampleExtension_PinholeCameraIntrinsics van elk frame gebruiken om cameraframes te vinden ten opzichte van de andere coördinaatsystemen van uw toepassing, zoals wordt weergegeven in deze voorbeeldcode:
#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 };
};
Scenario's voor gebruik van camera's
Een foto of video weergeven in de wereld waar deze is vastgelegd
De cameraframes van het apparaat worden geleverd met een transformatie 'Camera naar wereld', die kan worden gebruikt om precies aan te geven waar het apparaat was toen de afbeelding werd gemaakt. U kunt bijvoorbeeld een klein holografisch pictogram op deze locatie plaatsen (CameraToWorld.MultiplyPoint(Vector3.zero)) en zelfs een kleine pijl tekenen in de richting van de camera (CameraToWorld.MultiplyVector(Vector3.forward)).
Framesnelheid
Het behouden van een interactieve toepassingsframesnelheid is essentieel, met name wanneer u te maken hebt met langlopende algoritmen voor afbeeldingsherkenning. Daarom gebruiken we meestal het volgende patroon:
- Hoofdthread: beheert het cameraobject
- Hoofdthread: nieuwe frames aanvragen (asynchroon)
- Hoofdthread: nieuwe frames doorgeven aan traceringsthread
- Traceringsthread: verwerkt afbeelding om belangrijke punten te verzamelen
- Hoofdthread: verplaatst virtueel model om te voldoen aan gevonden sleutelpunten
- Hoofdthread: herhalen uit stap 2
Sommige afbeeldingsmarkeringssystemen bieden slechts één pixellocatie (andere bieden de volledige transformatie in welk geval deze sectie niet nodig is), wat overeenkomt met een ray van mogelijke locaties. Om op een derde locatie te komen, kunnen we vervolgens meerdere stralen gebruiken en het uiteindelijke resultaat vinden op basis van hun bij benadering snijpunt. Hiervoor moet u het volgende doen:
- Een lus maken met het verzamelen van meerdere camerabeelden
- De bijbehorende functiepunten en hun wereldstralen zoeken
- Wanneer u een woordenlijst met functies hebt, elk met meerdere wereldstralen, kunt u de volgende code gebruiken om het snijpunt van die stralen op te lossen:
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);
}
Een gemodelleerde scène plaatsen
Op basis van twee of meer bijgehouden taglocaties kunt u een gemodelleerde scène zo plaatsen dat deze past bij het huidige scenario van de gebruiker. Als u geen zwaartekracht kunt aannemen, hebt u drie taglocaties nodig. In veel gevallen gebruiken we een kleurenschema waarbij witte bollen realtime bijgehouden taglocaties vertegenwoordigen en blauwe bollen gemodelleerde taglocaties vertegenwoordigen. Hierdoor kan de gebruiker de uitlijningskwaliteit visueel meten. We gaan uit van de volgende installatie in al onze toepassingen:
- Twee of meer gemodelleerde taglocaties
- Eén 'kalibratieruimte', die in de scène het bovenliggende element van de tags is
- Functie-id van camera
- Gedrag, waardoor de kalibratieruimte wordt verplaatst om de gemodelleerde tags uit te lijnen met de realtime tags (we zijn voorzichtig met het verplaatsen van de bovenliggende ruimte, niet de gemodelleerde markeringen zelf, omdat andere verbindingsposities ten opzichte van deze tags zijn).
// 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;
Gelabelde stationaire of bewegende objecten/gezichten in de echte wereld bijhouden of identificeren met behulp van LED's of andere herkenningsbibliotheken
Voorbeelden:
- Industriële robots met LED's (of QR-codes voor tragere bewegende objecten)
- Objecten in de ruimte identificeren en herkennen
- Personen in de ruimte identificeren en herkennen, bijvoorbeeld door holografische visitekaartjes over gezichten te plaatsen