Capturing photos in a quick way (Unity, HoloLens 2)

%3CLINGO-SUB%20id%3D%22lingo-sub-1906081%22%20slang%3D%22en-US%22%3ECapturing%20photos%20in%20a%20quick%20way%20(Unity%2C%20HoloLens%202)%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1906081%22%20slang%3D%22en-US%22%3E%3CP%3EMy%20objective%20is%20to%20analyze%20the%20HoloLens'%20camera%20stream.%26nbsp%3BI've%20been%20trying%20to%20capture%20what%20the%20HoloLens%20is%20seeing%20for%20image%20processing%20but%20the%20only%20properly%20documented%20way%20I've%20found%20is%20through%20the%20library%26nbsp%3B%3CEM%3EUnityEngine.Windows.WebCam%3C%2FEM%3E.%20The%20problem%20with%20this%20library%20is%20that%20taking%20a%20photo%2C%20no%20matter%20how%20small%2C%20always%20takes%20a%20heavy%20toll%20on%20the%20device's%20performance.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI've%20looked%20up%20different%20ways%20to%20capture%20the%20camera%20stream%20from%20the%20HoloLens%20but%20the%20documentations%20are%20always%20lacking%20something.%20So%20I've%20tried%20implementing%20the%26nbsp%3B%3CEM%3EWindows.Media%3C%2FEM%3E%20library%20in%20Unity%20to%20at%20least%20take%20a%20quick%20photo%2C%20but%20it%20doesn't%20seem%20to%20work.%20Here%20is%20how%20I've%20implemented%20it%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Eusing%20Windows.Media.Capture.Core%3B%0Ausing%20Windows.Media.Devices.Core%3B%0A%0Aprivate%20MediaCapture%20_mediaCapture%3B%0A%0Aprivate%20async%20void%20StartScanTest()%0A%7B%0A%20_mediaCapture%20%3D%20new%20MediaCapture()%3B%0A%0A%20await%20_mediaCapture.InitializeAsync()%3B%0A%0A%20StartCoroutine(ScanTestUWP())%3B%0A%7D%0A%0Aprivate%20IEnumerator%20ScanTestUWP()%0A%7B%0A%20while%20(true)%0A%20%7B%0A%20%20CheckPatternUWP()%3B%0A%20%20yield%20return%20new%20WaitForSeconds(0.5f)%3B%0A%20%7D%0A%7D%0A%0Aprivate%20async%20void%20CheckPatternUWP()%0A%7B%0A%20var%20lowLagCapture%20%3D%20await%20mediaCapture.PrepareLowLagPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Bgra8))%3B%0A%20%0A%20var%20capturedPhoto%20%3D%20await%20lowLagCapture.CaptureAsync()%3B%0A%0A%20var%20softwareBitmap%20%3D%20capturedPhoto.Frame.SoftwareBitmap%3B%0A%20byte%5B%5D%20imageBytes%20%3D%20new%20byte%5B4%20*%20softwareBitmap.PixelWidth%20*%20softwareBitmap.PixelHeight%5D%3B%0A%20softwareBitmap.CopyToBuffer(imageBytes.AsBuffer())%3B%0A%20Texture2D%20photoTexture%20%3D%20new%20Texture2D(1%2C1)%3B%0A%20photoTexture.LoadImage(imageBytes)%3B%0A%0A%20await%20lowLagCapture.FinishAsync()%3B%0A%0A%20if%20(photoTexture)%0A%20%7B%0A%20%20CheckFromTexture(photoTexture)%3B%0A%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThis%20is%20all%20under%20the%26nbsp%3B%3CEM%3E%23if%20UNITY_UWP%3C%2FEM%3E%20%3CSPAN%3Epreprocessor%20directive.%3C%2FSPAN%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIs%20there%20a%20way%20to%20get%20the%20actual%20camera%20stream%20from%20the%20HoloLens%3F%20If%20not%2C%20how%20can%20I%20take%20pictures%20without%20stuttering%3F%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1906081%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EHoloLens%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EUnity%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1910477%22%20slang%3D%22en-US%22%3ERe%3A%20Capturing%20photos%20in%20a%20quick%20way%20(Unity%2C%20HoloLens%202)%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1910477%22%20slang%3D%22en-US%22%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIt%20has%20been%20a%20long%20while%20since%20I%20did%20camera%20capture%20stuff%2C%20but%20the%20MR%20Lighting%20Tools%20I%20built%20a%20while%20back%20takes%20intermittent%20camera%20grabs%20in%20a%20manner%20that%20sounds%20similar%20to%20what%20you're%20describing!%20%3CBR%20%2F%3E%3CBR%20%2F%3EYou%20can%20find%20some%20relevant%20code%20%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2Fmicrosoft%2FMRLightingTools-Unity%2Fblob%2Fmaster%2FAssets%2FMixedRealityToolkit.LightingTools%2FCameraCapture%2FCameraCaptureUWP.cs%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%22%3Eover%20in%20the%20repository%20here%3C%2FA%3E%20that%20you%20might%20find%20somewhat%20helpful%3F%20I%20believe%20it's%20a%20mix%20of%20%3CA%20href%3D%22https%3A%2F%2Fdocs.unity3d.com%2FScriptReference%2FWindows.WebCam.PhotoCapture.html%22%20target%3D%22_self%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3E%3CSPAN%20class%3D%22pl-en%22%3EPhotoCapture%3C%2FSPAN%3E%3C%2FA%3E%2C%20and%20some%20trickery%20to%20work%20with%20the%20underlying%20Windows%20Media%20code%20beneath%20that.%20Some%20parts%20may%20be%20overkill%20for%20you%20though%20%3A)%3C%2Fimg%3E%3CBR%20%2F%3E%3CBR%20%2F%3EOne%20of%20the%20things%20that%20I%20found%20was%20that%20often%2C%20a%20lot%20of%20the%20cost%20came%20from%20copying%20image%20data%20around%20and%20the%20processing%20associated%20with%20that.%20I'd%20set%20the%20camera%20resolution%20to%20the%20lowest%20possible%20value%2C%20and%20then%20I'd%20try%20my%20hardest%20to%20rely%20on%20Unity's%20methods%20for%20texture%20copy%20(like%20%3CA%20href%3D%22https%3A%2F%2Fdocs.unity3d.com%2FScriptReference%2FWindows.WebCam.PhotoCaptureFrame.UploadImageDataToTexture.html%22%20target%3D%22_self%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3EUploadImageDataToTexture%3C%2FA%3E)%2C%20since%20there's%20a%20good%20chance%20they've%20got%20some%20lower%20level%20optimizations%20in%20place.%3CBR%20%2F%3E%3CBR%20%2F%3EHope%20that's%20helpful!%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1918683%22%20slang%3D%22en-US%22%3ERe%3A%20Capturing%20photos%20in%20a%20quick%20way%20(Unity%2C%20HoloLens%202)%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1918683%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F710529%22%20target%3D%22_blank%22%3E%40koujaku%3C%2FA%3E%26nbsp%3BThank%20you%20for%20your%20answer!%20Sadly%2C%20I%20have%20tried%20using%20that%20library%20but%20taking%20a%20photo%20is%20too%20slow%20to%20be%20usable%20even%20once%20per%20second%2C%20empty%20app%20with%20just%20photo%20capturing%20staggers%20like%20it's%20about%20to%20crash.%20Maybe%20I'm%20calling%20the%20wrong%20methods%3F%20I%20initialized%20%3CEM%3ECameraCaptureUWP%3C%2FEM%3E%20and%20called%20%3CEM%3ERequestTextureAsync%3C%2FEM%3E%2C%20is%20there%20some%20other%20way%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIn%20the%20meantime%2C%20I've%20found%20a%20way%20to%20get%20a%20byte%20stream%20out%20of%20a%26nbsp%3B%3CEM%3EPhotoCaptureFrame%3C%2FEM%3E%20instance%3A%26nbsp%3B%3CEM%3ECopyRawImageDataIntoBuffer%3C%2FEM%3E.%20Once%20I%20get%20a%20byte%20stream%20I%20can%20dilute%20the%20amount%20of%20work%20needed%20to%20create%20a%20Texture2D%20over%20a%20few%20frames.%20Problem%20is%2C%20it%20seems%20that%26nbsp%3B%3CEM%3ECopyRawImageDataIntoBuffer%3C%2FEM%3E%20has%20some%20memory%20leak%20problems.%20Every%20time%20I%20call%20that%20method%20the%20available%20memory%20decreases%2C%20until%20the%20HoloLens%20crashes.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIs%20there%20another%20way%20to%20get%20the%20array%20of%20bytes%20out%20of%20the%20%3CEM%3EPhotoCaptureFrame%3C%2FEM%3E%3F%3C%2FP%3E%3C%2FLINGO-BODY%3E
New Contributor

