Webカメラで取得した画像を画面に表示したい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ZEROXE
記事: 5
登録日時: 12年前

Webカメラで取得した画像を画面に表示したい

#1

投稿記事 by ZEROXE » 9年前

1.2.こんにちは、何回かこの質問サイトを利用させていただいている者です。
 前回、このサイトでBMPの表示方法を質問させていただきました。
 そのプログラムをいじり、画像処理を色々とやってみて面白いと思いまして、
 最近あまり用途がないWebカメラを使って画像処理ができないか調べていたところ、
 幾つかサイトがヒットしました。
 それらのサイトを調べたところ、VFW(Video For Windows)を使うのが手っ取り早そう
 ということで、今回Webカメラを使い、WindowsAPI上でリアルタイム映像を表示する
 プログラムをサイトを見ながら少し改良しつつやってみたのですが、自己解決できそうにない
 エラーがでているので、質問させていただきます。

3.プログラムを実行すると、以下のエラー文がでてきます。
指定されたファイルが見つかりません。
カメラ制御.exe の 0x77c3f9f2 で初回の例外が発生しました: 0xC0000008: An invalid handle was specified

4.一応、このプログラムを実行し、エラーを無視して実行するか、
 デバックなしで実行をするとWebカメラの映像は表示されます。
 ですが、今回の質問は、このエラーが危険であるかのかどうか。
 また、どのように回避すればよいのかをご教授願えればありがたいです。
 よろしくお願いいたします。

5.C及びC++言語の知識
  変数,配列,関数,条件文などの基本的なことは理解しています。
 ポインタは、まだ自信が持てるほどではありませんが理解しています。
 メモリの動的確保は、理解はしていますが複雑な条件でのメモリリークは、少し心配です。
 クラスの方は、まだ勉強が浅いので、継承とコピーコンストラクタなどがまだ使いこなせていませんが、
 クラスの簡単な使い方は理解しています。

使用環境
Windows7 64bit
Microsoft Visual C++ 2010 Express
WindowsAPI

使用カメラ
Buffalo BSW3K04H USB PC Camera

ソースコード

コード:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <commctrl.h>
#include "math.h"
#pragma comment(lib, "comctl32.lib")
#pragma comment( lib, "gdiplus.lib" )
#include <gdiplus.h>
#include <Gdiplusinit.h>

#include "resource.h"


#include <vfw.h>
#pragma comment(lib,"vfw32.lib")

using namespace Gdiplus;

// 定数
#define WINDOW_WIDTH  (640)		// ウィンドウの幅:640
#define WINDOW_HEIGHT (480)		// ウィンドウの高さ:480
#define WINDOW_X ((GetSystemMetrics( SM_CXSCREEN ) - WINDOW_WIDTH ) / 2)
#define WINDOW_Y ((GetSystemMetrics( SM_CYSCREEN ) - WINDOW_HEIGHT ) / 2)
#define ID_MI 1000

GdiplusStartupInput gdip_SI;
ULONG_PTR gdip_Token;

// グローバル変数
HINSTANCE g_hInst;              // インスタンスハンドル
HWND g_hDlg;                    // ダイアログボックスのハンドル
HWND hWndCap;

// プロトタイプ宣言
HWND Create(HINSTANCE hInst);
HWND CreatePushButton(HWND hWnd, int x, int y, int w, int h, LPCTSTR caption, int id);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);

void SetCamera( HWND hWnd )
{
	//既に値が入っている場合はドライバ解放
	if(!(hWnd==NULL))capDriverDisconnect( hWndCap );
	RECT rect;
	GetClientRect(hWnd,&rect);

	hWndCap = capCreateCaptureWindow(
           _T("Captrue Window"),     //	キャプチャウィンドウの名前
           WS_CHILD | WS_VISIBLE,    //	ウィンドウスタイル このパラメータについてはここを見てください。
           0, 0,                     //	表示位置
           rect.right - rect.left, rect.bottom - rect.top,//	ウィンドウサイズ
           hWnd,                     //	親のハンドル NULL:親なしのウィンドウ
           1);

	if(hWndCap==NULL)MessageBox(hWnd,_T("hWndCapの値がNULLになっています。"), NULL, NULL);
	capDriverConnect( hWndCap, 0 ); //	チャンネル1に接続
	capPreviewRate( hWndCap, 1 );
	capPreview( hWndCap, TRUE );

	// ビデオフォーマット取得
	BITMAPINFO bmpinfo;
	capGetVideoFormat(hWndCap,&bmpinfo,sizeof(BITMAPINFO));
}

