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>