My objective is to analyze the HoloLens' camera stream. I've been trying to capture what the HoloLens is seeing for image processing but the only properly documented way I've found is through the library UnityEngine.Windows.WebCam. The problem with this library is that taking a photo, no matter how small, always takes a heavy toll on the device's performance.

 

I've looked up different ways to capture the camera stream from the HoloLens but the documentations are always lacking something. So I've tried implementing the Windows.Media library in Unity to at least take a quick photo, but it doesn't seem to work. Here is how I've implemented it:

 

using Windows.Media.Capture.Core;
using Windows.Media.Devices.Core;

private MediaCapture _mediaCapture;

private async void StartScanTest()
{
	_mediaCapture = new MediaCapture();

	await _mediaCapture.InitializeAsync();

	StartCoroutine(ScanTestUWP());
}

private IEnumerator ScanTestUWP()
{
	while (true)
	{
		CheckPatternUWP();
		yield return new WaitForSeconds(0.5f);
	}
}

private async void CheckPatternUWP()
{
	var lowLagCapture = await mediaCapture.PrepareLowLagPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Bgra8));
	
	var capturedPhoto = await lowLagCapture.CaptureAsync();

	var softwareBitmap = capturedPhoto.Frame.SoftwareBitmap;
	byte[] imageBytes = new byte[4 * softwareBitmap.PixelWidth * softwareBitmap.PixelHeight];
	softwareBitmap.CopyToBuffer(imageBytes.AsBuffer());
	Texture2D photoTexture = new Texture2D(1,1);
	photoTexture.LoadImage(imageBytes);

	await lowLagCapture.FinishAsync();

	if (photoTexture)
	{
		CheckFromTexture(photoTexture);
	}
}

 

