Page 18 - MSDN Magazine, June 2017
P. 18

image after a short while. Note that the RemoteCamera app is universal, so it can be deployed without any changes to your devel- opment PC, a smartphone, a tablet or to the Raspberry Pi. If you test the RemoteCamera app with your Windows 10 PC, you need to ensure that apps are allowed to use your camera. You configure this setting with the Settings app (Privacy/Camera). To test the app with Raspberry Pi I use a low-budget Microsoft Life Cam HD-3000. This is a USB webcam, so it’s automatically detected by Windows 10 IoT Core after I connect it to one of the four Raspberry Pi USB ports. You’ll find a full list of cameras compatible with Windows 10 IoT Coreatbit.ly/2p1ZHGD.OnceyouconnectyourwebcamtotheRasb- perry Pi, it will appear under the Devices tab of the Device Portal.
Image Processor
The ImageProcessor class calculates the brightness of the current frame in the background. To perform the background operation, I create a thread with the task-based asynchronous pattern, as shown in Figure 3.
Within the while loop, I determine image brightness, and then pass this value to listeners of the ProcessingDone event. This event is fed with an instance of the ImageProcessorEventArgs class, which has only one public property, Brightness. The processing runs until the task receives a cancellation signal. The key element of the image processing is the GetBrightness method, shown in Figure 4.
Figure 3 Calculating Brightness in the Background
I access the preview frame using GetPreviewBitmap of the CameraCapture class instance. Internally, GetPreviewBitmap uses GetPreviewFrameAsync of the MediaCapture class. There are two versions of GetPreviewFrameAsync. The first, a parameterless method, returns an instance of the VideoFrame class. In this case, you can get the actual pixel data by reading the Direct3DSurface property. The second version accepts an instance of the Video- Frame class and copies pixel data to its SoftwareBitmap property. Here, I use the second option (see the GetPreviewBitmap method of the CameraCapture class) and then access pixel data through theCopyToBuffermethodoftheSoftwareBitmapclassinstance shown in Figure 5.
I first verify that the pixel format is BGRA8, which means the image is represented with four 8-bit channels: three for the blue, green, and red colors, and one for alpha or transparency.
I first verify that the pixel format is BGRA8, which means the image is represented with four 8-bit channels: three for the blue, green, and red colors, and one for alpha or transparency. If the input bitmap has a different pixel format, I perform an appropriate conversion. Next, I copy pixel data to the byte array, whose size is determined by image height multiplied by image stride (bit.ly/2om8Ny9). I read both values from an instance of the BitmapPlaneDescrip- tion, which I obtain from the BitmapBuffer object, returned by SoftwareBitmap.LockBuffer method.
Given the byte array with pixel data, all I need to do is calculate the mean value of all the pixels. So, I iterate over the pixel buffer (see Figure 6).
Figure 5 Accessing Pixel Data
public event EventHandler<ImageProcessorEventArgs> ProcessingDone;
private void InitializeProcessingTask() {
processingCancellationTokenSource = new CancellationTokenSource();
processingTask = new Task(async () => {
while (!processingCancellationTokenSource.IsCancellationRequested) {
if (IsActive) {
var brightness = await GetBrightness();
ProcessingDone(this, new ImageProcessorEventArgs(brightness));
Task.Delay(delay).Wait(); }
}
}, processingCancellationTokenSource.Token);
}
private byte[] GetPixelBuffer(SoftwareBitmap softwareBitmap) {
// Ensure bitmap pixel format is Bgra8
if (softwareBitmap.BitmapPixelFormat != CameraCapture.BitmapPixelFormat) {
SoftwareBitmap.Convert(softwareBitmap, CameraCapture.BitmapPixelFormat); }
// Lock underlying bitmap buffer
var bitmapBuffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read);
// Use plane description to determine bitmap height
// and stride (the actual buffer width)
var planeDescription = bitmapBuffer.GetPlaneDescription(0);
var pixelBuffer = new byte[planeDescription.Height * planeDescription.Stride];
// Copy pixel data to a buffer softwareBitmap.CopyToBuffer(pixelBuffer.AsBuffer());
return pixelBuffer; }
Figure 4 The GetBrightness Method
private async Task<byte> GetBrightness() {
var brightness = new byte();
if (cameraCapture.IsPreviewActive) {
// Get current preview bitmap
var previewBitmap = await cameraCapture.GetPreviewBitmap();
// Get underlying pixel data
var pixelBuffer = GetPixelBuffer(previewBitmap);
// Process buffer to determine mean gray value (brightness)
brightness = CalculateMeanGrayValue(pixelBuffer); }
return brightness; }
14 msdn magazine
Internet of Things




















































   16   17   18   19   20