OpenCV en GUI Win32

Si estamos desarrollando una aplicación para Windows y trabajamos con el Windows API también llamado Win32, necesitaremos realizar conversiones para manejar las imágenes con opencv y mostrarlas en la interfaz win32, utilizaremos D2D (Direct2d) y WIC (Windows Imaging Component) para lograr esta interoperabilidad.

Para este proyecto asumimos que el lector ya conoce por lo menos los aspectos básicos para trabajar con una aplicación de ventana win32, puedes ver Creación de una Ventana Win32 para obtener algo de ayuda.

Una vez tenemos la ventana procedemos a manejar el mensaje WM_CREATE en donde inicializamos los componentes D2D y WIC.

if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFactory)))) return -1;

if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory))) return -1;

También en este mensaje creamos un control STATIC que usaremos para mostrar la captura de la webcam, creamos este control como lo hacemos de manera habitual para crear cualquier control, aprovechamos también para crear un Timer este lanzara el mensaje WM_TIMER cada 30 ms, podemos cambiar este valor según la velocidad de captura de la webcam usada.

m_RenderTarget = CreateWindow(_T("STATIC"), 
 _T("Video Visor"),
 WS_CHILD | WS_VISIBLE,
 10, 10, 480, 320,
 m_hwnd, NULL,
 reinterpret_cast<LPCREATESTRUCT>(lParam)->hInstance,
 NULL);

SetTimer(m_hwnd, NULL, 30, NULL);

Con esto cada 30 ms se enviara el mensaje WM_TIMER en donde iniciaremos la webcam y obtendremos la captura correspondiente como lo hemos hecho en otros proyectos opencv, puedes ver Acceder a la Webcam con Opencv.

if (!videoCap.isOpened()) videoCap.open(0);

cv::Mat frame;
videoCap >> frame;

if (!frame.empty() && SUCCEEDED(CreateGraphicsResources()))
{
 ID2D1Bitmap *pBitmap = MatToIWICImage(frame, pRenderTarget);
 OnPaint(pRenderTarget, pBitmap);
 SafeRelease(&pBitmap);
}

La función CreateGraphicsResources es la encargada de crear los dispositivos graficas donde D2D y WIC dibujaran la imagen, esta función inicia el dispositivo grafico sobre el control STATIC creado anteriormente, aquí mostraremos el video. 

RECT rc1;
GetClientRect(m_RenderTarget, &rc1);

D2D1_SIZE_U size1 = D2D1::SizeU(rc1.right, rc1.bottom);

hr = pFactory->CreateHwndRenderTarget(
 D2D1::RenderTargetProperties(),
 D2D1::HwndRenderTargetProperties(m_RenderTarget, size1),
 &pRenderTarget);

No podemos mostrar directamente un objeto cv::Mat directamente en D2D, aquí es en donde entra WIC el cual usaremos para convertir cv::Mat a un objeto IWICBitmap luego este será convertido en un ID2D1Bitmap que dibujaremos sobre el dispositivo que acabamos de crear.

La función MatToIWICImage hará la conversión, prestaremos atención solo al fragmento de código que realiza la conversión lo demás es código que corresponde a como mostrar un imagen con WIC en D2D que asumimos el lector ya debe conocer.

hr = pImgFactory->CreateBitmapFromMemory(
 imagen.cols,                                   //Cantidad de columnas
 imagen.rows,                                   //Cantidad de filas
 GUID_WICPixelFormat24bppBGR,                   //Formato de pixel de la imagen
 imagen.step,                                   //Bits por filas
 imagen.cols * imagen.rows * imagen.channels(), //Tamano del buffer
 static_cast<BYTE*>(imagen.data),               //Datos que forman la imagen 
 &pWBitmap                                      //Bitmap WIC de salida
 );

Si imagen es un objeto cv::Mat de tipo CV_8UC3 la conversión se hará correctamente, para usar otro tipo de matriz opencv, por ejemplo, una imagen en escala de grises el tipo seria CV_8UC1 por lo que habría que cambiar el parámetro GUID_WICPixelFormat24bppBGR  por GUID_WICPixelFormat8bppGray existen diversos tipos que corresponden a los tipos opencv solo debemos buscar el apropiado.

Con esto solo resta dibujar el Bitmap obtenido, lo hacemos como lo haríamos con cualquier dibujo en D2D. 

target->BeginDraw();
target->Clear(D2D1::ColorF(D2D1::ColorF::SkyBlue));

if (bitmap)
{
 D2D1_SIZE_F rtSize = target->GetSize();
 D2D1_RECT_F rectangle = D2D1::RectF(0.0f, 0.0f, rtSize.width, rtSize.height);
 target->DrawBitmap(bitmap, rectangle);
}

hr = target->EndDraw();

Ahora estamos en capacidad de crear una interfaz grafica win32 que se interoperable con opencv.

Para poder ejecutar este proyecto necesitas configurar opencv para ejecutar en una aplicación win32 de ventana  no una de consola los paso son los mismo que en Configurar OpenCV para Visual Studio solo que en lugar de seleccionar un proyecto tipo consola seleccionamos Proyecto Win32 de ventana.

Para facilidad de uso en el archivo descargable se proporciona el archivo CMakeLists.txt para construir el proyecto con CMake, una vez lo hemos construido lo abrimos y vamos a las propiedades del proyecto y hacemos el siguiente cambio:

opencv gui win32

Comentarios

Publicar un comentario

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API