// 開始位置
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, int showCmd)
{
	HWND hWnd;
	MSG msg;

	//GDI+ Start
	GdiplusStartup(&gdip_Token, &gdip_SI, NULL);
	g_hInst = hInst;  // インスタンスハンドルをグローバル変数に保存しておく

	// ウィンドウを作成する
	hWnd = Create(hInst);
	if (hWnd == NULL)
	{
		MessageBox(NULL, TEXT("ウィンドウの作成に失敗しました"), TEXT("エラー"), MB_OK);
		return 1;
	}

	// ウィンドウを表示する
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	// メッセージループ
	while (1)
	{
		BOOL ret = GetMessage(&msg, NULL, 0, 0);  // メッセージを取得する
		if (ret == 0 || ret == -1)
		{
			// アプリケーションを終了させるメッセージが来ていたら、
			// あるいは GetMessage() が失敗したら( -1 が返されたら )、ループを抜ける
			break;
		}
		else
		{
			if ((g_hDlg == NULL || !IsDialogMessage(g_hDlg, &msg)))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}
	//GDI+ 終了処理
	GdiplusShutdown(gdip_Token);
	return (int)msg.wParam;
}

// ウィンドウを作成する
HWND Create(HINSTANCE hInst)
{
	WNDCLASSEX wc;

	// ウィンドウクラスの情報を設定
	wc.cbSize = sizeof(wc);               // 構造体サイズ
	wc.style = CS_HREDRAW | CS_VREDRAW;   // スタイル
	wc.lpfnWndProc = WndProc;             // ウィンドウプロシージャ
	wc.cbClsExtra = 0;                    // 拡張情報1
	wc.cbWndExtra = 0;                    // 拡張情報2
	wc.hInstance = hInst;                 // インスタンスハンドル
	wc.hIcon = (HICON)LoadImage(          // アイコン
		NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
		);
	wc.hIconSm = wc.hIcon;                // 子アイコン
	wc.hCursor = (HCURSOR)LoadImage(      // マウスカーソル
		NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
		);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // ウィンドウ背景
	wc.lpszMenuName  = NULL;                    // メニュー名
	wc.lpszClassName = TEXT("Default Class Name");// ウィンドウクラス名

	// ウィンドウクラスを登録する
	if (RegisterClassEx(&wc) == 0){ return NULL; }

	// ウィンドウを作成する
	return CreateWindow(
		wc.lpszClassName,      // ウィンドウクラス名
		_T("camera" ),  // タイトルバーに表示する文字列
		WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,   // ウィンドウの種類
		WINDOW_X,              // ウィンドウを表示する位置(X座標)
		WINDOW_Y,              // ウィンドウを表示する位置(Y座標)
		WINDOW_WIDTH,          // ウィンドウの幅
		WINDOW_HEIGHT,         // ウィンドウの高さ
		NULL,                  // 親ウィンドウのウィンドウハンドル
		NULL,                  // メニューハンドル
		hInst,                 // インスタンスハンドル
		NULL                   // その他の作成データ
		);
	//*******************************************************
}
static HWND hButton1;

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	static DEVMODE devmode;         // ディスプレイモード
	static OPENFILENAME ofn = { 0 };
	static TCHAR strCustom[256] = TEXT("Before files\0*.*\0\0");

	switch (msg) 
	{
	case WM_CREATE:			// ウィンドウが作成されたとき
        hButton1 = CreateWindow(
                _T("BUTTON"), _T("ダイアログボックス"),
                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                20, 20, 150, 30, hWnd, (HMENU)ID_MI, g_hInst ,NULL);
		//カメラ設定関数の呼び出し
		SetCamera(hWnd);
		return 0;

	case WM_COMMAND:        // 項目が選択されたとき
		switch (LOWORD(wp))
		{
		case MB_OK:
			break;

		case ID_MI:
			//モードレスダイアログの定義
			//MessageBox(hWnd, _T("ボタンが押されました"), _T("確認"), NULL);
			
			//g_hDlg = CreateDialog(g_hInst, _T("IDD_DIALOG1"), hWnd, (DLGPROC)DlgProc);
			break;
		}
		return 0;

	case WM_PAINT:

		break;
	case WM_CLOSE:          // ウィンドウが閉じられるとき
		if(!(hWndCap == NULL))capDriverDisconnect( hWndCap );
		break;  // ウィンドウを閉じる処理はDefWindowProc()に任せる

	case WM_DESTROY:        // ウィンドウが破棄されるとき
		// ダイアログボックスを破棄する
		if(!(g_hDlg == NULL))DestroyWindow(g_hDlg);

		if(!(hWndCap == NULL))capDriverDisconnect( hWndCap );

		DestroyWindow(hWnd);

		PostQuitMessage(0);

		return 0;
	}
	return DefWindowProc(hWnd, msg, wp, lp);

}
// ダイアログプロシージャ
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg){
	case WM_INITDIALOG:  // ダイアログボックスが作成されたとき
		return TRUE;

	case WM_COMMAND:     // ダイアログボックス内の何かが選択されたとき
		switch (LOWORD(wp))
		{
		case IDOK:
			break;
		}		
		return TRUE;

	case WM_CLOSE:		// ダイアログボックスが閉じられるとき
		// ダイアログボックスを消す
		DestroyWindow(g_hDlg);
		return TRUE;
	}

	return FALSE;  // DefWindowProc()ではなく、FALSEを返すこと!
}

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: Webカメラで取得した画像を画面に表示したい

