위치를 찾을 수 있는 카메라

여기를 시작 하기 전에 개요 정보 및 HoloLens 1 및 2 카메라 세부 정보를 포함 하는 테이블을 포함 하는 과정이 카메라 개요 문서를 확인 하는 것이 좋습니다.

MediaFrameReference 사용

이러한 지침은 MediaFrameReference 클래스를 사용 하 여 카메라에서 이미지 프레임을 읽는 경우에 적용 됩니다.

각 이미지 프레임 (사진 또는 비디오)에는 캡처 시점에 카메라를 기반으로 하는 SpatialCoordinateSystem 이 포함 되어 있으며,이는 MediaFrameReferenceCoordinateSystem 속성을 사용 하 여 액세스할 수 있습니다. 각 프레임에는 CameraIntrinsics 속성에서 찾을 수 있는 카메라 렌즈 모델에 대 한 설명이 포함 되어 있습니다. 이러한 변환은 함께 각 픽셀에 대해 픽셀을 생성 한 photons에서 가져온 경로를 나타내는 3D 공간의 광선을 정의 합니다. 이러한 광선은 프레임의 좌표계에서 다른 좌표계 (예: 고정 참조 프레임)로 변환을 가져와서 앱의 다른 콘텐츠와 관련 될 수 있습니다.

각 이미지 프레임은 다음을 제공 합니다.

HolographicFaceTracking 샘플 에서는 카메라의 좌표계와 사용자 고유의 응용 프로그램 좌표계 간에 변환을 쿼리 하는 매우 간단한 방법을 보여 줍니다.

미디어 파운데이션 사용

카메라에서 이미지 프레임을 읽기 위해 미디어 파운데이션를 직접 사용 하는 경우 다음 샘플 코드에 표시 된 것 처럼 각 프레임의 MFSampleExtension_CameraExtrinsics 특성MFSampleExtension_PinholeCameraIntrinsics 특성 을 사용 하 여 응용 프로그램의 다른 좌표계를 기준으로 카메라 프레임을 찾을 수 있습니다.

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

과정이 카메라 사용 시나리오

캡처된 전 세계에 사진 또는 비디오 표시

장치 카메라 프레임에는 이미지를 찍은 시간을 정확히 표시 하는 데 사용할 수 있는 "전 세계 카메라" 변환이 포함 되어 있습니다. 예를 들어이 위치 (MultiplyPoint (Vector3))에 작은 holographic 아이콘을 배치 하 고 카메라가 연결 된 방향으로 약간의 화살표 (CameraToWorld (MultiplyVector))를 그릴 수 있습니다.

프레임 속도

특히 장기 실행 이미지 인식 알고리즘을 처리할 때는 대화형 응용 프로그램 프레임 률을 유지 하는 것이 중요 합니다. 이러한 이유로 일반적으로 다음과 같은 패턴을 사용 합니다.

  1. 주 스레드: 카메라 개체를 관리 합니다.
  2. 주 스레드: 새 프레임 (비동기)을 요청 합니다.
  3. 주 스레드: 추적 스레드에 새 프레임 전달
  4. 추적 스레드: 키 요소를 수집 하는 이미지를 처리 합니다.
  5. 주 스레드: 찾은 키 지점과 일치 하도록 가상 모델을 이동 합니다.
  6. 주 스레드: 2 단계에서 반복

일부 이미지 표식 시스템은 단일 픽셀 위치만 제공 합니다 (다른 사용자는이 섹션이 필요 하지 않은 전체 변환을 제공 함) .이는 가능한 위치의 광선과 동일 합니다. 단일 세 번째 위치로 이동 하려면 여러 광선을 활용 하 여 대략적인 교집합에 따라 최종 결과를 찾을 수 있습니다. 이 작업을 수행하려면 다음 작업이 필요합니다.

  1. 여러 카메라 이미지를 수집 하는 루프 가져오기
  2. 연결 된 기능 지점과 해당 세계 광선 찾기
  3. 여러 가지 세계 광선을 포함 하는 기능 사전이 있는 경우 다음 코드를 사용 하 여 이러한 광선의 교차를 해결할 수 있습니다.
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);
 }

모델링 된 장면 위치 지정

두 개 이상의 추적 된 태그 위치가 지정 된 경우 사용자의 현재 시나리오에 맞게 모델링 된 장면을 배치할 수 있습니다. 중력을 가정할 수 없는 경우 세 가지 태그 위치가 필요 합니다. 대부분의 경우 흰색 구는 실시간 추적 된 태그 위치를 나타내고 파란색 구는 모델링 된 태그 위치를 나타내는 색 구성표를 사용 합니다. 이를 통해 사용자는 맞춤 품질을 시각적으로 측정할 수 있습니다. 모든 응용 프로그램에서 다음 설정을 가정 합니다.

  • 두 개 이상의 모델링 한 태그 위치
  • 장면에 있는 하나의 ' 보정 공간 '은 태그의 부모입니다.
  • 카메라 기능 식별자
  • 동작-보정 공간을 이동 하 여 모델링 된 태그를 실시간 태그에 맞춰 정렬 합니다. 즉, 다른 연결에 상대적인 위치에 있으므로 모델링 된 마커 자체가 아니라 부모 공간을 이동 하는 것은 주의를 기울여야 합니다.
// 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 또는 다른 인식기 라이브러리를 사용 하 여 태그가 지정 된 고정 또는 실제 개체/얼굴 이동 추적 또는 식별

예제:

  • Led가 있는 산업 로봇 (또는 느린 개체 이동에 대 한 QR 코드)
  • 대화방의 개체 식별 및 인식
  • 대화방에서 사람을 식별 하 고 인식 합니다 (예: 얼굴에 holographic 접점 카드 배치).

참고 항목