c# windows-runtime windows-8.1

c# - WinRT Extrae fotogramas de la transmisión de video



windows-runtime windows-8.1 (2)

Creo que es necesario mostrar la vista previa de los medios y manejar las diferentes excepciones posibles, aquí un ejemplo simple de cómo hacer eso,

supongamos que tiene la siguiente IU, con un elemento CaptureElement para mostrar la vista previa y un control de Image para mostrar la imagen capturada,

mc:Ignorable="d" Loaded="MainPage_OnLoaded"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <CaptureElement x:Name="PreviewElement" Width="400" Height="400" Grid.Column="0" Grid.Row="0"/> <Image x:Name="ImageElement" Width="400" Height="400" Grid.Column="1" Grid.Row="0"/> <Button Click="TakePhoto_Click" Content="Take Photo" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" /> </Grid>

En el código detrás declara un campo mediaCapture,

private MediaCapture _mediaCapture;

a continuación, en el controlador de eventos cargado de página que necesita

  • iniciar el dispositivo de captura de medios,
  • establecer los varios manejadores de excepciones posibles,
  • y comenzar la vista previa de la cámara

    private async void MainPage_OnLoaded(object sender, RoutedEventArgs e) { //Start the device try { _mediaCapture = new MediaCapture(); _mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded; _mediaCapture.Failed += MediaCapture_Failed; await _mediaCapture.InitializeAsync(); } catch (UnauthorizedAccessException ex) { (new MessageDialog("Set the permission to use the webcam")).ShowAsync(); } catch (Exception ex) { (new MessageDialog("Can''t initialize the webcam !")).ShowAsync(); } //Start the preview if (_mediaCapture != null) { try { PreviewElement.Source = _mediaCapture; await _mediaCapture.StartPreviewAsync(); } catch (Exception ex) { (new MessageDialog("Something went wrong !")).ShowAsync(); } } } private async void MediaCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => (new MessageDialog("Media capture failed")).ShowAsync()); } private async void MediaCapture_RecordLimitationExceeded(MediaCapture sender) { await _mediaCapture.StopRecordAsync(); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => (new MessageDialog("Record limitation exceeded")).ShowAsync()); }

Y finalmente aquí cómo tomar una foto correctamente, todo es asíncrono, por lo que no hay demora ni nada

private async void TakePhoto_Click(object sender, RoutedEventArgs e) { if (_mediaCapture != null) { try { ImageEncodingProperties encodingProperties = ImageEncodingProperties.CreateJpeg(); WriteableBitmap bitmap = new WriteableBitmap((int)ImageElement.Width, (int)ImageElement.Height); using (var imageStream = new InMemoryRandomAccessStream()) { await this._mediaCapture.CapturePhotoToStreamAsync(encodingProperties, imageStream); await imageStream.FlushAsync(); imageStream.Seek(0); bitmap.SetSource(imageStream); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { ImageElement.Source = bitmap; }); } } catch (Exception ex) { (new MessageDialog("Something went wrong !")).ShowAsync(); } } }

Estoy descifrando códigos de barras usando la cámara incorporada, hago esto con capElement.Source.CapturePhotoToStreamAsync para capturar fotos de la vista previa. funciona, pero congela la aplicación por un breve momento, que se siente muy torpe y con errores.

Así que me gustaría tener esto en segundo plano al menos dejando una interfaz de usuario receptiva al procesar las fotos.

Hasta ahora se me ocurrió esto para capturar la transmisión de video:

private async void ScanInBackground() { bool failedScan = true; var stream = new InMemoryRandomAccessStream(); await capElement.Source.StartRecordToStreamAsync(MediaEncodingProfile.CreateWmv(VideoEncodingQuality.HD1080p), stream); while(failedScan) { Byte[] bytes = await GetBytesFromStream(stream); //How to split the bytes into frames? Task.Delay(50); } Dispatcher.RunAsync(CoreDispatcherPriority.Low,() => StopCap()); }

y este método para obtener los bytes de la secuencia:

public static async Task<byte[]> GetBytesFromStream(IRandomAccessStream randomStream) { var reader = new DataReader(randomStream.GetInputStreamAt(0)); var bytes = new byte[randomStream.Size]; try { await reader.LoadAsync((uint)randomStream.Size); reader.ReadBytes(bytes); } catch(Exception ex) { Logger.LogExceptionAsync(ex, "GetBytesFromStream"); } return bytes; }

A partir del comentario en ScanInBackground , puede ver que no tengo ni idea de cómo dividir la secuencia en fotos / marcos.


Hay una muestra en la página de github de Microsoft que es relevante, aunque apuntan a Windows 10. Puede que esté interesado en migrar su proyecto para obtener esta funcionalidad.

GetPreviewFrame : esta muestra capturará marcos de vista previa en lugar de fotos completas. Una vez que tiene un marco de vista previa, puede leer y editar los píxeles en él.

Aquí está la parte relevante:

private async Task GetPreviewFrameAsSoftwareBitmapAsync() { // Get information about the preview var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties; // Create the video frame to request a SoftwareBitmap preview frame var videoFrame = new VideoFrame(BitmapPixelFormat.Bgra8, (int)previewProperties.Width, (int)previewProperties.Height); // Capture the preview frame using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame)) { // Collect the resulting frame SoftwareBitmap previewFrame = currentFrame.SoftwareBitmap; // Add a simple green filter effect to the SoftwareBitmap EditPixels(previewFrame); } } private unsafe void EditPixels(SoftwareBitmap bitmap) { // Effect is hard-coded to operate on BGRA8 format only if (bitmap.BitmapPixelFormat == BitmapPixelFormat.Bgra8) { // In BGRA8 format, each pixel is defined by 4 bytes const int BYTES_PER_PIXEL = 4; using (var buffer = bitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite)) using (var reference = buffer.CreateReference()) { // Get a pointer to the pixel buffer byte* data; uint capacity; ((IMemoryBufferByteAccess)reference).GetBuffer(out data, out capacity); // Get information about the BitmapBuffer var desc = buffer.GetPlaneDescription(0); // Iterate over all pixels for (uint row = 0; row < desc.Height; row++) { for (uint col = 0; col < desc.Width; col++) { // Index of the current pixel in the buffer (defined by the next 4 bytes, BGRA8) var currPixel = desc.StartIndex + desc.Stride * row + BYTES_PER_PIXEL * col; // Read the current pixel information into b,g,r channels (leave out alpha channel) var b = data[currPixel + 0]; // Blue var g = data[currPixel + 1]; // Green var r = data[currPixel + 2]; // Red // Boost the green channel, leave the other two untouched data[currPixel + 0] = b; data[currPixel + 1] = (byte)Math.Min(g + 80, 255); data[currPixel + 2] = r; } } } } }

Y declara esto fuera de tu clase:

[ComImport] [Guid("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] unsafe interface IMemoryBufferByteAccess { void GetBuffer(out byte* buffer, out uint capacity); }

Y, por supuesto, su proyecto deberá permitir que el código no sea seguro para que todo esto funcione.

Eche un vistazo más de cerca a la muestra para ver cómo obtener todos los detalles. O bien, para obtener un tutorial, puede ver la sesión de la cámara desde la // compilación / conferencia reciente, que incluye un pequeño recorrido a través de algunas muestras de cámara.