#include "DShowMovie.h"
#include "ECTrainerGUI.h"
#include "ECTrainer.h"
#include "Worker.h"
#include "EyeTrack.h"
#include "ImageProc.h"
#include "BitalMonitor.h"
#include "TobiiREST.h"
#include "Stimulus.h"
#include "Marker.h"
#include "myWinUtils.h"
#pragma comment(lib,"winmm.lib")
#define CVUI_IMPLEMENTATION
#include "cvui.h"
// コンストラクタ
ECTrainerGUI::ECTrainerGUI(ECTrainer* pEct) : BaseProcess(pEct)
, _SceneBufferHeight(0)
, _DispImageHeight(0)
, _SceneBufferScale(1.F)
, _hWndMain(NULL)
, _Booting(true)
{
}
// 初期化
bool ECTrainerGUI::Init() {
// GUIウインドウ
cvui::init(WIN_MAIN);
_MainFrame.create(MAIN_FRAME_SIZE, CV_8UC3);
// 刺激提示ウインドウ
std::vector<RECT> displays = mwut::GetDisplayInfo(); // 実行環境の情報取得
if (displays.size() > 1 && !FORCE_SINGLE_MONITOR) {
// マルチモニタの場合
// サブ画面に操作パネル表示
cv::moveWindow(WIN_MAIN, displays[1].left, displays[1].top);
// メイン画面に被験者映像提示(DirectShow動画がサブ画面に出力できないため)
cv::namedWindow(WIN_DISP, cv::WINDOW_NORMAL | cv::WINDOW_OPENGL);
cv::moveWindow(WIN_DISP, displays[0].left, displays[0].top);
cv::resizeWindow(WIN_DISP, cv::Size(640, 400));
cv::setWindowProperty(WIN_DISP, cv::WND_PROP_FULLSCREEN, cv::WINDOW_FULLSCREEN);
} else {
// シングルモニタの場合
cv::moveWindow(WIN_MAIN, displays[0].left + 100, displays[0].top + 100);
displays[0].right /= 2;
displays[0].bottom /= 2;
cv::namedWindow(WIN_DISP, cv::WINDOW_AUTOSIZE);
cv::moveWindow(WIN_DISP, displays[0].left, displays[0].top);
}
_FullDispBuf.create(cv::Size(displays[0].right - displays[0].left,
displays[0].bottom - displays[0].top), CV_8UC3);
_hWndMain = ::FindWindowA(NULL, WIN_MAIN.c_str());
_blank = cv::Mat(DISP_SIZE, CV_8UC3, cv::Scalar(0));
_Logo = cv::imread(HEADER_FILE);
_pEct->PDSMovie()->Init(displays[0]);
return true;
}
// 基本処理
bool ECTrainerGUI::Routine() {
// メインウインドウフレームクリア
_MainFrame = cv::Scalar(49, 52, 49);
// 視野画像バッファ生成
cv::Mat sceneBuf = _pEct->PImageProc()->GetImage();
if (sceneBuf.empty()) sceneBuf = _blank;
// 表示バッファ生成(実験中はWorkerクラスの生成画像,非実験中はStimulusクラスの画像を使う)
bool fTarget = _pEct->PWorker()->GetAppStatus() == APP_STATUS::STIM;
bool isNew = fTarget ? _pEct->PWorker()->IsNewTargetImg() : _pEct->PStimulus()->IsNewDisplay();
cv::Mat dispBuf = fTarget ? _pEct->PWorker()->GetTargetImg().clone() : _pEct->PStimulus()->GetDisplay().clone();
if (dispBuf.empty()) dispBuf = _blank;
if (isNew && !_pEct->PDSMovie()->IsPlaying()) this->MakeFullDispBuffer(dispBuf);
// 注視点の表示
if (_pEct->PEyeTrack()->GetGazeV().x >= 0) {
cv::circle(sceneBuf, _pEct->PEyeTrack()->GetGazeV(), 20, CV_RGB(0, 0, 255), 3);
}
// UI描画
cvui::image(_MainFrame, 0, 0, _Logo);
cvui::beginColumn(_MainFrame, 10, 100, 140, -1, 10);
switch (_pEct->PWorker()->GetAppStatus()) {
case APP_STATUS::IDLE:
if (cvui::button(140, 30, "CALIBRATION")) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_START);
}
if (cvui::button(140, 30, "START")) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_START);
}
break;
case APP_STATUS::STIM:
if (cvui::button(140, 30, "STOP")) {
_pEct->PDSMovie()->StopMovie();
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_STOP);
}
//cvui::trackbar(140, &targetSize, (float)0, (float)3.0);
//cvui::checkbox("Show Eyes", &fShowEyesPos);
//cvui::text(fContact ? "Eyes Contact!" : "No contact");
break;
}
cvui::printf("View Gaze %.0f, %.0f", _pEct->PEyeTrack()->GetGazeV().x, _pEct->PEyeTrack()->GetGazeV().y);
//cvui::printf("Img Gaze %.2f, %.2f", _pEct->GetGazeI().x, _pEct->GetGazeI().y);
cvui::text(_pEct->PMarker()->IsDetected() ? "AR Markers OK" : "AR Markers NG");
cvui::printf("Battery %d %%", _pEct->PTobiiREST()->GetBatteryLevel());
if (_pEct->PStimulus()->GetStimNo() >= 0) {
cvui::printf("%s", _pEct->PStimulus()->GetStimFile().c_str());
cvui::printf("Showing %.1f s", _pEct->PStimulus()->GetStimTime() / 1000.F);
}
bool snapshot = cvui::button(140, 30, "SNAPSHOT");
if (cvui::button(140, 30, "QUIT")) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END);
}
if (_pEct->PBitalMonitor()->IsUseDevice()) {
cvui::printf("HeartBeat (bpm)");
cvui::text(cv::format("%3d", _pEct->PBitalMonitor()->GetHB()), 2.0);
} else {
cvui::printf("HeartBeat no device");
}
cvui::endColumn();
// 画像表示
cvui::text(_MainFrame, SCENE_BUFFER_POS.x, 100, "SCENE CAMERA");
cv::Mat sceneResized = gocv::KeepAspectResize(sceneBuf, DISP_SIZE.width);
cvui::image(_MainFrame, SCENE_BUFFER_POS.x, SCENE_BUFFER_POS.y, sceneResized);
cvui::text(_MainFrame, DISP_IMAGE_POS.x, 100, "STIMULUS IMAGE");
cv::Mat dispResized = gocv::KeepAspectResize(dispBuf, DISP_SIZE.width);
cvui::image(_MainFrame, DISP_IMAGE_POS.x, DISP_IMAGE_POS.y, dispResized);
//// 刺激動画
//if (_pEct->PStimulus()->IsNewMovie()) {
// std::wstring movie = _pEct->PStimulus()->GetMovie();
// //if (movie.size() < 1 || movie == L"STOP") {
// // mwut::DebugPrintf(_T("ECTMSG::MOVIE_STOP [%s]\n"), movie.c_str());
// // ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::MOVIE_STOP);
// //} else {
// if (_pEct->PDSMovie()->PlayMovie(movie)) {
// ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::MOVIE_START);
// } else {
// _pEct->PDSMovie()->StopMovie();
// ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SYSTEM_ERROR);
// }
// //}
//}
// 動画の末尾まで再生チェック
if (_pEct->PDSMovie()->IsReachToEnd()) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::MOVIE_END);
}
// 画面表示
cvui::update();
cv::imshow(WIN_MAIN, _MainFrame);
if (!_pEct->PDSMovie()->IsPlaying()) cv::imshow(WIN_DISP, _FullDispBuf);
if (snapshot) cv::imwrite(SNAPSHOT_FILE, _MainFrame);
// ウインドウ×ボタンで閉じた時
if (_hWndMain != ::FindWindowA(NULL, WIN_MAIN.c_str())) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END);
}
// 初回のGUI表示が完了したところで起動メッセージ発信
if (_Booting) {
_Booting = false;
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_START);
}
return true;
}
// イベント処理
bool ECTrainerGUI::EventProc(MSG& msg) {
switch (msg.message) {
case WM_CHAR: // キー入力
if (msg.wParam == 27) {
#ifdef _DEBUG
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END);
#endif
}
mwut::DebugPrintf(_T("Key '%c'\n"), msg.wParam);
break;
case (int)ECTMSG::MOVIE_START:
if (!_pEct->PDSMovie()->PlayMovie(_pEct->PStimulus()->GetMovie())) {
((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SYSTEM_ERROR);
}
break;
case (int)ECTMSG::MOVIE_STOP:
_pEct->PDSMovie()->StopMovie();
break;
default:
//mwut::DebugPrintf(_T("Event %04X\n"), msg.message);
break;
}
return true;
}
// 刺激画像バッファに画像を設定
void ECTrainerGUI::MakeFullDispBuffer(cv::Mat& img) {
_DispImageHeight = img.rows * IMAGE_WIDTH / img.cols;
cv::Mat buf(_FullDispBuf.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(_FullDispBuf);
}