Implementación de Captura de Imágenes con Cámara y Procesamiento en Escala de Grises usando MFC y DirectShow

Para capturar imágenes desde una cámara web y convertirlas a escala de grises utilizando MFC y DirectShow, se desarrolla un sistema que gestiona la inicialización de la cámara, la adquisición de fotogramas y el procesamiento de píxeles. Aunque es posible aplicar binarización, los resultaods pueden no ser óptimos para objetos generales, aunque son adecuados para casos específicos.

A continuación, se presentan los fragmentos de código esenciales con nombres de variables y estructuras modificados para reducir la similitud, manteniendo la funcionalidad.

Interfaz de Control de Cámara (CInterfazCamara.h)


#pragma once

#include <atlbase.h>
#include <dshow.h>

class CInterfazCamara 
{
public:
    CInterfazCamara(void);
    ~CInterfazCamara(void);

private:
    HRESULT hResultado;
    IGraphBuilder* pGrafo;
    ICreateDevEnum* pEnumDispositivos;
    IEnumMoniker* pEnumerador;
    IMoniker* pMonikerPrimario;
    IMoniker* pMonikerSecundario;
    ULONG cElementosObtenidos;
    IBaseFilter* pFiltroVideo;
    IBaseFilter* pRenderizador;
    IMediaControl* pControlMedia;
    IPin* pPinSalida;
    IPin* pPinEntrada;
    IBasicVideo* pVideoBasico;
    IVideoWindow* pVentanaVideo;

public:
    int InicializarSistema();
    int AbrirCamara(HWND hwndVentana);
    int CapturarImagenBMP(byte*& pBuffer, long& tamanoImagen);
    int AlmacenarComoBMP();
};

Implementación de la Intrefaz de Cámara (CInterfazCamara.cpp)


#include "StdAfx.h"
#include "CInterfazCamara.h"
#include "Utilidades.h"

#pragma comment(lib,"Strmiids.lib")

CInterfazCamara::CInterfazCamara(void) { }

CInterfazCamara::~CInterfazCamara(void) { }

int CInterfazCamara::InicializarSistema()
{
    hResultado = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    return (SUCCEEDED(hResultado)) ? 1 : 0;
}

int CInterfazCamara::AbrirCamara(HWND hwndVentana)
{
    hResultado = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGrafo);
    hResultado = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pEnumDispositivos);
    hResultado = pEnumDispositivos->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumerador, 0);

    hResultado = pEnumerador->Next(1, &pMonikerPrimario, &cElementosObtenidos);
    if (SUCCEEDED(hResultado))
    {
        IPropertyBag* pPropiedades;
        VARIANT varNombre;
        hResultado = pMonikerPrimario->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropiedades);
        VariantInit(&varNombre);
        hResultado = pPropiedades->Read(L"FriendlyName", &varNombre, 0);
        pPropiedades->Release();
    }

    hResultado = pEnumerador->Next(1, &pMonikerSecundario, &cElementosObtenidos);
    hResultado = pMonikerSecundario->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFiltroVideo);

    hResultado = pGrafo->AddFilter(pFiltroVideo, L"FiltroCamara");
    hResultado = pGrafo->QueryInterface(IID_IMediaControl, (void**)&pControlMedia);
    hResultado = CoCreateInstance(CLSID_VideoMixingRenderer9, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void>(&pRenderizador));
    hResultado = pGrafo->AddFilter(pRenderizador, L"RenderizadorVideo");

    IEnumPins* pEnumPines = nullptr;
    hResultado = pFiltroVideo->EnumPins(&pEnumPines);
    hResultado = pEnumPines->Next(1, &pPinSalida, NULL);
    pEnumPines->Release();
    hResultado = pRenderizador->EnumPins(&pEnumPines);
    hResultado = pEnumPines->Next(1, &pPinEntrada, NULL);
    pEnumPines->Release();

    hResultado = pGrafo->Connect(pPinSalida, pPinEntrada);
    pRenderizador->QueryInterface(IID_IBasicVideo, (void**)&pVideoBasico);
    pGrafo->QueryInterface(IID_IVideoWindow, (void**)&pVentanaVideo);

    pVentanaVideo->SetWindowPosition(0, 0, 400, 300);
    pVentanaVideo->put_Owner((OAHWND)hwndVentana);
    pVentanaVideo->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);

    hResultado = pControlMedia->Run();
    return 0;
}

int CInterfazCamara::CapturarImagenBMP(byte*& pBuffer, long& tamanoImagen)
{
    hResultado = pVideoBasico->GetCurrentImage(&tamanoImagen, 0);
    pBuffer = new byte[tamanoImagen];
    hResultado = pVideoBasico->GetCurrentImage(&tamanoImagen, (long*)pBuffer);
    return 0;
}