#2

投稿記事 by h2so5 » 9年前

例外の発生箇所は分かりませんか?中断すればコールスタックが見れるはずです。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: Webカメラで取得した画像を画面に表示したい

#3

投稿記事 by みけCAT » 9年前

ZEROXE さんが書きました:42,43行目

コード:

	//既に値が入っている場合はドライバ解放
	if(!(hWnd==NULL))capDriverDisconnect( hWndCap );
全くテストしていないので検討違いかもしれませんが、
hWndではなくhWndCapがNULLでないかどうかをチェックするべきなのではないでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ZEROXE
記事: 5
登録日時: 12年前

Re: Webカメラで取得した画像を画面に表示したい

#4

投稿記事 by ZEROXE » 9年前

h2so5さん回答ありがとうございます。
例外発生場所は、capDribverConnectだと思われます。
自動変数名:hWnd:0x002c1034{unused=???}
unused:CXX0030:エラーです:式を評価できません。
自動変数名:hWndCap:0x00140ecc{unused=???}
unused:CXX0030:エラーです:式を評価できません。
このようになっています。

みけCATさん回答ありがとうございます。
まったくその通りです。
間違っているのに気づいていませんでした。
ありがとうございます。
ですが、原因はこれではないみたいです。

ZEROXE
記事: 5
登録日時: 12年前

Webカメラで取得した画像を画面に表示したい

#5

投稿記事 by ZEROXE » 9年前

追記(編集:この追記は、間違っていたので訂正の方をご覧ください)

コード:

if(hWndCap==NULL)MessageBox(hWnd,_T("#1:hWndCapの値がNULLになっています。"), NULL, NULL);

hWndCap = capCreateCaptureWindow(...);

if(hWndCap==NULL)MessageBox(hWnd,_T("#2:hWndCapの値がNULLになっています。"), NULL, NULL);
ちなみにですが、
このようにhWndCapのNULLCHECKを実行した場合、#1:NULLCHECKは作動しますが、
#2:NULLCHECKが作動する前にエラーが発生する為、hWndCap = capCreateCaptureWindow(...);の箇所で
エラーが発生していると考えられます。
最後に編集したユーザー ZEROXE on 2015年1月12日(月) 15:57 [ 編集 1 回目 ]

ZEROXE
記事: 5
登録日時: 12年前

Re: Webカメラで取得した画像を画面に表示したい

#6

投稿記事 by ZEROXE » 9年前

追記についての訂正

コード:

	//hWndの値のNULLcheck
	if(hWnd == NULL)MessageBox(hWnd, _T("#0:hWndの値がNULLになっています。"), NULL, NULL);

	//hWndCapの値のNULLcheck
	if(hWndCap==NULL)MessageBox(hWnd, _T("#1:hWndCapの値がNULLになっています。"), NULL, NULL);
	
	//1:hWndCapの値が存在しているかを視覚化
	if(hWndCap)MessageBox(hWnd, _T("hWndCapのデータは存在しています。"), NULL, NULL);

	hWndCap = capCreateCaptureWindow(...);//長いので省略

	//2:hWndCapの値が存在しているかを視覚化
	if(hWndCap)MessageBox(hWnd, _T("hWndCapのデータは存在しています。"), NULL, NULL);

	//hWndCapの値のNULLcheck
	if(hWndCap==NULL)MessageBox(hWnd, _T("#2:hWndCapの値がNULLになっています。"), NULL, NULL);
少しエラー場所の特定をして見たところ、追記で書いた様にcapCreateCaptureWindow(...)でエラーが発生していると思っていましたが、
capCreateCaptureWindow(...)からは、値は出力されている模様です。
1:hWndCapの値が存在しているかを視覚化、こちらはNULL
2:hWndCapの値が存在しているかを視覚化、こちらには何らかの値が入っていることが確認できました。

