使用USB摄像头(Webcam)拍照

时间:2021-12-02 13:31:03

你想不想使用webcam来自己拍张照片呢?
首先你要通过capCreateCaptureWindow创建Capture 窗口,使用capDriverConnect连接WebCam,调用capSetVideoFormat设置视频格式,通过capSetCallbackOnFrame设置CallBack函数。

int CWebCamCamera::StartWebCam(HWND hParent, int nWidth, int nHeight)
{
 TCHAR szName[MAX_PATH] = {0};
 TCHAR szVer[MAX_PATH]  = {0};

 BOOL bRet = capGetDriverDescription(0, szName, MAX_PATH, szVer, MAX_PATH);
 if (!bRet)
 {
  return -1;
 }

 m_hWnd = capCreateCaptureWindow(szName, WS_VISIBLE | WS_CHILD, 0, 0, nWidth, nHeight, hParent, 0);

 if (capDriverConnect(m_hWnd, 0))
 {
        capPreviewRate(m_hWnd, 66);
        capPreview(m_hWnd, TRUE);
  capPreviewScale(m_hWnd, TRUE);

        BITMAPINFO bitmapinfo;
        bitmapinfo.bmiHeader.biSize     = sizeof(bitmapinfo.bmiHeader);
        bitmapinfo.bmiHeader.biWidth    = nWidth;
        bitmapinfo.bmiHeader.biHeight   = nHeight;
        bitmapinfo.bmiHeader.biPlanes   = 1;
        bitmapinfo.bmiHeader.biBitCount = 24;

        capSetVideoFormat(m_hWnd, &bitmapinfo, sizeof(bitmapinfo));
        capSetCallbackOnFrame(m_hWnd, FrameCallbackProc);
  
  SetWindowPos(m_hWnd, NULL, 0, 0, nWidth,
    nHeight, SWP_NOZORDER | SWP_NOMOVE);
 }

 return 0;
}


这时你就可以开始Capture视频了.
获取视频帧图像数据保存为图像,可以有两种方式:
1.在CallBack函数里面获取实时帧数据。

LRESULT WINAPI CWebCamCamera::FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
    if (!hWnd) return FALSE;
 // lpVHdr->lpData, Dib bits data(after BITMAPINFOHEADER)
 // lpVHdr->dwBytesUsed, Dib size
 //Create bitmap from a frame data
 HANDLE hFile = CreateFile(L"F://Frame.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,   
                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

 BITMAPINFOHEADER bih;
    BITMAPFILEHEADER bfh;
    ZeroMemory(&bfh, sizeof(bfh));
 ZeroMemory(&bih, sizeof(bih));
 
 DWORD dwSize, dwWritten;
 dwSize = capGetVideoFormatSize(hWnd);
 capGetVideoFormat(hWnd, &bih, dwSize);

    bfh.bfType = 'MB';  // Little-endian for "MB".
    bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lpVHdr->dwBytesUsed;
 bfh.bfReserved1 = 0;   
 bfh.bfReserved2 = 0;   
 bfh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

 WriteFile(hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
 WriteFile(hFile, &bih, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
 WriteFile(hFile, lpVHdr->lpData, lpVHdr->dwBytesUsed, &dwWritten, NULL);

 CloseHandle(hFile);   
 
    return (LRESULT) TRUE ;
}

2.也可以通过capGrabFrame和capEditCopy把帧数据保存到剪贴板里,然后通过剪贴板获取图像。
然后把位图数据保存为位图文件。

  
  HBITMAP hBitmap = NULL;
  capGrabFrame(m_hWnd);
  capEditCopy(m_hWnd);

  OpenClipboard(NULL);
  hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
  CloseClipboard();

就是这么简单。封装成一个class,代码如下:

//WebCamCamera.h

#pragma once
#include <Vfw.h>

class CWebCamCamera
{
public:
 CWebCamCamera(void);
 virtual ~CWebCamCamera(void);
 int StartWebCam(HWND hParent, int nWidth, int nHeight);
 static LRESULT WINAPI FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr);
 int TakeSnapshot(IStream* pIPicture);
 int TakeSnapshot(TCHAR* szPicture);
 int CloseWebCam(void);
private:
 PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp);
 HWND m_hWndParent;
 HWND m_hWnd;
};

//WebCamCamera.cpp

#include "stdafx.h"
#include "WebCamCamera.h"

CWebCamCamera::CWebCamCamera(void)
{
 m_hWndParent = NULL;
 m_hWnd = NULL;
}

CWebCamCamera::~CWebCamCamera(void)
{
}