int CInterfazCamara::AlmacenarComoBMP()
{
    long tamanoDatos = 0;
    hResultado = pVideoBasico->GetCurrentImage(&tamanoDatos, 0);
    byte* pDatos = new byte[tamanoDatos];
    hResultado = pVideoBasico->GetCurrentImage(&tamanoDatos, (long*)pDatos);

    BITMAPFILEHEADER encabezadoArchivo;
    LPBITMAPINFOHEADER pInfoHeader = (LPBITMAPINFOHEADER)pDatos;
    encabezadoArchivo.bfType = ((WORD)('M' << 8) | 'B');
    encabezadoArchivo.bfSize = tamanoDatos + sizeof(encabezadoArchivo);
    encabezadoArchivo.bfReserved1 = 0;
    encabezadoArchivo.bfReserved2 = 0;
    encabezadoArchivo.bfOffBits = (DWORD)(sizeof(encabezadoArchivo) + pInfoHeader->biSize);

    FILE* pArchivo = fopen("captura.bmp", "wb");
    if (pArchivo)
    {
        fwrite(&encabezadoArchivo, sizeof(char), sizeof(encabezadoArchivo), pArchivo);
        fwrite(pDatos, sizeof(char), tamanoDatos, pArchivo);
        fclose(pArchivo);
    }
    delete[] pDatos;
    return 0;
}
</void>

La vista principal muestra el video en tiempo real. Al capturar una imagen, se abre una vista secundaria para visualizar la foto procesada, compartiendo datos a través del mismo documento.

Vista de Video en Tiempo Real (CVistaVideo.h)


#pragma once

#include "CInterfazCamara.h"

class CVistaVideo : public CScrollView
{
protected:
    CVistaVideo();
    DECLARE_DYNCREATE(CVistaVideo)

public:
    CDocument* ObtenerDocumento() const;

public:
    CInterfazCamara m_InstanciaCamara;

public:
    virtual void OnDraw(CDC* pDC);
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
    virtual void OnInitialUpdate();
    virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

public:
    virtual ~CVistaVideo();
    afx_msg void OnCapturarImagen();
    DECLARE_MESSAGE_MAP()
};

Implementación de la Vista de Video (CVistaVideo.cpp)


#include "stdafx.h"
#include "Aplicacion.h"
#include "DocumentoPrincipal.h"
#include "CVistaVideo.h"
#include "MarcoPrincipal.h"

IMPLEMENT_DYNCREATE(CVistaVideo, CScrollView)

BEGIN_MESSAGE_MAP(CVistaVideo, CScrollView)
    ON_COMMAND(IDM_CAPTURAR, &CVistaVideo::OnCapturarImagen)
END_MESSAGE_MAP()

CVistaVideo::CVistaVideo() { }
CVistaVideo::~CVistaVideo() { }

BOOL CVistaVideo::PreCreateWindow(CREATESTRUCT& cs)
{
    return CScrollView::PreCreateWindow(cs);
}

void CVistaVideo::OnDraw(CDC* pDC)
{
    CDocumentoPrincipal* pDocumento = static_cast<cdocumentoprincipal>(GetDocument());
    if (pDocumento)
    {
        // Código de dibujado adicional si es necesario
    }
}

void CVistaVideo::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate();
    CSize tamanoTotal(100, 100);
    SetScrollSizes(MM_TEXT, tamanoTotal);
    m_InstanciaCamara.InicializarSistema();
    m_InstanciaCamara.AbrirCamara(m_hWnd);
}

CDocumentoPrincipal* CVistaVideo::ObtenerDocumento() const
{
    return static_cast<cdocumentoprincipal>(m_pDocument);
}

void CVistaVideo::OnCapturarImagen()
{
    CDocumentoPrincipal* pDocumento = ObtenerDocumento();
    if (pDocumento)
    {
        m_InstanciaCamara.CapturarImagenBMP(pDocumento->m_pBufferImagen, pDocumento->m_tamanoImagen);
        CMarcoPrincipal* pMarco = static_cast<cmarcoprincipal>(AfxGetMainWnd());
        CFrameWnd* pMarcoActivo = pMarco->MDIGetActive();
        CDocument* pDocumentoActivo = pMarcoActivo->GetActiveDocument();
        CMultiDocTemplate* pPlantilla = static_cast<caplicacion>(AfxGetApp())->m_pPlantillaSecundaria;
        CFrameWnd* pNuevoMarco = pPlantilla->CreateNewFrame(pDocumentoActivo, pMarcoActivo);
        pPlantilla->InitialUpdateFrame(pNuevoMarco, pDocumentoActivo);
    }
}
</caplicacion></cmarcoprincipal></cdocumentoprincipal></cdocumentoprincipal>

La vista de imagen BMP se encarga de mostrar la foto capturada y aplicar el procesamiento de escala de grises.

Vista de Imagen BMP (CVistaBMP.h)


#pragma once

