カメラ映像の表示方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ttb

カメラ映像の表示方法

#1

投稿記事 by ttb » 6年前

winapiにおいてcreatewindow関数を使用して親ウィンドウの中に子ウィンドウを作成してその子ウィンドウにカメラからの映像をリアルタイムで表示することは可能ですか?
また可能ならどのようにすればいいのでしょうか?

環境はvisual studio2013 for windows desktop、 C++、 カメラ映像の取得にはopencvを使用しています。

Math

Re: カメラ映像の表示方法

#2

投稿記事 by Math » 6年前

>カメラ映像の取得にはopencvを使用しています。

まずopencvのコードを提示してください。(Win32ConsoleApplication ではありませんか?)

アバター
usao
記事: 1887
登録日時: 11年前

Re: カメラ映像の表示方法

#3

投稿記事 by usao » 6年前

>カメラ映像の取得にはopencvを使用しています

カメラ映像であれば,大抵,横幅は4の倍数だろうから,
BitBlt()などで簡単にDCに描画できます.

#とかいう話ではなくて, リアルタイム の部分のための工夫が主題なのでしょうか?

ttb

Re: カメラ映像の表示方法

#4

投稿記事 by ttb » 6年前

カメラ映像をvideoCaptureで取得してリアルタイムで表示したいです.
いかに主要なコードを掲載します。

