Camera met locatable
Voordat u hier aan de slag gaat, raden we u aan ons artikel Locatable camera overview 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 vastleggen. Dit is toegankelijk 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 een 3D-ruimte die het pad vertegenwoordigt dat is genomen door de fotonen die de pixel produceren. 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-/enzovoort-indeling)
- Een SpatialCoordinateSystem van de locatie van de opname
- Een CameraIntrinsics-klasse met de lensmodus van de camera
Het HolographicFaceTracking-voorbeeld toont de vrij eenvoudige manier om een query uit te voeren voor de transformatie tussen het coördinatensysteem 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ördinatensystemen 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 };
};
Gebruiksscenario's voor locatable 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 weer te geven waar het apparaat was toen de foto 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 waar de camera naartoe gericht was (CameraToWorld.MultiplyVector(Vector3.forward)).
Framesnelheid
Het behouden van een interactieve framesnelheid van een toepassing is essentieel, met name wanneer u te maken hebt met langlopende algoritmen voor afbeeldingsherkenning. Daarom gebruiken we vaak 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: virtueel model wordt verplaatst om te voldoen aan gevonden sleutelpunten
- Hoofdthread: herhalen uit stap 2
Sommige afbeeldingsmarkeringssystemen bieden slechts een locatie met één pixel (andere bieden de volledige transformatie in welk geval deze sectie niet nodig is), wat overeenkomt met een straal van mogelijke locaties. Om naar één derde locatie te komen, kunnen we vervolgens meerdere stralen gebruiken en het uiteindelijke resultaat vinden op basis van hun snijpunt bij benadering. Hiervoor moet u het volgende doen:
- Een lus aan de slag 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 locaties voor bijgehouden tags 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 instellingen 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 om de bovenliggende ruimte te verplaatsen, niet de gemodelleerde markeringen zelf, omdat andere verbindingsposities ten opzichte van hen 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 recognizer-bibliotheken
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