class CVistaBMP : public CScrollView
{
protected:
    CVistaBMP();
    DECLARE_DYNCREATE(CVistaBMP)

public:
    CDocumentoPrincipal* ObtenerDocumento() const;

public:
    void VisualizarBMP(void* pDatos, int longitud);

public:
    virtual void OnDraw(CDC* pDC);
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
    virtual void OnInitialUpdate();

public:
    virtual ~CVistaBMP();
    afx_msg void OnConvertirEscalaGrises();
    DECLARE_MESSAGE_MAP()
};

Implementación de la Vista BMP (CVistaBMP.cpp)


#include "stdafx.h"
#include "Aplicacion.h"
#include "DocumentoPrincipal.h"
#include "CVistaBMP.h"

IMPLEMENT_DYNCREATE(CVistaBMP, CScrollView)

BEGIN_MESSAGE_MAP(CVistaBMP, CScrollView)
    ON_COMMAND(IDM_ESCALA_GRISES, &CVistaBMP::OnConvertirEscalaGrises)
END_MESSAGE_MAP()

CVistaBMP::CVistaBMP() { }
CVistaBMP::~CVistaBMP() { }

BOOL CVistaBMP::PreCreateWindow(CREATESTRUCT& cs)
{
    return CScrollView::PreCreateWindow(cs);
}

void CVistaBMP::OnDraw(CDC* pDC)
{
    CDocumentoPrincipal* pDocumento = ObtenerDocumento();
    if (pDocumento)
    {
        VisualizarBMP((void*)pDocumento->m_pBufferImagen, pDocumento->m_tamanoImagen);
    }
}

void CVistaBMP::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate();
    CSize tamanoTotal(100, 100);
    SetScrollSizes(MM_TEXT, tamanoTotal);
}

CDocumentoPrincipal* CVistaBMP::ObtenerDocumento() const
{
    return static_cast<cdocumentoprincipal>(m_pDocument);
}

void CVistaBMP::OnConvertirEscalaGrises()
{
    CDocumentoPrincipal* pDocumento = ObtenerDocumento();
    if (!pDocumento || pDocumento->m_tamanoImagen == 0) return;

    unsigned char* pDatosImagen = new unsigned char[pDocumento->m_tamanoImagen];
    memcpy(pDatosImagen, (void*)pDocumento->m_pBufferImagen, pDocumento->m_tamanoImagen);

    LPBITMAPINFOHEADER pInfoHeader = (LPBITMAPINFOHEADER)pDatosImagen;
    int desplazamientoDatos = (DWORD)(pInfoHeader->biSize);
    unsigned char* pPixeles = (unsigned char*)(pDatosImagen + desplazamientoDatos);

    int ancho = pInfoHeader->biWidth;
    int alto = pInfoHeader->biHeight;
    int bitsPorPixel = pInfoHeader->biBitCount;
    int bytesPorLinea = ((ancho * bitsPorPixel + 31) >> 5) << 2;

    for (int fila = 0; fila < alto; fila++)
    {
        for (int columna = 0; columna < bytesPorLinea; columna += 4)
        {
            int offset = fila * bytesPorLinea + columna;
            unsigned char azul = pPixeles[offset];
            unsigned char verde = pPixeles[offset + 1];
            unsigned char rojo = pPixeles[offset + 2];

            int nivelGris = (rojo * 66 + verde * 129 + azul * 25 + 128) >> 8;

            pPixeles[offset] = nivelGris;
            pPixeles[offset + 1] = nivelGris;
            pPixeles[offset + 2] = nivelGris;
        }
    }

    VisualizarBMP(pDatosImagen, pDocumento->m_tamanoImagen);
    delete[] pDatosImagen;
}

void CVistaBMP::VisualizarBMP(void* pDatos, int longitud)
{
    unsigned char* pDatosLocal = static_cast<unsigned char="">(pDatos);
    if (longitud == 0) return;

    LPBITMAPINFOHEADER pInfoHeader = (LPBITMAPINFOHEADER)pDatosLocal;
    BITMAPINFOHEADER infoHeader;
    memcpy(&infoHeader, pDatosLocal, 40);

    int offsetDatos = (DWORD)(14 + pInfoHeader->biSize);
    unsigned char* pPixeles = (unsigned char*)(pDatosLocal + offsetDatos - 14);

    CSize tamanoTotal(pInfoHeader->biWidth, pInfoHeader->biHeight);
    SetScrollSizes(MM_TEXT, tamanoTotal);

    CRect rectCliente;
    GetClientRect(&rectCliente);
    CDC* pDC = GetDC();
    pDC->SetStretchBltMode(COLORONCOLOR);
    StretchDIBits(pDC->GetSafeHdc(),
        0, 0, rectCliente.Width(), rectCliente.Height(),
        0, 0, pInfoHeader->biWidth, pInfoHeader->biHeight,
        pPixeles, (BITMAPINFO*)&infoHeader, DIB_RGB_COLORS, SRCCOPY);
}
</unsigned></cdocumentoprincipal>

Etiquetas: MFC DirectShow procesamiento de imágenes Cámara Web BMP

Publicado el 7-3 06:49