Newer
Older
PrismSoftware / ECTrainer2 / ECTrainerGUI.cpp

#include "ECTrainer.h"
#include "ECTrainerGUI.h"
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
#include <time.h>

#define CVUI_IMPLEMENTATION
#include "cvui.h"

#ifdef _DEBUG
#include <iostream>
#endif

// コンストラクタ
ECTrainerGUI::ECTrainerGUI(ECTrainer* pEct) :BaseProcess(pEct) {
}

// 初期化
bool ECTrainerGUI::Init() {
	// 実行環境の情報取得
	GetDisplayInfo();
	// GUIウインドウ
	cvui::init(WIN_MAIN);
	_MainFrame.create(MAIN_FRAME_SIZE, CV_8UC3);
	// 刺激提示ウインドウ
	cv::namedWindow(WIN_DISP, cv::WINDOW_NORMAL | cv::WINDOW_OPENGL);
	if (_Displays.size() > 1) {
		// メインモニタの決定
		int mainMonitor = 0, subMonitor = 1;
		for (int i = 0; i < _Displays.size(); i++) {
			if (_Displays[i].left == 0 && _Displays[i].top == 0) mainMonitor = i;
			else subMonitor = i;
		}
		// マルチモニタの場合 サブ画面でフルスクリーン化
		cv::moveWindow(WIN_DISP, _Displays[subMonitor].left, _Displays[subMonitor].top);
		cv::setWindowProperty(WIN_DISP, cv::WND_PROP_FULLSCREEN, cv::WINDOW_FULLSCREEN);
		_DispBuffer.create(cv::Size(_Displays[subMonitor].right - _Displays[subMonitor].left,
			_Displays[subMonitor].bottom - _Displays[subMonitor].top), CV_8UC3);
		cv::moveWindow(WIN_MAIN, _Displays[mainMonitor].left + 100, _Displays[mainMonitor].top + 100);
	}
	else {
		// シングルモニタの場合
		cv::moveWindow(WIN_DISP, _Displays[0].left, _Displays[0].top);
		_DispBuffer.create(cv::Size(640, 480), CV_8UC3);
		cv::moveWindow(WIN_MAIN, _Displays[0].left + 100, _Displays[0].top + 100);
	}
	cv::Mat blank(cv::Size(IMAGE_WIDTH, 480), CV_8UC3, cv::Scalar(0));
	blank.copyTo(_SceneBuffer);
	blank.copyTo(_DispImage);
	_Logo = cv::imread("../images/ECTrainerLogo640x91.png");

	return true;
}

