Получение изображения с USB камеры


Здесь описан простейший алгоритм с использованием древнего API Video for Windows. Основной недостаток метода - невозможность прямыми путями выбрать источник записи (если камер несколько), так как это реализовано через вызов диалога. То же и про настройки камеры - всё в диалогах. Ниже дан исходный текст потока, который занимается захватом кадров и преобразованием их в jpeg. Поскольку это VFW, может использоваться любое устройство, не только usb. Кусок взят из работающего проекта и отредактирован по памяти, так что маленькие косяки допустимы.

#include <jpeg.hpp>
#include <vfw.h>
#include "math.h"
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#define MAX_FRAME 4194304
HWND capwnd=NULL;                // окно захвата. без него в vfw никак
LPVIDEOHDR vhr;                  // информация о полученном кадре
HIC hic=NULL;                    // декомпрессор, нужен если кадр получаем не в формате DIB
bool grabbed;
//---------------------------------------------------------------------------
// это callback, где мы получим от драйвера готовый кадр
LRESULT __stdcall CALLBACK FrameCallback(HWND wnd, LPVIDEOHDR hdr)
{
vhr=hdr;
grabbed=true;
return true;
}
//---------------------------------------------------------------------------
void __fastcall TGetThread::Execute()
{
int n;
int jpgsize;
int capstate=0;                   // захват: 0=надо запустить, 1=запущено
int lastcount=0;
int lastsize;

BITMAPINFO bmi, bmo;
BITMAPFILEHEADER bfh;             // заголовок bmp
CAPDRIVERCAPS caps;

jpg=new TJPEGImage();
stream1=new TMemoryStream();
stream1->Position=0;
stream1->SetSize(MAX_FRAME);
gbm=new Graphics::TBitmap;
buf0=new byte[MAX_FRAME];

capwnd=capCreateCaptureWindow('capture',WS_CHILD/*|WS_VISIBLE*/,0,0,640,480,Form1->Handle,RandomRange(100,1000000));

Sleep(200);
while (!Terminated)
  {
  Sleep(2);

  if (capstate==0) // инициализируем захват
    {
    CloseCap();
    if (Terminated) break;
    if (capDriverConnect(capwnd, 0)) // цифра от 0 до 9 - номер драйвера видеозахвата
      {
      n=0;
      while (n==0) // ждем готовность драйвера
        {
        if (Terminated) break;
        Sleep(10);
        capDriverGetCaps(capwnd, &caps, sizeof(CAPDRIVERCAPS));
        n=caps.fCaptureInitialized;
        }
      capSetCallbackOnFrame(capwnd, FrameCallback);
      capGetVideoFormat(capwnd, &bmi, sizeof(BITMAPINFO));
      bmi.bmiHeader.biWidth=640;  
      bmi.bmiHeader.biHeight=480;
      bmi.bmiHeader.biSizeImage=bmi.bmiHeader.biWidth*bmi.bmiHeader.biHeight*3;
      bmi.bmiHeader.biCompression=BI_RGB;
      capSetVideoFormat(capwnd, &bmi, sizeof(BITMAPINFO));  // попытаемся установить интересующий нас формат кадра
      capGetVideoFormat(capwnd, &bmi, sizeof(BITMAPINFO));
      n=bmi.bmiHeader.biWidth*bmi.bmiHeader.biHeight*3;
      if (bmi.bmiHeader.biCompression!=BI_RGB)     // если это не DIB, готовим декомпрессию
        {
        hic=ICOpen(mmioFOURCC('V','I','D','C'),bmi.bmiHeader.biCompression,ICMODE_DECOMPRESS);
        ICDecompressGetFormat(hic,&(bmi.bmiHeader),&(bmo.bmiHeader));
        ICDecompressBegin(hic,&(bmi.bmiHeader),&(bmo.bmiHeader));
        }
      bfh.bfType='B'+('M'<<8);                    // заголовок bmp файла
      bfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
      bfh.bfSize=n+bfh.bfOffBits;
      bfh.bfReserved1=0;
      bfh.bfReserved2=0;
      lastcount=0;
      lastsize=0;
      capstate=1;  // готов к работе!
      }
    else
      {
      Sleep(3000);
      }
    }

  else if (capstate==1 && lastcount<50)  // получаем и обрабатываем кадр
    {
    grabbed=false;
    capGrabFrame(capwnd);
    while (!grabbed && !Terminated) Sleep(2); // здесь зависнем, если камера изначально отсутствует
    if (Terminated) break;                    // ErrorCallback не помогает
    try
      {
      stream1->Size=0;
      stream1->Position=0;
      stream1->Write(&bfh, sizeof(BITMAPFILEHEADER));
      if (hic==NULL)
        {
        stream1->Write(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
        stream1->Write(vhr->lpData, vhr->dwBytesUsed);
        }
      else  // сначала надо декомпрессировать кадр
        {
        stream1->Write(&bmo.bmiHeader, sizeof(BITMAPINFOHEADER));
        ICDecompress(hic, 0, &(bmi.bmiHeader), vhr->lpData, &(bmo.bmiHeader), buf0);
        stream1->Write(buf0, bmo.bmiHeader.biWidth*bmo.bmiHeader.biHeight*3);
        } 
      stream1->Position=0;  // теперь в stream1 полноценный bmp, можно его сохранить на диск
      stream1->SaveToFile("c:\\001.bmp");
      stream1->Position=0;
      gbm->LoadFromStream(stream1);  // мне нужен jpeg
      jpg->CompressionQuality=80;    // 1...100
      jpg->Assign(gbm);              // далее идут легкие извращения
      stream1->Position=0;
      stream1->Size=0;
      jpg->SaveToStream(stream1);
      stream1->Position=0;
      jpgsize=stream1->Read(buf0, MAX_FRAME);
      if (lastsize==jpgsize) lastcount++;    // если камеру отключить, драйвер нам об этом не скажет
      else {lastsize=jpgsize; lastcount=0;}  // поэтому вычисляем зависон косвенно, по большому к-ву одинаковых кадров
      }
    catch(...)
      {
      }
    }

  else  // 50 одинаковых по размеру кадров пришли подряд: йа завис
    {
    jpgsize=0;
    capstate=0;
    }

  }
// здесь мы окажемся, когда пора закрываться
CloseCap();
delete jpg;
delete stream1;
delete gbm;
delete [] buf0;
}
//---------------------------------------------------------------------------
void __fastcall TGetThread::CloseCap(void)
{
capSetCallbackOnFrame(capwnd, NULL);
Sleep(200);
capDriverDisconnect(capwnd);
if (hic!=NULL)
  {
  ICDecompressEnd(hic);
  ICClose(hic);
  }
Sleep(50);
}
//---------------------------------------------------------------------------


В начало


Сайт управляется системой uCoz