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(); |
この方法も不必要にメモリコピーを繰り返しているわけで, 不効率と言えば不効率です. ファイルのセーブロードとどっちが いいかは悩みどころですが...