VC++ でUSB カメラを使って画像プログラムを組む必要があって ちょっと調べてみました. いろんな方法があるようですが, Video for Windows が楽そうだったので それを使うことにしました.
キーワードは capCreateCaptureWindow です.
というわけで, これを使って簡単なアプリを作る手順は
|
#include <vfw.h> #pragma comment(lib,"vfw32.lib") |
| HWND m_hWndCap; |
CWnd* pWnd = GetDlgItem(IDC_CAPWINDOW); m_hWndCap = capCreateCaptureWindow( "Captrue Window", WS_CHILD | WS_VISIBLE, 0, 0, 320, 240, pWnd->m_hWnd, 0); capDriverConnect(m_hWndCap, 0); capPreviewRate(m_hWndCap, 1); capPreview(m_hWndCap, TRUE); |
カメラ画像をファイルに保存する方法ですが, capFileSaveDIB を使えばできます. 具体的には例えば, 適当なボタンを作りそのボタンが押されたら 呼ばれる関数を作ります (HogeDlg::OnBnClickedTest). その中で
| capFileSaveDIB(m_hWndCap, "hoge.bmp"); |
得た画像に対して何か処理して表示させたいことがあります. 一番簡単な方法は保存したデータを読み込んで加工して表示する ことです.
まず, 最初に保存したデータを表示する方法ですが,
| CDC *m_pDC; |
| m_pDC=m_Draw.GetDC(); |
|
file.Open("hoge.bmp", CFile::modeRead | CFile::typeBinary); nBMPSize=file.GetLength(); BYTE *pDib = (BYTE *)new char[nBMPSize]; file.Read(pDib, nBMPSize); file.Close(); BITMAPFILEHEADER *pBMFH = (BITMAPFILEHEADER*)pDib; BITMAPINFOHEADER *pBMIH = (BITMAPINFOHEADER *)(pDib + sizeof(BITMAPFILEHEADER)); BITMAPINFO *pBMI = (BITMAPINFO *)pBMIH; BYTE *pData = pDib + pBMFH->bfOffBits; //// ここ /// ::StretchDIBits(m_pDC->GetSafeHdc(), 0, 0, pBMIH->biWidth, pBMIH->biHeight, 0, 0, pBMIH->biWidth, pBMIH->biHeight, pData, pBMI, DIB_RGB_COLORS, SRCCOPY); delete pDib; |
「ここ」と書いた所に適当な画像処理を記述できます. 例えば, 下から 3 ピクセル目に赤い線を引きたければ
int y=2; |
なお, ここでは単純のため, ボタンを押したら処理を行っていますが 例えば監視プログラムのように一定間隔で処理をしたいなら SetTimer という関数を使えばいいと思います.
また, 加工した画像を保存したい場合は delete 文の前あたりで次のように書くとよろしいようです.
|
FILE *fp=fopen("auau.bmp", "wb"); fwrite (pDib, pBMFH->bfSize, 1, fp); fclose(fp); |
上記方法は一応動くんで遊んでみる分には申し分ないんですが, やはり毎回一旦ファイルに落として読むため, 精神衛生上 好ましくありません. できたらファイルを介さずメモリ上で完結させたいものです.
そういう方法を最近見つけましたので書いておきます (と言うか見つけたのでこのページを書くことにした). ただし, あまり美しくありませんが...
BYTE *g_pBMI;
volatile bool g_bGetting;
LRESULT PASCAL FrameCallbackProc(HWND hWndCap, LPVIDEOHDR lpVHdr) {
if (!g_bGetting){
g_bGetting=true;
BITMAPINFOHEADER nFormat;
capGetVideoFormat(hWndCap, &nFormat, sizeof(BITMAPINFOHEADER));
int nBMPSize=nFormat.biSizeImage + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
if (g_pBMI) {
BITMAPFILEHEADER *pBMFH = (BITMAPFILEHEADER*)g_pBMI;
if (pBMFH->bfSize != nBMPSize){
delete [] g_pBMI;
g_pBMI=NULL;
}
}
if (!g_pBMI){
g_pBMI = (BYTE *)new char[nBMPSize];
BITMAPFILEHEADER *pBMFH = (BITMAPFILEHEADER*)g_pBMI;
pBMFH->bfType = *((WORD*)"BM");
pBMFH->bfSize = nBMPSize;
pBMFH->bfReserved1 = pBMFH->bfReserved2 = 0;
pBMFH->bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
}
BITMAPINFOHEADER *pBMIH = (BITMAPINFOHEADER *)(g_pBMI + sizeof(BITMAPFILEHEADER));
memcpy(pBMIH, &nFormat, sizeof(BITMAPINFOHEADER));
memcpy(g_pBMI + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER), lpVHdr->lpData, lpVHdr->dwBufferLength);
g_bGetting=false;
}
return TRUE ;
}
BYTE *CapDIB(){
while (g_bGetting);
g_bGetting=true;
BITMAPFILEHEADER *pBMFH = (BITMAPFILEHEADER*)g_pBMI;
BYTE *pRet = (BYTE *)new char[pBMFH->bfSize];
memcpy(pRet, g_pBMI, pBMFH->bfSize);
g_bGetting=false;
return pRet;
}
|
|
g_pBMI=NULL; g_bGetting=false; capSetCallbackOnFrame(m_hWndCap, FrameCallbackProc); |
| BYTE *pDib = CapDIB(); |
この方法も不必要にメモリコピーを繰り返しているわけで, 不効率と言えば不効率です. ファイルのセーブロードとどっちが いいかは悩みどころですが...