コード:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR lpCmdLine, int nCmdShow) {

		HWND hwnd;
		MSG msg;
		WNDCLASS winc;

		winc.style = CS_HREDRAW | CS_VREDRAW;
		winc.lpfnWndProc = WndProc;
		winc.cbClsExtra = winc.cbWndExtra = 0;
		winc.hInstance = hInstance;
		winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		winc.hCursor = LoadCursor(NULL, IDC_ARROW);
		winc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
		winc.lpszMenuName = NULL;
		winc.lpszClassName = TEXT("opencv");

		if (!RegisterClass(&winc)) return -1;

		hwnd = CreateWindow(
			TEXT("opencv"), TEXT("画面"),
			WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE,
			0, 0, 1350, 800,
			NULL, NULL, hInstance, NULL
			);

		CreateWindow(//ここで作成したウィンドウにカメラ映像を表示させたい
			TEXT("STATIC"), NULL,
			WS_CHILD | WS_VISIBLE | WS_BORDER ,
			100, 0, 800, 700,
			hwnd, NULL, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("111"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			1000, 100, 200, 50,
			hwnd, (HMENU)BUTTON_ID1, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("aaa"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			1000, 400, 100, 50,
			hwnd, (HMENU)BUTTON_ID2, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("bbb"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			1200, 400, 100, 50,
			hwnd, (HMENU)BUTTON_ID3, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("ccc"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			1000, 300, 80, 40,
			hwnd, (HMENU)BUTTON_ID4, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("ddd"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			1125, 300, 80, 40,
			hwnd, (HMENU)BUTTON_ID5, hInstance, NULL
			);

		CreateWindow(
			TEXT("BUTTON"), TEXT("eee"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			1250, 300, 80, 40,
			hwnd, (HMENU)BUTTON_ID6, hInstance, NULL
			);

		if (hwnd == NULL) return -1;

		while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
		return msg.wParam;
	
}

結城紬
記事: 42
登録日時: 6年前

Re: カメラ映像の表示方法

#5

投稿記事 by 結城紬 » 6年前

手元にWEBカメラ等が無かったので、とりあえず動画ファイルを全力で描画するサンプルを作ってみました。
子ウィンドウに描くとかは面倒くさいのでやってません。

コード:

#include <Windows.h>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

static HBITMAP g_hBitmap;
static HDC g_hBitmapDC;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	switch (message) {
	case WM_CREATE:
		hdc = GetDC(hWnd);
		g_hBitmap = CreateCompatibleBitmap(hdc, 800, 600);
		g_hBitmapDC = CreateCompatibleDC(hdc);
		SelectObject(g_hBitmapDC, g_hBitmap);
		ReleaseDC(hWnd, hdc);
		return NOERROR;
	case WM_DESTROY:
		DeleteDC(g_hBitmapDC);
		DeleteObject(g_hBitmap);
		PostQuitMessage(0);
		return NOERROR;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		hdc = BeginPaint(hWnd, &ps);
		BitBlt(hdc, 110, 10, 800, 600, g_hBitmapDC, 0, 0, SRCCOPY);
		EndPaint(hWnd, &ps);
		return NOERROR;
	}
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR lpCmdLine, int nCmdShow) {

	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style = CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc = WndProc;
	winc.cbClsExtra = winc.cbWndExtra = 0;
	winc.hInstance = hInstance;
	winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winc.hCursor = LoadCursor(NULL, IDC_ARROW);
	winc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
	winc.lpszMenuName = NULL;
	winc.lpszClassName = TEXT("opencv");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
		TEXT("opencv"), TEXT("画面"),
		WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE,
		0, 0, 1350, 800,
		NULL, NULL, hInstance, NULL
	);

	if (hwnd == NULL) return -1;

	cv::VideoCapture reader("C:\\TEMP\\test.avi");
	if (!reader.isOpened())
	{
		return -1;
	}
	while (true)
	{
		if (PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE))
		{
			break;
		}
		else if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			// リアルタイムループ
			cv::Mat image;
			reader >> image;
			// とりあえず、取得できなくなったら終了する
			if (image.empty())
			{
				break;
			}
			BITMAPINFO bi;
			ZeroMemory(&bi, sizeof bi);
			bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
			bi.bmiHeader.biWidth = image.cols;
			bi.bmiHeader.biHeight = -image.rows;
			bi.bmiHeader.biBitCount = image.channels() * 8;
			bi.bmiHeader.biPlanes = 1;
			bi.bmiHeader.biCompression = BI_RGB;
			SetDIBits(g_hBitmapDC, g_hBitmap, 0, image.rows, image.data, &bi, DIB_RGB_COLORS);
			InvalidateRect(hwnd, NULL, FALSE);
		}
	}
	return 0;
}

ttb

Re: カメラ映像の表示方法

#6

投稿記事 by ttb » 6年前

ありがとうございます。少し方法は違いますが自分でも作ってみました。しかし、webカメラ映像は読み込めるのに今回作っているプロジェクト(win32プロジェクト)ではaviutilでavi出力したファイルが読み込めませんでした。
しかし今回のプロジェクトとは別のプロジェクト(コンソールアプリケーション)では読み込むことが出来ました。
今回のプロジェクトで読み込めたのはAGデスクトップレコーダーでキャプチャーした動画ファイルは読み込むことが出来ました。
これは何が原因なのでしょか?
コードは以下になります。
Header.hには
以下の物を読み込んでいます。
#include<opencv2/opencv.hpp>
#include<opencv2/opencv_lib.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>

#include<windows.h>
#include <process.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <Commdlg.h>
#include <SDKDDKVer.h>
#include<iostream>
#include<stdio.h>

#define STATIC_ID0 00
#define BUTTON_ID1 0
#define BUTTON_ID2 1
#define BUTTON_ID3 2
#define BUTTON_ID4 3
#define BUTTON_ID5 4
#define BUTTON_ID6 5
#define BUTTON_ID7 6

コード:

#include"Header.h"

using namespace cv;
using namespace std;

Mat imgMatCamera1;
Mat imgMatCamera2;
Mat imgMatAvi;

Mat imgMatSyn1;
Mat imgMatSyn2;
IplImage *imgFromOpenCV1 = NULL;
IplImage *imgFromOpenCV2 = NULL;
IplImage img1;
IplImage img2;

HWND hwnd;
HWND hwPic = NULL;
HDC hDC = NULL;
HDC hDCPic = NULL;

DWORD dwThread;

BITMAPINFO bmpInfo1;
unsigned long* bmpData1 = NULL;
BITMAPINFO bmpInfo2;
unsigned long* bmpData2 = NULL;
void iplTobmp(const IplImage*, BITMAPINFO&, unsigned long*);

unsigned int __stdcall CameraCaptureThread(PVOID pv);
HANDLE threadHandle1 = NULL;
BOOL endFlag1 = FALSE;

unsigned int __stdcall GuideThread(PVOID pv);
HANDLE threadHandle2 = NULL;
BOOL endFlag2 = FALSE;

//スレッド1起動関数
void function1(HWND hwnd) {
	threadHandle1 = (HANDLE)_beginthreadex(
		NULL,
		0,
		CameraCaptureThread,
		NULL,
		0,
		(unsigned int*)&dwThread);

	if (!threadHandle1) {
		MessageBox(hwnd, TEXT("DEBUG"), TEXT("スレッド起動エラー"), MB_OK);
	}
}

//スレッド2起動関数
void function2(HWND hwnd) {
	threadHandle2 = (HANDLE)_beginthreadex(
		NULL,
		0,
		GuideThread,
		NULL,
		0,
		(unsigned int*)&dwThread);

	if (!threadHandle2) {
		MessageBox(hwnd, TEXT("DEBUG"), TEXT("スレッド起動エラー"), MB_OK);
	}
}

//iplimageからbmp変換
void iplTobmp(const IplImage* srcIplImage, BITMAPINFO& bmpInfo, unsigned long* bmpData) {
	int width = srcIplImage->width;
	int height = srcIplImage->height;

	ZeroMemory(&bmpInfo, sizeof(bmpInfo));
	bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmpInfo.bmiHeader.biWidth = width;
	bmpInfo.bmiHeader.biHeight = height;
	bmpInfo.bmiHeader.biPlanes = 1;
	bmpInfo.bmiHeader.biBitCount = 32;
	bmpInfo.bmiHeader.biCompression = BI_RGB;

	unsigned long pixel;
	unsigned char r, g, b;
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			b = srcIplImage->imageData[srcIplImage->widthStep * i + j * 3];
			g = srcIplImage->imageData[srcIplImage->widthStep * i + j * 3 + 1];
			r = srcIplImage->imageData[srcIplImage->widthStep * i + j * 3 + 2];
			pixel = 0x00000000;
			pixel = (r << 16) + (g << 8) + b;
			bmpData[j + (height - i - 1) * width] = pixel;
		}
	}
}