int CWebCamCamera::StartWebCam(HWND hParent, int nWidth, int nHeight)
{
 TCHAR szName[MAX_PATH] = {0};
 TCHAR szVer[MAX_PATH]  = {0};

 BOOL bRet = capGetDriverDescription(0, szName, MAX_PATH, szVer, MAX_PATH);
 if (!bRet)
 {
  return -1;
 }

 m_hWnd = capCreateCaptureWindow(szName, WS_VISIBLE | WS_CHILD, 0, 0, nWidth, nHeight, hParent, 0);

 if (capDriverConnect(m_hWnd, 0))
 {
        capPreviewRate(m_hWnd, 66);
        capPreview(m_hWnd, TRUE);
  capPreviewScale(m_hWnd, TRUE);

        BITMAPINFO bitmapinfo;
        bitmapinfo.bmiHeader.biSize     = sizeof(bitmapinfo.bmiHeader);
        bitmapinfo.bmiHeader.biWidth    = nWidth;
        bitmapinfo.bmiHeader.biHeight   = nHeight;
        bitmapinfo.bmiHeader.biPlanes   = 1;
        bitmapinfo.bmiHeader.biBitCount = 24;

        capSetVideoFormat(m_hWnd, &bitmapinfo, sizeof(bitmapinfo));
        capSetCallbackOnFrame(m_hWnd, FrameCallbackProc);
  
  SetWindowPos(m_hWnd, NULL, 0, 0, nWidth,
    nHeight, SWP_NOZORDER | SWP_NOMOVE);
 }

 return 0;
}

int CWebCamCamera::CloseWebCam(void)
{
 if (m_hWnd)
 {
  capDriverDisconnect(m_hWnd);

  m_hWnd = NULL;
 }

 return 0;
}
LRESULT WINAPI CWebCamCamera::FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
    if (!hWnd) return FALSE;
 // lpVHdr->lpData, Dib bits data(after BITMAPINFOHEADER)
 // lpVHdr->dwBytesUsed, Dib size
 /* Create bitmap from a frame data
 HANDLE hFile = CreateFile(L"F://Frame.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,   
                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

 BITMAPINFOHEADER bih;
    BITMAPFILEHEADER bfh;
    ZeroMemory(&bfh, sizeof(bfh));
 ZeroMemory(&bih, sizeof(bih));
 
 DWORD dwSize, dwWritten;
 dwSize = capGetVideoFormatSize(hWnd);
 capGetVideoFormat(hWnd, &bih, dwSize);

    bfh.bfType = 'MB';  // Little-endian for "MB".
    bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lpVHdr->dwBytesUsed;
 bfh.bfReserved1 = 0;   
 bfh.bfReserved2 = 0;   
 bfh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

 WriteFile(hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
 WriteFile(hFile, &bih, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
 WriteFile(hFile, lpVHdr->lpData, lpVHdr->dwBytesUsed, &dwWritten, NULL);

 CloseHandle(hFile);  
 */
    return (LRESULT) TRUE ;
}

int CWebCamCamera::TakeSnapshot(IStream* pIPicture)
{
 if (pIPicture == NULL)
 {
  return -1;
 }
  
 HBITMAP hBitmap = NULL;

 if (m_hWnd)
 {
  capGrabFrame(m_hWnd);
  capEditCopy(m_hWnd);

  OpenClipboard(NULL);
  hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
  CloseClipboard();

  static BOOL bShow = TRUE;
  if (bShow)
  {
   ShowWindow(m_hWnd, SW_HIDE);
   bShow = FALSE;
  }
  else
  {
   ShowWindow(m_hWnd, SW_SHOW);
   bShow = TRUE;
  }

  capPreview(m_hWnd, TRUE);
 }

 if (hBitmap)
 {
  LARGE_INTEGER liZero;
  ZeroMemory(&liZero, sizeof(liZero));

  BITMAPINFO bmi;
        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
  ZeroMemory(&bmi, sizeof(bmi));
  
  DWORD dwSize;
  dwSize = capGetVideoFormatSize(m_hWnd);
  capGetVideoFormat(m_hWnd,&bmi,dwSize);

  LPBITMAPINFOHEADER pbih = (LPBITMAPINFOHEADER)&bmi;

  LPBYTE lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

  if (!lpBits) return -2;
  
  HDC hDC = GetDC(NULL);

  if (!GetDIBits(hDC, hBitmap, 0, (WORD)pbih->biHeight, lpBits, (LPBITMAPINFO)&bmi,
   DIB_RGB_COLORS))
  {
   return -3;
  }

        bfh.bfType = 'MB';  // Little-endian for "MB".
        bfh.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
                 pbih->biSize + pbih->biClrUsed
                 * sizeof(RGBQUAD) + pbih->biSizeImage);
        bfh.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
                    pbih->biSize + pbih->biClrUsed
                    * sizeof (RGBQUAD);

  pIPicture->Seek(liZero, STREAM_SEEK_SET, NULL);

  pIPicture->Write(&bfh, sizeof(bfh), NULL);
  pIPicture->Write((LPVOID)pbih, sizeof(BITMAPINFOHEADER)
                  + pbih->biClrUsed * sizeof (RGBQUAD), NULL);
  pIPicture->Write(lpBits, pbih->biSizeImage, NULL);

  GlobalFree((HGLOBAL)lpBits);
 }

 return 0;
}