// イベントループ
bool ECTrainerGUI::MainLoop() {

	bool fShowEyesPos = true;
	bool fStimulus = false;
	bool fContact = false;
	bool fKeepContact = false;
	float targetSize = 2.0;
	clock_t startContact = 0;

	while (_pEct->IsRunning() && cv::getWindowProperty(WIN_MAIN, 0) >= 0) {

		// メインウインドウフレームクリア
		_MainFrame = cv::Scalar(49, 52, 49);

		// マウスカーソルを注視点とする処理
		//cvui::rect(_MainFrame, SCENE_BUFFER_POS.x, SCENE_BUFFER_POS.y, 
		//	SCENE_BUFFER_SIZE.width, SCENE_BUFFER_SIZE.height, 0xFF0000);
		cv::Point mp = cvui::mouse(WIN_MAIN);
		if (cvui::iarea(SCENE_BUFFER_POS.x, SCENE_BUFFER_POS.y, IMAGE_WIDTH, _SceneBufferHeight) == cvui::DOWN) {
			_pEct->SetGazeV(cv::Point(mp.x - SCENE_BUFFER_POS.x, mp.y - SCENE_BUFFER_POS.y));
		} else {
			_pEct->SetGazeV(cv::Point(-1, -1));
		}
		cv::Point2f imagePos = cv::Point2f(-1.F, -1.F);
		if (cvui::iarea(DISP_IMAGE_POS.x, DISP_IMAGE_POS.y, IMAGE_WIDTH, _DispImageHeight) == cvui::OVER) {
			imagePos.x = (float)(mp.x - DISP_IMAGE_POS.x) / IMAGE_WIDTH;
			imagePos.y = (float)(mp.y - DISP_IMAGE_POS.y) / _DispImageHeight;
		}

		// 刺激画像
		if (_pEct->GetGazeI().x >= 0) {
			cv::circle(_DispImage,
				cv::Point((int)(_DispImage.cols * _pEct->GetGazeI().x), (int)(_DispImage.rows * _pEct->GetGazeI().y)),
				(int)(_DispImage.rows * 0.03), CV_RGB(0, 0, 255), 2);
		}
		fContact = false;
		if (fStimulus) {
			float dx = _pEct->GetEyeR().x - _pEct->GetEyeL().x;
			float dy = _pEct->GetEyeR().y - _pEct->GetEyeL().y;
			float eyesDistance = sqrtf(dx * dx + dy * dy);
			float areaSize = eyesDistance * targetSize / 2.0F;
			if (fShowEyesPos) {
				cv::circle(_DispImage,
					cv::Point((int)(_DispImage.cols * _pEct->GetEyeR().x), (int)(_DispImage.rows * _pEct->GetEyeR().y)),
					(int)(_DispImage.rows * 0.03), CV_RGB(0, 128, 0), 2);
				cv::circle(_DispImage,
					cv::Point((int)(_DispImage.cols * _pEct->GetEyeL().x), (int)(_DispImage.rows * _pEct->GetEyeL().y)),
					(int)(_DispImage.rows * 0.03), CV_RGB(0, 128, 0), 2);
				cv::circle(_DispImage,
					cv::Point((int)(_DispImage.cols * _pEct->GetEyeR().x), (int)(_DispImage.rows * _pEct->GetEyeR().y)),
					(int)(_DispImage.cols * areaSize), CV_RGB(200, 200, 0), 2);
				cv::circle(_DispImage,
					cv::Point((int)(_DispImage.cols * _pEct->GetEyeL().x), (int)(_DispImage.rows * _pEct->GetEyeL().y)),
					(int)(_DispImage.cols * areaSize), CV_RGB(200, 200, 0), 2);
			}
			if (_pEct->GetGazeI().x >= 0) {
				float dxR = _pEct->GetEyeR().x - _pEct->GetGazeI().x;
				float dyR = _pEct->GetEyeR().y - _pEct->GetGazeI().y;
				float edR = sqrtf(dxR * dxR + dyR * dyR);
				float dxL = _pEct->GetEyeL().x - _pEct->GetGazeI().x;
				float dyL = _pEct->GetEyeL().y - _pEct->GetGazeI().y;
				float edL = sqrtf(dxL * dxL + dyL * dyL);
				if (edR <= areaSize || edL <= areaSize) {
					fContact = true;
				}
			}
		}

		// コンタクト判定
		if (fContact) {
			if (!fKeepContact) {
				fKeepContact = true;
				startContact = clock();
				PlaySound(_T("../voices/voice2.wav"), NULL, SND_FILENAME | SND_ASYNC);
			} else {
				if ((clock() - startContact) / CLOCKS_PER_SEC >= 3) {
					PlaySound(_T("../voices/voice1.wav"), NULL, SND_FILENAME | SND_SYNC);
					fKeepContact = false;
					targetSize -= 0.2;
					_pEct->Next();
				}
			}
		} else {
			fKeepContact = false;
		}

		// UI描画
		cvui::image(_MainFrame, 0, 0, _Logo);
		cvui::beginColumn(_MainFrame, 10, 100, 140, -1, 10);
		if (fStimulus) {
			if (cvui::button(140, 30, "STOP")) {
				fKeepContact = false;
				fStimulus = false;
			}
			if (cvui::button(140, 30, "NEXT")) {
				fKeepContact = false;
				_pEct->Next();
			}
			cvui::trackbar(140, &targetSize, (float)0, (float)3.0);
			cvui::checkbox("Show Eyes", &fShowEyesPos);
			cvui::text(fContact ? "Eyes Contact!" : "No contact");
		} else {
			if (cvui::button(140, 30, "CALIBRATION")) _pEct->Calib();
			if (cvui::button(140, 30, "START")) {
				fStimulus = true;
				fKeepContact = false;
				_pEct->Start();
			}
		}
		cvui::printf("Mouse W %d, %d", mp.x, mp.y);
		cvui::printf("Mouse L %.2f, %.2f", imagePos.x, imagePos.y);
		cvui::printf("View Gaze %d, %d", _pEct->GetGazeV().x, _pEct->GetGazeV().y);
		cvui::printf("Img Gaze %.2f, %.2f", _pEct->GetGazeI().x, _pEct->GetGazeI().y);
		cvui::text(_pEct->GetHomographyStatus() ? "Homography OK" : "No homography");
		bool snapshot = cvui::button(140, 30, "SNAPSHOT");
		if (cvui::button(140, 30, "QUIT")) _pEct->Stop();
		cvui::endColumn();
		cvui::text(_MainFrame, SCENE_BUFFER_POS.x, 100, "SCENE CAMERA");
		cvui::image(_MainFrame, SCENE_BUFFER_POS.x, SCENE_BUFFER_POS.y, _SceneBuffer);
		cvui::text(_MainFrame, DISP_IMAGE_POS.x, 100, "STIMULUS IMAGE");
		cvui::image(_MainFrame, DISP_IMAGE_POS.x, DISP_IMAGE_POS.y, _DispImage);

		// 画面表示
		cvui::update();
		cv::imshow(WIN_MAIN, _MainFrame);
		cv::imshow(WIN_DISP, _DispBuffer);
		if (snapshot) cv::imwrite("d:/usr/dl/snapshot.jpg", _MainFrame);
		int key = cv::waitKey(1);
		if (key == KEY_ESC) _pEct->Stop();
	}
	return true;
}