エラーの発生場所は、
capDriverConnect( hWndCap, 0 ); // チャンネル1に接続
この部分であることが分かりました。(初回例外の部分)
ちなみに、エラーは、第二引数を、0にすると発生します。
その他の数字は、エラーは発生しませんが、映像が映らず黒い画面しかでません。
カメラは、一番最初に書いたbuffaloのカメラ一台しか接続はしていませんので、チャンネル1の数値0で問題ないと思いますが、エラーが発生します。

コード:

	int count = 0;
	char check_str[128];

	//省略

	//チャンネルに接続
	while(1)
	{
		count++;

		wsprintf(LPWSTR(check_str), _T("#%d:このチャンネルは存在しません。"), count);
		
		//戻り値を確認
		if(capDriverConnect( hWndCap, count ) == FALSE)
			MessageBox(hWnd, LPCWSTR(check_str), NULL, NULL);
		//ドライバーを見つけた場合又はcountが9に達した時点で脱出
		if(count == 9 || !(capDriverConnect( hWndCap, count ) == FALSE))
		{
			break;
		}
	}
0の場合を除いて、1~9までマッチングさせてみましたが、ヒットもせずエラーも発生しませんでした。

編集
エラーが出る原因は分かりませんが、Checkする為のプログラムを少し改良したのであげておきます。
カメラドライバーの情報は、チャンネル番号が0の時のみ表示される為、目的のカメラのチャンネル番号は0で良い模様。

コード:

void SetCamera( HWND hWnd )
{
	char check_str[128];
	char Driver_Name[128];
	char Version[128];

	//既に値が入っている場合はドライバ解放
	if(!(hWndCap == NULL))capDriverDisconnect( hWndCap );

	//rect構造体の定義
	RECT rect;

	//rect構造体のデータ取得
	GetClientRect(hWnd,&rect);

	//ウィンドウハンドルのNULLCHECK
	if(hWnd == NULL)MessageBox(hWnd, _T("#0:hWndの値がNULLになっています。"), NULL, NULL);

	//hWndCapのNULLCHECK
	if(hWndCap==NULL)
	{
		MessageBox(hWnd, _T("#1:hWndCapの値がNULLになっています。"), NULL, NULL);
	}else
	{
		MessageBox(hWnd, _T("#1:hWndCapのデータは存在しています。"), NULL, NULL);
	}

	//CaptureWindowの作成
	hWndCap = capCreateCaptureWindow(
           _T("Captrue Window"),							//	キャプチャウィンドウの名前
           WS_CHILD | WS_VISIBLE,							//	ウィンドウスタイル このパラメータについてはここを見てください。
           0, 0,											//	表示位置
           rect.right - rect.left, rect.bottom - rect.top,	//	ウィンドウサイズ
           hWnd,											//	親のハンドル NULL:親なしのウィンドウ
           NULL);

	//hWndCapのNULLCHECK
	if(hWndCap==NULL)
	{
		MessageBox(hWnd, _T("#2:hWndCapの値がNULLになっています。"), NULL, NULL);
	}else
	{
		MessageBox(hWnd, _T("#2:hWndCapのデータは存在しています。"), NULL, NULL);
	}

	//	チャンネル接続
	for(int i = 0; i < 10; ++i)
	{
		//エラー時のコメント作成
		wsprintf(LPWSTR(check_str), _T("#ErrorDriverNo.%d:このチャンネルは存在しません。"), i);

		//カメラの情報を取得
		if(capGetDriverDescription(i, (LPWSTR) Driver_Name, sizeof(Driver_Name), (LPWSTR)Version, sizeof(Version)))
		{
			//カメラのドライバー名の表示
			MessageBox(hWnd, (LPWSTR) Driver_Name, NULL, NULL);

			//カメラのバージョン情報の表示
			MessageBox(hWnd, (LPWSTR)Version, NULL, NULL);

			//カメラのドライバーのチャンネル接続
/*⇒*/		capDriverConnect( hWndCap, i );//(初回例外発生箇所:第2引数iが0の時に発生)

			break;
		}else//ノーマッチング処理
		{
			MessageBox(hWnd, LPWSTR(check_str), NULL, NULL);
		}	
	}

	capPreviewRate( hWndCap, 1 );
	capPreview( hWndCap, TRUE );

	// ビデオフォーマット取得
	//BITMAPINFO bmpinfo;
	//capGetVideoFormat(hWndCap,&bmpinfo,sizeof(BITMAPINFO));
}

ZEROXE
記事: 5
登録日時: 12年前

Webカメラで取得した画像を画面に表示したい

#7

投稿記事 by ZEROXE » 9年前

カメラのドライバーを入れ直してみたところ正常に動作しました。
ですが、初回例外が発生していたことが気になりますが、
一応問題の方解決できたので、トピックを閉めさせていただきます。
h2so5さん、みけCATさん、ご解答の方ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る