//カメラ映像の表示
unsigned int __stdcall CameraCaptureThread(PVOID pv) {

	VideoCapture cap1(0);

	if (!cap1.isOpened()) {
		return -1;
	}

	while (1){
		if (endFlag1 == TRUE) {
			break;
		}

		cap1 >> imgMatCamera1;
		imgMatSyn1 = imgMatCamera1;

		img1 = imgMatSyn1;
		imgFromOpenCV1 = &img1;

		//メモリを確保
		if (bmpData1 != NULL) {
			HeapFree(GetProcessHeap(), 0, bmpData1);
		}

		bmpData1 = (LPDWORD)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, imgFromOpenCV1->width * imgFromOpenCV1->height * 4);

		//iplImageからDIBへ変換
		iplTobmp(imgFromOpenCV1, bmpInfo1, bmpData1);

		//ここで描画
		StretchDIBits(hDC,
			15, 20, imgFromOpenCV1->width*1.3, imgFromOpenCV1->height*1.3,
			0, 0, imgFromOpenCV1->width, imgFromOpenCV1->height,
			bmpData1, &bmpInfo1, DIB_RGB_COLORS, SRCCOPY);
	}
	return 0;
}

//動画の表示
unsigned int __stdcall GuideThread(PVOID pv) {
	//VideoCapture cap12(0);
	VideoCapture video("C:\\Users\tatebe riku\\Documents\\Visual Studio 2013\\Projects\\Win32Project1\\Win32Project1\\ddd.avi");
	
	//if (!cap12.isOpened()) {
		//return -1;
	//}
	if (!video.isOpened()) {
		MessageBox(hwnd, TEXT("DEBUG"), TEXT("ファイルopenエラー"), MB_OK);
		return -1;
	}
	while (1){
		if (endFlag2==TRUE) {
			break;
		}
		
		//cap12 >> imgMatCamera2;
		video >> imgMatAvi;
		//imgMatSyn2 = imgMatCamera2 +imgMatAvi;
		
		img2 = imgMatAvi;
		imgFromOpenCV2 = &img2;

		//メモリを確保
		if (bmpData2 != NULL) {
			HeapFree(GetProcessHeap(), 0, bmpData2);
		}

		bmpData2 = (LPDWORD)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, imgFromOpenCV2->width * imgFromOpenCV2->height * 4);

		//iplImageからDIBへ変換
		iplTobmp(imgFromOpenCV2, bmpInfo2, bmpData2);

		//ここで描画
		StretchDIBits(hDC,
			15, 20, imgFromOpenCV2->width*1.3, imgFromOpenCV2->height*1.3,
			0, 0, imgFromOpenCV2->width, imgFromOpenCV2->height,
			bmpData2, &bmpInfo2, DIB_RGB_COLORS, SRCCOPY);
	}
	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
	hwPic = GetDlgItem(hwnd, STATIC_ID0);
	hDC = GetDC(hwPic);
	
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wp)) {
		case BUTTON_ID1:
			function1(hwnd);
			break;

		case BUTTON_ID2://開始
			endFlag1 = TRUE;
			WaitForSingleObject(threadHandle1, INFINITE);
			CloseHandle(threadHandle1);
			if (imgFromOpenCV1 != NULL){
				HeapFree(GetProcessHeap(), 0, bmpData1);
			}
			if (threadHandle2 != NULL) {
				MessageBox(hwnd, TEXT("DEBUG"), TEXT("スレッド起動エラー"), MB_OK);
				//break;
			}
			function2(hwnd);
			break;

		case BUTTON_ID3://終了
			break;

		case BUTTON_ID4:
			break;

		case BUTTON_ID5:
			break;

		case BUTTON_ID6:
			break;
		}
		return 0;
	}
	return DefWindowProc(hwnd, msg, wp, lp);
}

