Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se explica cómo usar la clase SoftwareBitmap, que usa muchas API diferentes de Windows Runtime para representar imágenes, con la Biblioteca De Computer Vision de código abierto (OpenCV), una biblioteca de código abierto y nativa que proporciona una amplia variedad de algoritmos de procesamiento de imágenes.
Los ejemplos de este artículo le guían a través de la creación de un componente nativo de Windows Runtime que se puede usar desde una aplicación para UWP, incluidas las aplicaciones que se crean mediante C#. Este componente auxiliar expondrá un único método, desenfoque, que utilizará la función de desenfoque para procesamiento de imágenes de OpenCV. El componente implementa métodos privados que obtienen un puntero al búfer de datos de imagen subyacente que la biblioteca openCV puede usar directamente, lo que facilita la extensión del componente auxiliar para implementar otras características de procesamiento de OpenCV.
- Para obtener una introducción al uso de SoftwareBitmap, consulte Crear, editar y guardar imágenes de mapa de bits.
- Para obtener información sobre cómo usar la biblioteca OpenCV, vaya a https://opencv.org.
- Para ver cómo usar el componente auxiliar de OpenCV que se muestra en este artículo con MediaFrameReader para implementar el procesamiento de imágenes en tiempo real de fotogramas desde una cámara, consulte Usar OpenCV con MediaFrameReader.
- Para obtener un ejemplo de código completo que implemente algunos efectos diferentes, vea el Fotogramas de cámara + OpenCV Sample en el repositorio de GitHub de ejemplos universales de Windows.
Nota:
La técnica usada por el componente OpenCVHelper, descrita en detalle en este artículo, requiere que los datos de imagen que se procesen residen en la memoria de CPU, no en la memoria de GPU. Por lo tanto, para las API que permiten solicitar la ubicación de memoria de las imágenes, como la clase MediaCapture, debe especificar la memoria de CPU.
Crear un componente auxiliar de Windows Runtime para la interoperabilidad de OpenCV
1. Agregar un nuevo proyecto de componente de Windows Runtime de código nativo a la solución
- Agregue un nuevo proyecto a la solución en Visual Studio haciendo clic con el botón derecho en la solución en el Explorador de soluciones y seleccionando Agregar>Nuevo proyecto.
- En la categoría
Visual C++, seleccione Componente de Windows Runtime (Windows universal) . En este ejemplo, asigne al proyecto el nombre "OpenCVBridge" y haga clic en Aceptar. - En el cuadro de diálogo Nuevo proyecto universal de Windows, seleccione la versión del sistema operativo de destino y mínima para su aplicación y haga clic en Aceptar.
- Haga clic con el botón derecho en el archivo generado automáticamente Class1.cpp en el Explorador de soluciones y seleccione Quitar, cuando aparezca el cuadro de diálogo de confirmación, elija Eliminar. A continuación, elimine el archivo de encabezado Class1.h.
- Haga clic con el botón derecho en el icono del proyecto OpenCVBridge y seleccione Agregar->Clase.... En el cuadro de diálogo Agregar Clase, escriba "OpenCVHelper" en el campo Nombre de Clase y, a continuación, haga clic en Aceptar. El código se agregará a los archivos de clase creados en un paso posterior.
2. Agregar los paquetes NuGet de OpenCV al proyecto de componente
- Haga clic con el botón derecho en el icono del proyecto OpenCVBridge en el Explorador de soluciones y seleccione Administrar paquetes NuGet...
- Cuando se abra el cuadro de diálogo del Administrador de paquetes NuGet, seleccione la pestaña Examinar y escriba "OpenCV.Win" en el cuadro de búsqueda.
- Seleccione "OpenCV.Win.Core" y haga clic en Instalar. En el cuadro de diálogo vista previa de
, haga clic en Aceptar . - Use el mismo procedimiento para instalar el paquete "OpenCV.Win.ImgProc".
Nota:
OpenCV.Win.Core y OpenCV.Win.ImgProc no se actualizan periódicamente y no pasan las comprobaciones de cumplimiento de la Tienda, por lo que estos paquetes están diseñados solo para experimentación.
3. Implementar la clase OpenCVHelper
Pegue el código siguiente en el archivo de encabezado OpenCVHelper.h. Este código incluye archivos de encabezado openCV para el de core de
#pragma once
// OpenCVHelper.h
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
namespace OpenCVBridge
{
public ref class OpenCVHelper sealed
{
public:
OpenCVHelper() {}
void Blur(
Windows::Graphics::Imaging::SoftwareBitmap^ input,
Windows::Graphics::Imaging::SoftwareBitmap^ output);
private:
// helper functions for getting a cv::Mat from SoftwareBitmap
bool TryConvert(Windows::Graphics::Imaging::SoftwareBitmap^ from, cv::Mat& convertedMat);
bool GetPointerToPixelData(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap,
unsigned char** pPixelData, unsigned int* capacity);
};
}
Elimine el contenido existente del archivo OpenCVHelper.cpp y agregue las siguientes directivas de inclusión.
#include "pch.h"
#include "OpenCVHelper.h"
#include "MemoryBuffer.h"
Después de las directivas include, añada los siguientes usando directivas.
using namespace OpenCVBridge;
using namespace Platform;
using namespace Windows::Graphics::Imaging;
using namespace Windows::Foundation;
using namespace Microsoft::WRL;
using namespace cv;
A continuación, agregue el método getPointerToPixelData a OpenCVHelper.cpp. Este método toma un SoftwareBitmap y, a través de una serie de conversiones, obtiene una representación de interfaz COM de los datos de píxeles a través de los cuales podemos obtener un puntero al búfer de datos subyacente en forma de matriz de char.
En primer lugar, una BitmapBuffer que contiene los datos de píxeles se obtiene llamando a LockBuffer, solicitando un búfer de lectura y escritura para que la biblioteca OpenCV pueda modificar esos datos de píxeles.
CreateReference se llama para obtener el objeto IMemoryBufferReference. A continuación, la interfaz IMemoryBufferByteAccess
bool OpenCVHelper::GetPointerToPixelData(SoftwareBitmap^ bitmap, unsigned char** pPixelData, unsigned int* capacity)
{
BitmapBuffer^ bmpBuffer = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite);
IMemoryBufferReference^ reference = bmpBuffer->CreateReference();
ComPtr<IMemoryBufferByteAccess> pBufferByteAccess;
if ((reinterpret_cast<IInspectable*>(reference)->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess))) != S_OK)
{
return false;
}
if (pBufferByteAccess->GetBuffer(pPixelData, capacity) != S_OK)
{
return false;
}
return true;
}
A continuación, agregue el método TryConvert que se muestra a continuación. Este método toma un SoftwareBitmap e intenta convertirlo en un objeto Mat, que es la matriz que utiliza OpenCV para representar los búferes de datos de imagen. Este método llama al método GetPointerToPixelData definido anteriormente para obtener una matriz de caracteres char que representa el búfer de datos de píxeles. Si esto se realiza correctamente, se llama al constructor de la clase Mat, pasando el ancho y el alto de píxeles obtenidos del objeto de origen SoftwareBitmap.
Nota:
En este ejemplo se especifica la constante CV_8UC4 como formato de píxel para el objeto mat
Se devuelve una copia superficial del objeto Mat creado desde el método para que el procesamiento adicional funcione en el mismo búfer de datos de píxeles al que hace referencia el SoftwareBitmap y no en una copia de este búfer.
bool OpenCVHelper::TryConvert(SoftwareBitmap^ from, Mat& convertedMat)
{
unsigned char* pPixels = nullptr;
unsigned int capacity = 0;
if (!GetPointerToPixelData(from, &pPixels, &capacity))
{
return false;
}
Mat mat(from->PixelHeight,
from->PixelWidth,
CV_8UC4, // assume input SoftwareBitmap is BGRA8
(void*)pPixels);
// shallow copy because we want convertedMat.data = pPixels
// don't use .copyTo or .clone
convertedMat = mat;
return true;
}
Por último, esta clase auxiliar de ejemplo implementa un único método de procesamiento de imágenes, Desenfoque, que simplemente usa el método tryConvert definido anteriormente para recuperar un objeto Mat que representa el mapa de bits de origen y el mapa de bits de destino para la operación de desenfoque y, a continuación, llama al método desenfoque de la biblioteca OpenCV ImgProc. El otro parámetro para desenfoque especifica el tamaño del efecto de desenfoque en las direcciones X e Y.
void OpenCVHelper::Blur(SoftwareBitmap^ input, SoftwareBitmap^ output)
{
Mat inputMat, outputMat;
if (!(TryConvert(input, inputMat) && TryConvert(output, outputMat)))
{
return;
}
blur(inputMat, outputMat, cv::Size(15, 15));
}
Ejemplo sencillo de OpenCV de SoftwareBitmap mediante el componente auxiliar
Ahora que se ha creado el componente OpenCVBridge, podemos crear una aplicación de C# sencilla que use el método OpenCV desenfoque para modificar un SoftwareBitmap. Para acceder al componente de Windows Runtime desde la aplicación para UWP, primero debes agregar una referencia al componente. En el Explorador de soluciones, haga clic con el botón derecho en el nodo Referencias en el proyecto de aplicación para UWP y seleccione Agregar referencia.... En el cuadro de diálogo Administrador de referencias, seleccione Projects->Solution. Active la casilla situada junto al proyecto OpenCVBridge y haga clic en Aceptar.
El código de ejemplo siguiente permite al usuario seleccionar un archivo de imagen y, a continuación, usa bitmapDecoder para crear una SoftwareBitmap representación de la imagen. Para obtener más información sobre cómo trabajar con SoftwareBitmap, vea Crear, editar y guardar imágenes de mapa de bits.
Como se explicó anteriormente en este artículo, la clase OpenCVHelper requiere que todas las imágenes proporcionadas SoftwareBitmap se codifiquen con el formato de píxel BGRA8 con valores alfa premultipados, por lo que si la imagen aún no está en este formato, el código de ejemplo llama a Convertir para convertir la imagen en el formato esperado.
A continuación, se crea un SoftwareBitmap que se usará como destino de la operación de desenfoque. Las propiedades de la imagen de entrada se usan como argumentos para el constructor para crear un mapa de bits con formato coincidente.
Se crea una nueva instancia de OpenCVHelper y se invoca el método Blur, pasando los mapas de bits de origen y de destino. Por último, se crea un SoftwareBitmapSource para asignar la imagen de salida a un control XAML Image.
Este código de ejemplo usa las APIs de los siguientes espacios de nombres, además de los espacios de nombres incluidos en la plantilla de proyecto predeterminada.
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
var inputFile = await fileOpenPicker.PickSingleFileAsync();
if (inputFile == null)
{
// The user cancelled the picking operation
return;
}
SoftwareBitmap inputBitmap;
using (IRandomAccessStream stream = await inputFile.OpenAsync(FileAccessMode.Read))
{
// Create the decoder from the stream
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
// Get the SoftwareBitmap representation of the file
inputBitmap = await decoder.GetSoftwareBitmapAsync();
}
if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8
|| inputBitmap.BitmapAlphaMode != BitmapAlphaMode.Premultiplied)
{
inputBitmap = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
SoftwareBitmap outputBitmap = new SoftwareBitmap(inputBitmap.BitmapPixelFormat, inputBitmap.PixelWidth, inputBitmap.PixelHeight, BitmapAlphaMode.Premultiplied);
var helper = new OpenCVBridge.OpenCVHelper();
helper.Blur(inputBitmap, outputBitmap);
var bitmapSource = new SoftwareBitmapSource();
await bitmapSource.SetBitmapAsync(outputBitmap);
imageControl.Source = bitmapSource;
Temas relacionados