int CWebCamCamera::TakeSnapshot(TCHAR* szPicture)
{
 if ( NULL == szPicture )
 {
  return -1;
 }
  
 HBITMAP hBitmap = NULL;

 if ( m_hWnd )
 {
  capGrabFrame(m_hWnd);
  capEditCopy(m_hWnd);

  OpenClipboard(NULL);
  hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
  CloseClipboard();

  static BOOL bShow = TRUE;
  if (bShow)
  {
   ShowWindow(m_hWnd, SW_HIDE);
   bShow = FALSE;
  }
  else
  {
   ShowWindow(m_hWnd, SW_SHOW);
   bShow = TRUE;
  }

  capPreview(m_hWnd, TRUE);
 }

 if ( hBitmap )
 {
  HANDLE hFile = CreateFile(szPicture, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,   
         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

  LARGE_INTEGER liZero;
  ZeroMemory(&liZero, sizeof(liZero));

  BITMAPINFO bmi;
        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
  ZeroMemory(&bmi, sizeof(bmi));
  
  DWORD dwSize;
  dwSize = capGetVideoFormatSize(m_hWnd);
  capGetVideoFormat(m_hWnd,&bmi,dwSize);

  LPBITMAPINFOHEADER pbih = (LPBITMAPINFOHEADER)&bmi;

  LPBYTE lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

  if (!lpBits) return -2;
  
  HDC hDC = GetDC(NULL);

  if (!GetDIBits(hDC, hBitmap, 0, (WORD)pbih->biHeight, lpBits, (LPBITMAPINFO)&bmi,
   DIB_RGB_COLORS))
  {
   return -3;
  }

        bfh.bfType = 'MB';  // Little-endian for "MB".
        bfh.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
                 pbih->biSize + pbih->biClrUsed
                 * sizeof(RGBQUAD) + pbih->biSizeImage);
        bfh.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
                    pbih->biSize + pbih->biClrUsed
                    * sizeof (RGBQUAD);

  WriteFile(hFile, &bfh, sizeof(bfh), &dwWritten, NULL);
  WriteFile(hFile, (LPVOID)pbih, sizeof(BITMAPINFOHEADER)+ pbih->biClrUsed * sizeof (RGBQUAD), &dwWritten, NULL);
  WriteFile(hFile, lpBits, pbih->biSizeImage, &dwWritten, NULL);

  GlobalFree((HGLOBAL)lpBits);

  if ( hFile )
  {
   CloseHandle(hFile);  
  }
 }
 else
 {
  return -4;
 }

 return 0;
}
PBITMAPINFO CWebCamCamera::CreateBitmapInfoStruct(HBITMAP hBmp)
{
    BITMAP bmp;
    PBITMAPINFO pbmi;
    WORD    cClrBits;

    // Retrieve the bitmap color format, width, and height.
    if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
     return NULL;

    // Convert the color format to a count of bits.
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
    if (cClrBits == 1)
        cClrBits = 1;
    else if (cClrBits <= 4)
        cClrBits = 4;
    else if (cClrBits <= 8)
        cClrBits = 8;
    else if (cClrBits <= 16)
        cClrBits = 16;
    else if (cClrBits <= 24)
        cClrBits = 24;
    else cClrBits = 32;

    // Allocate memory for the BITMAPINFO structure. (This structure
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
    // data structures.)

     if (cClrBits != 24)
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
                    sizeof(BITMAPINFOHEADER) +
                    sizeof(RGBQUAD) * (1<< cClrBits));

     // There is no RGBQUAD array for the 24-bit-per-pixel format.

     else
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
                    sizeof(BITMAPINFOHEADER));

    // Initialize the fields in the BITMAPINFO structure.

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (cClrBits < 24)
        pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

    // If the bitmap is not compressed, set the BI_RGB flag.
    pbmi->bmiHeader.biCompression = BI_RGB;

    // Compute the number of bytes in the array of color
    // indices and store the result in biSizeImage.
    // For Windows NT, the width must be DWORD aligned unless
    // the bitmap is RLE compressed. This example shows this.
    // For Windows 95/98/Me, the width must be WORD aligned unless the
    // bitmap is RLE compressed.
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
                                  * pbmi->bmiHeader.biHeight;
    // Set biClrImportant to 0, indicating that all of the
    // device colors are important.
     pbmi->bmiHeader.biClrImportant = 0;
     return pbmi;
 }