//ウィンドウの設定
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) {

		MSG msg;
		WNDCLASS winc;

		winc.style = CS_HREDRAW | CS_VREDRAW;
		winc.lpfnWndProc = WndProc;
		winc.cbClsExtra = winc.cbWndExtra = 0;
		winc.hInstance = hInstance;
		winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		winc.hCursor = LoadCursor(NULL, IDC_ARROW);
		winc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
		winc.lpszMenuName = NULL;
		winc.lpszClassName = TEXT("opencv");

		if (!RegisterClass(&winc)) return -1;

		hwnd = CreateWindow(
			TEXT("opencv"), TEXT("Training System"),
			WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE,
			0, 0, 1350, 800,
			NULL, NULL, hInstance, NULL);

		CreateWindow(
			TEXT("STATIC"), NULL,
			WS_CHILD | WS_VISIBLE | WS_BORDER | SS_BITMAP ,
			0, 0, 870, 660,
			hwnd, (HMENU)STATIC_ID0, hInstance, NULL);


		CreateWindow(
			TEXT("BUTTON"), TEXT("WebCamera start-up"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			900, 80, 250, 50,
			hwnd, (HMENU)BUTTON_ID1, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Sample display"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			900, 400, 150, 50,
			hwnd, (HMENU)BUTTON_ID2, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Sample Exit"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			1170, 400, 150, 50,
			hwnd, (HMENU)BUTTON_ID3, hInstance, NULL);

		CreateWindow(
			TEXT("STATIC"), TEXT("Select Level"),
			WS_CHILD | WS_VISIBLE | WS_BORDER ,
			880, 220, 450, 100,
			hwnd, NULL, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Beginner"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			900, 250, 120, 50,
			hwnd, (HMENU)BUTTON_ID4, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Intermediate"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			1050, 250, 120, 50,
			hwnd, (HMENU)BUTTON_ID5, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Advanced"),
			WS_CHILD | WS_BORDER | WS_VISIBLE | BS_AUTORADIOBUTTON,
			1200, 250, 120, 50,
			hwnd, (HMENU)BUTTON_ID6, hInstance, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("Judge"),
			WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
			1200, 500, 120, 50,
			hwnd, (HMENU)BUTTON_ID7, hInstance, NULL);

		if (hwnd == NULL) return -1;

		while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
		
		return msg.wParam;
	
}

Math

Re: カメラ映像の表示方法

#7

投稿記事 by Math » 6年前

opencvのプロジェクトはコンソールアプリケーションですね。
映像表示のプロジェクトはwin32プロジェクトですね。

これらの混在には工夫がいります。
http://independence-sys.net/main/?p=2612
に記述がありますがめんどうです。

私のホームページには混在する方法がかいてありますが(win32プロジェクトでコンソールアプリケーションを動かす)opencvは試したこはありません。(OpenCVのVersionとコードを提示頂ければテストしてみれますが)
http://cswin10.nisinippon.com/cs/cs1/vs ... /dx01.html

結城紬
記事: 42
登録日時: 6年前

Re: カメラ映像の表示方法

#8

投稿記事 by 結城紬 » 6年前

ttb さんが書きました:ありがとうございます。少し方法は違いますが自分でも作ってみました。しかし、webカメラ映像は読み込めるのに今回作っているプロジェクト(win32プロジェクト)ではaviutilでavi出力したファイルが読み込めませんでした。
しかし今回のプロジェクトとは別のプロジェクト(コンソールアプリケーション)では読み込むことが出来ました。
今回のプロジェクトで読み込めたのはAGデスクトップレコーダーでキャプチャーした動画ファイルは読み込むことが出来ました。
これは何が原因なのでしょか?
すみませんが、今何が問題なのかよくわかりません。
もう少し整理して書いてもらえないでしょうか?
コードは以下になります。
win32 プロジェクトのことでしょうか?
コンソールアプリケーションの方はどうなっていますか?

ttb

Re: カメラ映像の表示方法

#9

投稿記事 by ttb » 6年前

Mathさん
opencvのバージョンは2.4.9です。

結城紬さん
問題になっているのは今回作成したwin32プロジェクトにおいてopencvのVideoCapture関数で再生できない動画ファイルが存在するということです。
具体的にはAviUtilでavi出力したものが再生できず困っています。
そこで動画ファイル自体に問題があるのかと思い、別で作成していたコンソールアプリケーションで再生したところ再生できたので動画自体には問題はないという判断に至りました。
今回作りたいプログラムはwin32プロジェクトの方なので再生できず困っています。

ttb

Re: カメラ映像の表示方法

#10

投稿記事 by ttb » 6年前

コードを見直していてファイルパスの一部に¥が一つ抜けているのを発見しました。
修正したところしっかりと再生することができました。
こんな簡単なミスでお騒がせして申し訳ありませんでした。
また、協力していただいた皆様、本当にありがとうございました。     

返信

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