HoloLens 카메라의 화각을 시각화
HoloLens의 탭 조작에 의해, 사진을 찍으면 손가락이 비쳐 버려, 회수하는 경우가 자주 있습니다. 이번에는 HoloLens의 카메라 화각을 3D 맵에 시각화해 보았습니다.
개발 환경
구현
이 기사 HoloLens에서 방의 3D 스캔을 시도했습니다. 을 기반으로 만들어 갑니다.
HoloPointCloud을 다운로드하세요.
Unity에서 열고 Project 보기에서 saco->Scenes->PhotoCaptureRaw 장면을 선택합니다.
HoloToolkit 및 Point Cloud Free Viewer의 Shader 폴더만 가져옵니다.
SpatialMapping의 Inspector 보기에서 PhotoCaptureRaw.cs를 다음과 같이 편집합니다.
using HoloToolkit.Unity.InputModule;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.VR.WSA.WebCam;
public class PhotoCaptureRaw : MonoBehaviour, IInputClickHandler
{
private PhotoCapture photoCaptureObject = null;
private bool isCapturing = false;
private int Width;
private int Height;
public int lengthOfLineRenderer;
// Use this for initialization
void Start()
{
InputManager.Instance.PushFallbackInputHandler(gameObject);
}
// Update is called once per frame
void Update()
{
//if (Input.GetKeyDown("space"))
//{
// FrameDisplay();
//}
}
void /*FrameDisplay()*/IInputClickHandler.OnInputClicked(InputClickedEventData eventData)
{
Debug.Log("Tapped");
if (!isCapturing)
{
if (photoCaptureObject != null)
{
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
isCapturing = true;
}
}
void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
photoCaptureObject = captureObject;
CameraParameters c = new CameraParameters();
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
c.hologramOpacity = 0.0f;
c.cameraResolutionWidth = cameraResolution.width;
c.cameraResolutionHeight = cameraResolution.height;
c.pixelFormat = CapturePixelFormat.BGRA32;
Width = cameraResolution.width;
Height = cameraResolution.height;
Debug.Log(String.Format("width={0}, height={1}", Width, Height));
//captureObject.StartPhotoModeAsync(c, OnPhotoModeStarted);
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(c, delegate (PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
}
void CreateFrame(PhotoCaptureFrame photoCaptureFrame)
{
// ------------------------------------------------------
// make xyxrgb
//
int texWidth = Width / 8;
int texHeight = Height / 8;
int numPoints = texWidth * texHeight;
List<Vector3> points = new List<Vector3>();
List<int> indecies = new List<int>();
// ------------------------------------------------------
// pos & rot
//
Matrix4x4 cameraToWorldMatrix;
photoCaptureFrame.TryGetCameraToWorldMatrix(out cameraToWorldMatrix);
Matrix4x4 worldToCameraMatrix = cameraToWorldMatrix.inverse;
Matrix4x4 projectionMatrix;
photoCaptureFrame.TryGetProjectionMatrix(out projectionMatrix);
// 0 right
// 1 up
// 2 forward
// 3 position
// Position the canvas object slightly in front
// of the real world web camera.
#if UNITY_EDITOR
Vector3 position = cameraToWorldMatrix.GetColumn(3) + cameraToWorldMatrix.GetColumn(2);
#else
Vector3 position = cameraToWorldMatrix.GetColumn(3) - cameraToWorldMatrix.GetColumn(2);
#endif
// Rotate the canvas object so that it faces the user.
Quaternion rotation = Quaternion.LookRotation(cameraToWorldMatrix.GetColumn(2), cameraToWorldMatrix.GetColumn(1));
// ------------------------------------------------------
// pos -> ray
//
int pointCount = 0;
{
int y = texHeight - 1;
for (int x = texWidth - 1; x >= 0; --x)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int x = 0;
for (int y = texHeight - 2; y >= 1; --y)
{
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
//int i = y * texWidth + x;
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int y = 0;
for (int x = 0; x <= texWidth - 1; ++x)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int x = texWidth - 1;
for (int y = 1; y < texHeight - 1; ++y)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
Debug.Log(pointCount);
LineRenderer lineRenderer = GetComponent<LineRenderer>();
lengthOfLineRenderer = pointCount;
lineRenderer.positionCount = pointCount;
var t = Time.time;
for (int i = 0; i < lengthOfLineRenderer; i++)
{
lineRenderer.SetPosition(i, points[i]);
}
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
photoCaptureObject.Dispose();
photoCaptureObject = null;
isCapturing = false;
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
if (result.success)
{
string filename = string.Format(@"CapturedImage{0}_n.jpg", Time.time);
string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename);
photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);
CreateFrame(photoCaptureFrame);
}
else
{
Debug.LogError("Unable to start photo mode!");
}
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
isCapturing = false;
}
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
{
if (result.success)
{
Debug.Log("Saved Photo to disk!");
}
else
{
Debug.Log("Failed to save Photo to disk");
}
}
}
이미지 프레임에 대해 RayCast의 HitPoint를 LineRenderer로 연결하여 표시하고 있습니다.
SpatialMapping에 LineRenderer를 추가하고 Width를 0.01로 설정합니다.

평소처럼 빌드하고 실행하십시오.
탭하면 촬영한 범위의 화각이 표시됩니다.
실행 동영상은 여기입니다.

손가락이 비치고 있으면 소리 등으로 알려주면 좋을지도 모릅니다.
PhotoCaptureFrame을 얻고 나서가 아니라면 카메라 Matrix를 얻을 수 없으므로 항상 촬영해야합니다 ...

그림 1 MRC로 촬영한 영상

그림 2 PhotoCapture로 촬영한 이미지
참고 기사
using HoloToolkit.Unity.InputModule;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.VR.WSA.WebCam;
public class PhotoCaptureRaw : MonoBehaviour, IInputClickHandler
{
private PhotoCapture photoCaptureObject = null;
private bool isCapturing = false;
private int Width;
private int Height;
public int lengthOfLineRenderer;
// Use this for initialization
void Start()
{
InputManager.Instance.PushFallbackInputHandler(gameObject);
}
// Update is called once per frame
void Update()
{
//if (Input.GetKeyDown("space"))
//{
// FrameDisplay();
//}
}
void /*FrameDisplay()*/IInputClickHandler.OnInputClicked(InputClickedEventData eventData)
{
Debug.Log("Tapped");
if (!isCapturing)
{
if (photoCaptureObject != null)
{
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
isCapturing = true;
}
}
void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
photoCaptureObject = captureObject;
CameraParameters c = new CameraParameters();
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
c.hologramOpacity = 0.0f;
c.cameraResolutionWidth = cameraResolution.width;
c.cameraResolutionHeight = cameraResolution.height;
c.pixelFormat = CapturePixelFormat.BGRA32;
Width = cameraResolution.width;
Height = cameraResolution.height;
Debug.Log(String.Format("width={0}, height={1}", Width, Height));
//captureObject.StartPhotoModeAsync(c, OnPhotoModeStarted);
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(c, delegate (PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
}
void CreateFrame(PhotoCaptureFrame photoCaptureFrame)
{
// ------------------------------------------------------
// make xyxrgb
//
int texWidth = Width / 8;
int texHeight = Height / 8;
int numPoints = texWidth * texHeight;
List<Vector3> points = new List<Vector3>();
List<int> indecies = new List<int>();
// ------------------------------------------------------
// pos & rot
//
Matrix4x4 cameraToWorldMatrix;
photoCaptureFrame.TryGetCameraToWorldMatrix(out cameraToWorldMatrix);
Matrix4x4 worldToCameraMatrix = cameraToWorldMatrix.inverse;
Matrix4x4 projectionMatrix;
photoCaptureFrame.TryGetProjectionMatrix(out projectionMatrix);
// 0 right
// 1 up
// 2 forward
// 3 position
// Position the canvas object slightly in front
// of the real world web camera.
#if UNITY_EDITOR
Vector3 position = cameraToWorldMatrix.GetColumn(3) + cameraToWorldMatrix.GetColumn(2);
#else
Vector3 position = cameraToWorldMatrix.GetColumn(3) - cameraToWorldMatrix.GetColumn(2);
#endif
// Rotate the canvas object so that it faces the user.
Quaternion rotation = Quaternion.LookRotation(cameraToWorldMatrix.GetColumn(2), cameraToWorldMatrix.GetColumn(1));
// ------------------------------------------------------
// pos -> ray
//
int pointCount = 0;
{
int y = texHeight - 1;
for (int x = texWidth - 1; x >= 0; --x)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int x = 0;
for (int y = texHeight - 2; y >= 1; --y)
{
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
//int i = y * texWidth + x;
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int y = 0;
for (int x = 0; x <= texWidth - 1; ++x)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
{
int x = texWidth - 1;
for (int y = 1; y < texHeight - 1; ++y)
{
//int i = y * texWidth + x;
points.Add(new Vector3(((x - 1) - (texWidth / 2)) / 200.0F,
(y - (texHeight / 2)) / 200.0F, 0.0F));
#if false
Vector3 point = points[pointCount];
#else
// SetTRS
Vector3 translation = position;
//Vector3 eulerAngles;
Vector3 scale = new Vector3(1.0F, 1.0F, 1.0F);
Matrix4x4 m = Matrix4x4.identity;
//Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
m.SetTRS(translation, rotation, scale);
Vector3 point = m.MultiplyPoint3x4(points[pointCount]);
#endif
Vector3 front = new Vector3(point.x - cameraToWorldMatrix.GetColumn(3).x,
point.y - cameraToWorldMatrix.GetColumn(3).y,
point.z - cameraToWorldMatrix.GetColumn(3).z);
RaycastHit hit;
if (Physics.Raycast(cameraToWorldMatrix.GetColumn(3), front, out hit, 50.0F))
{
points[pointCount] = hit.point;
indecies.Add(pointCount);
pointCount++;
}
else
{
points.RemoveAt(pointCount);
}
}
}
Debug.Log(pointCount);
LineRenderer lineRenderer = GetComponent<LineRenderer>();
lengthOfLineRenderer = pointCount;
lineRenderer.positionCount = pointCount;
var t = Time.time;
for (int i = 0; i < lengthOfLineRenderer; i++)
{
lineRenderer.SetPosition(i, points[i]);
}
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
photoCaptureObject.Dispose();
photoCaptureObject = null;
isCapturing = false;
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
if (result.success)
{
string filename = string.Format(@"CapturedImage{0}_n.jpg", Time.time);
string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename);
photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);
CreateFrame(photoCaptureFrame);
}
else
{
Debug.LogError("Unable to start photo mode!");
}
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
isCapturing = false;
}
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
{
if (result.success)
{
Debug.Log("Saved Photo to disk!");
}
else
{
Debug.Log("Failed to save Photo to disk");
}
}
}
Reference
이 문제에 관하여(HoloLens 카메라의 화각을 시각화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/SatoshiGachiFujimoto/items/7fc937682d99da6cfe61텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)