This is all under the #if UNITY_UWP preprocessor directive.

 

Is there a way to get the actual camera stream from the HoloLens? If not, how can I take pictures without stuttering?

2 Replies

 

It has been a long while since I did camera capture stuff, but the MR Lighting Tools I built a while back takes intermittent camera grabs in a manner that sounds similar to what you're describing!

You can find some relevant code over in the repository here that you might find somewhat helpful? I believe it's a mix of PhotoCapture, and some trickery to work with the underlying Windows Media code beneath that. Some parts may be overkill for you though :)

One of the things that I found was that often, a lot of the cost came from copying image data around and the processing associated with that. I'd set the camera resolution to the lowest possible value, and then I'd try my hardest to rely on Unity's methods for texture copy (like UploadImageDataToTexture), since there's a good chance they've got some lower level optimizations in place.

Hope that's helpful!

@koujaku Thank you for your answer! Sadly, I have tried using that library but taking a photo is too slow to be usable even once per second, empty app with just photo capturing staggers like it's about to crash. Maybe I'm calling the wrong methods? I initialized CameraCaptureUWP and called RequestTextureAsync, is there some other way?

 

In the meantime, I've found a way to get a byte stream out of a PhotoCaptureFrame instance: CopyRawImageDataIntoBuffer. Once I get a byte stream I can dilute the amount of work needed to create a Texture2D over a few frames. Problem is, it seems that CopyRawImageDataIntoBuffer has some memory leak problems. Every time I call that method the available memory decreases, until the HoloLens crashes.

 

Is there another way to get the array of bytes out of the PhotoCaptureFrame?