// 視野画像バッファに画像を設定
void ECTrainerGUI::SetSceneBuffer(cv::Mat& img) {
	_SceneBufferHeight = img.rows * IMAGE_WIDTH / img.cols;
	cv::resize(img, _SceneBuffer, cv::Size(IMAGE_WIDTH, _SceneBufferHeight));
}

// 刺激画像バッファに画像を設定
void ECTrainerGUI::SetDispBuffer(cv::Mat& img) {
	_DispImageHeight = img.rows * IMAGE_WIDTH / img.cols;
	cv::resize(img, _DispImage, cv::Size(IMAGE_WIDTH, _DispImageHeight));

	cv::Mat buf(_DispBuffer.size(), CV_8UC3, cv::Scalar(0));
	cv::Rect roiRect;
	if (img.rows * buf.cols / buf.rows > img.cols) {
		roiRect.width = img.cols * buf.rows / img.rows;
		roiRect.height = buf.rows;
	} else {
		roiRect.width = buf.cols;
		roiRect.height = img.rows * buf.cols / img.cols;
	}
	roiRect.x = (buf.cols - roiRect.width) / 2;
	roiRect.y = (buf.rows - roiRect.height) / 2;
	if (roiRect.x + roiRect.width > buf.cols) roiRect.width = buf.cols - roiRect.x;
	if (roiRect.y + roiRect.height > buf.rows) roiRect.height = buf.rows - roiRect.y;
	cv::Mat roi(buf, roiRect);
	cv::resize(img, roi, roi.size());
	buf.copyTo(_DispBuffer);
}

// 全ディスプレイの情報を取得
void ECTrainerGUI::GetDisplayInfo() {
	EnumDisplayMonitors(NULL, NULL, 
		(MONITORENUMPROC)MonitorEnumProc, (LPARAM)&_Displays);
#ifdef _DEBUG
	for (int i = 0; i < _Displays.size(); i++) {
		std::cout << "Monitor" << i << " : "
			<< _Displays[i].left << ","	<< _Displays[i].top << ","
			<< _Displays[i].right << "," << _Displays[i].bottom << std::endl;
	}
#endif // _DEBUG
}

// 各ディスプレイの情報を取得
BOOL CALLBACK ECTrainerGUI::MonitorEnumProc(
	HMONITOR hMon, HDC hdcMon, LPRECT lpMon, LPARAM dwData) {
	std::vector<RECT>* displays = (std::vector<RECT>*)dwData;
	displays->push_back(*lpMon);
	return TRUE;
}