diff --git a/ECTrainer2/BaseProcess.cpp b/ECTrainer2/BaseProcess.cpp index 5b4dd51..eccdead 100644 --- a/ECTrainer2/BaseProcess.cpp +++ b/ECTrainer2/BaseProcess.cpp @@ -1,22 +1,20 @@ #include "BaseProcess.h" -#include "ECTrainer.h" -#include "HPTimer.h" -#include "nkcWinUtils.h" // コンストラクタ -BaseProcess::BaseProcess(ECTrainer* pEct) - : _pEct(pEct) +BaseProcess::BaseProcess(void* pUD) + : _pUserdata(pUD) , _messageQueReady(false) , _threadHandle(NULL) , _threadID(0) , _mainThread(false) + , _fpsMeasureTime(5000) { } // スレッド起動 bool BaseProcess::Launch() { - _threadHandle = ::CreateThread(NULL, 0, ThreadEntry, this, 0, &_threadID); - return true; + _threadHandle = ::CreateThread(NULL, 0, this->ThreadEntry, this, 0, &_threadID); + return (_threadHandle != NULL); } // 初期化 @@ -30,13 +28,14 @@ _mainThread = true; _threadID = ::GetCurrentThreadId(); // メインスレッドID取得 } - //mwut::DebugPrintf(_T("Thread %d start\n"), _threadID); + + // ループ直前処理 + if (!this->InitLoop()) return false; // メッセージループ MSG msg; - nkc::HPTimer timer; + DWORD beginTime = ::GetTickCount(); int fpsCounter = 0; - const int fpsCount = 100; while (1) { if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!::GetMessage(&msg, NULL, 0, 0)) break; @@ -47,13 +46,15 @@ ::DispatchMessage(&msg); } } else { - if (this->Routine() && this->ClassName().size() > 0) { + if (this->Routine()) { // FPS処理 - if (++fpsCounter >= fpsCount) { + ++fpsCounter; + DWORD current = ::GetTickCount(); + if (current - beginTime >= _fpsMeasureTime) { + double fps = fpsCounter * 1000.0 / (current - beginTime); fpsCounter = 0; - double fps = fpsCount * 1000.0 / timer.Interval(); - nkc::wut::DebugPrintf(_T("%s : %.1f fps\n"), - nkc::wut::Multi2Wide(this->ClassName()).c_str(), fps); + beginTime = current; + this->FPS(fps); } } } @@ -62,18 +63,6 @@ return true; } -// 非メッセージ時の処理 -bool BaseProcess::Routine() { - - return true; -} - -// メッセージ処理 -bool BaseProcess::EventProc(MSG &msg) { - //mwut::DebugPrintf(_T("BaseProcess::EventProc Thread %d msg %d\n"), _threadID, msg.message); - return true; -} - // スレッド起動点 DWORD WINAPI BaseProcess::ThreadEntry(LPVOID lpParameter) { if (!((BaseProcess*)lpParameter)->MainLoop()) return 1; @@ -84,13 +73,13 @@ bool BaseProcess::WaitForExit(DWORD timeOut) { if (!_threadHandle) return false; bool rv = (::WaitForSingleObject(_threadHandle, timeOut) == WAIT_OBJECT_0); - if (rv) CloseHandle(_threadHandle); + if (rv) ::CloseHandle(_threadHandle); return rv; } // メッセージを送る(非同期) -bool BaseProcess::PostMsg(ECTMSG msg, WPARAM wp, LPARAM lp) { +bool BaseProcess::PostMsg(int msgid, WPARAM wp, LPARAM lp) { if (!_messageQueReady || !_threadID) return false; // メッセージキューの準備確認 - ::PostThreadMessage(_threadID, (int)msg, wp, lp); + ::PostThreadMessage(_threadID, msgid, wp, lp); return true; } diff --git a/ECTrainer2/BaseProcess.h b/ECTrainer2/BaseProcess.h index cc75776..1bd41d7 100644 --- a/ECTrainer2/BaseProcess.h +++ b/ECTrainer2/BaseProcess.h @@ -1,42 +1,43 @@ #pragma once #include -#include - -class ECTrainer; -enum class ECTMSG; // マルチスレッド繰り返し処理の基本クラス class BaseProcess { -protected: - ECTrainer* _pEct; +private: bool _messageQueReady; // メッセージキューの準備状況 HANDLE _threadHandle; // スレッドハンドル DWORD _threadID; // スレッドID bool _mainThread; // メイン(GUI)スレッドかどうか + int _fpsMeasureTime; // FPSを算出する時間(ms) + +protected: + void* _pUserdata; // スレッドエントリポイント static DWORD WINAPI ThreadEntry(LPVOID lpParameter); + // ループ直前処理 + virtual bool InitLoop() { return true; } // 非メッセージ時の処理 - virtual bool Routine(); + virtual bool Routine() { return true; } // メッセージ処理 - virtual bool EventProc(MSG& msg); - // クラス名取得(派生クラスでオーバーライドすればFPS表示) - virtual std::string ClassName() { return ""; } // FPS表示 + virtual bool EventProc(MSG& msg) { return true; } + // FPS表示 + virtual void FPS(double fps) { } public: // コンストラクタ - BaseProcess(ECTrainer* pEct); - // スレッド起動 - virtual bool Launch(); + BaseProcess(void* pUD); // 初期化 virtual bool Init(); - // スレッド本体 + // 新スレッドでループ処理開始 + virtual bool Launch(); + // スレッドを生成せずループ処理開始 virtual bool MainLoop(); // スレッド終了待ち 正常終了でハンドル閉じる virtual bool WaitForExit(DWORD timeOut = 3000); // メッセージを送る(非同期) - virtual bool PostMsg(ECTMSG msg, WPARAM wp = 0, LPARAM lp = 0); + virtual bool PostMsg(int msgid, WPARAM wp = 0, LPARAM lp = 0); }; diff --git a/ECTrainer2/BitalMonitor.cpp b/ECTrainer2/BitalMonitor.cpp index 3f7b3a7..071ce24 100644 --- a/ECTrainer2/BitalMonitor.cpp +++ b/ECTrainer2/BitalMonitor.cpp @@ -1,4 +1,5 @@ -#include "BitalMonitor.h" +#include "BaseProcess.h" +#include "BitalMonitor.h" #include "ECTrainer.h" #include "Worker.h" @@ -26,13 +27,13 @@ DWORD readBytes = _Com.Receive((BYTE*)comBuf, BUF_LEN - 1); if (readBytes < 1) continue; if ((*comBuf >= '0' && *comBuf <= '9') || *comBuf == '#' || *comBuf == LF) { - _pEct->PWorker()->EventLog(_T("Bital device connected.")); + Ect()->PWorker()->EventLog(_T("Bital device connected.")); _useDevice = true; return true; } } - _pEct->PWorker()->EventLog(_T("Bital device not found.")); + Ect()->PWorker()->EventLog(_T("Bital device not found.")); _useDevice = false; return true; } @@ -41,7 +42,7 @@ bool BitalMonitor::Routine() { if (!_useDevice) { - Sleep(100); + ::Sleep(100); return true; } @@ -83,7 +84,7 @@ _lastRR = 0; } - Sleep(10); + ::Sleep(10); return true; } diff --git a/ECTrainer2/BitalMonitor.h b/ECTrainer2/BitalMonitor.h index dd00e3a..01d662e 100644 --- a/ECTrainer2/BitalMonitor.h +++ b/ECTrainer2/BitalMonitor.h @@ -1,9 +1,11 @@ #pragma once -#include "BaseProcess.h" #include "ComPort.h" #include "RingBuffer.h" +class ECTrainer; +class BaseProcess; + class BitalMonitor : public BaseProcess { @@ -18,7 +20,8 @@ DWORD _lastRR; // 前回のRR取得時間(タイムアウト時 0) char lastBuf[BUF_LEN]; - //std::string ClassName() { return "BitalMonitor"; } // FPS表示 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } public: diff --git a/ECTrainer2/ECTrainer.cpp b/ECTrainer2/ECTrainer.cpp index d83d284..873310d 100644 --- a/ECTrainer2/ECTrainer.cpp +++ b/ECTrainer2/ECTrainer.cpp @@ -66,7 +66,7 @@ // アプリケーションを停止 void ECTrainer::StopApp() { - for (int i = 0; i < (int)PROC::NUM; i++) _pProcs[i]->PostMsg(ECTMSG::QUIT); + for (int i = 0; i < (int)PROC::NUM; i++) _pProcs[i]->PostMsg((int)ECTMSG::QUIT); } // メッセージボックス表示 diff --git a/ECTrainer2/ECTrainerGUI.cpp b/ECTrainer2/ECTrainerGUI.cpp index 1608786..b827c59 100644 --- a/ECTrainer2/ECTrainerGUI.cpp +++ b/ECTrainer2/ECTrainerGUI.cpp @@ -56,7 +56,7 @@ _Logo = cv::imread(HEADER_FILE); _hWndMain = ::FindWindowA(NULL, WIN_MAIN.c_str()); - _pEct->PDSMovie()->Init(displays[0]); + Ect()->PDSMovie()->Init(displays[0]); return true; } @@ -68,73 +68,73 @@ _MainFrame = cv::Scalar(49, 52, 49); // 視野画像バッファ生成 - cv::Mat sceneBuf = _pEct->PImageProc()->GetImage(); + cv::Mat sceneBuf = Ect()->PImageProc()->GetImage(); if (sceneBuf.empty()) sceneBuf = _blank; // 表示バッファ生成(実験中はWorkerクラスの生成画像,非実験中はStimulusクラスの画像を使う) - //bool fTarget = _pEct->PWorker()->GetAppStatus() == APP_STATUS::STIM; - //bool isNew = fTarget ? : _pEct->PStimulus()->IsNewDisplay(); - cv::Mat dispBuf = _pEct->PWorker()->GetTargetImg().clone(); + //bool fTarget = Ect()->PWorker()->GetAppStatus() == APP_STATUS::STIM; + //bool isNew = fTarget ? : Ect()->PStimulus()->IsNewDisplay(); + cv::Mat dispBuf = Ect()->PWorker()->GetTargetImg().clone(); if (dispBuf.empty()) dispBuf = _blank; // 注視点の表示 - if (_pEct->PEyeTrack()->GetGazeV().x >= 0) { - cv::circle(sceneBuf, _pEct->PEyeTrack()->GetGazeV(), 20, CV_RGB(0, 0, 255), 3); + if (Ect()->PEyeTrack()->GetGazeV().x >= 0) { + cv::circle(sceneBuf, Ect()->PEyeTrack()->GetGazeV(), 20, CV_RGB(0, 0, 255), 3); } // ヘッダー描画 cv::Mat header = _Logo.clone(); - double contactTime = _pEct->PWorker()->GetContactTime() / 1000.0; - double expTime = _pEct->PWorker()->GetExpTime() / 1000.0; - double progress = expTime / _pEct->PStimulus()->GetTotalExpTime(); + double contactTime = Ect()->PWorker()->GetContactTime() / 1000.0; + double expTime = Ect()->PWorker()->GetExpTime() / 1000.0; + double progress = expTime / Ect()->PStimulus()->GetTotalExpTime(); cv::rectangle(header, cv::Rect(1000, 14, (int)(400 * progress), 22), CV_RGB(0, 199, 0), cv::FILLED); cv::rectangle(header, cv::Rect(1000, 54, (int)(400 * contactTime / 15.0), 22), CV_RGB(240, 116, 95), cv::FILLED); cvui::image(_MainFrame, 0, 0, header); cvui::text(_MainFrame, 850, 18, cv::format("%2d:%02d/%2d:%02d", (int)expTime / 60, (int)expTime % 60, - (int)_pEct->PStimulus()->GetTotalExpTime()/60, (int)_pEct->PStimulus()->GetTotalExpTime() % 60), 0.6, 0); + (int)Ect()->PStimulus()->GetTotalExpTime()/60, (int)Ect()->PStimulus()->GetTotalExpTime() % 60), 0.6, 0); cvui::text(_MainFrame, 880, 58, cv::format("%5.1f sec", contactTime), 0.6, 0); // UI描画 cvui::beginColumn(_MainFrame, 10, 100, 140, -1, 10); - switch (_pEct->PWorker()->GetAppStatus()) { + switch (Ect()->PWorker()->GetAppStatus()) { case APP_STATUS::IDLE: if (cvui::button(140, 30, "CALIBRATION")) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_START); + Ect()->PWorker()->PostMsg((int)ECTMSG::CALIB_START); } if (cvui::button(140, 30, "START")) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_START); + Ect()->PWorker()->PostMsg((int)ECTMSG::EXP_START); } break; case APP_STATUS::STIM: //if (cvui::button(140, 30, "NEXT")) { - // _pEct->PDSMovie()->StopMovie(); - // ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_STOP); + // Ect()->PDSMovie()->StopMovie(); + // ((BaseProcess*)Ect()->PWorker())->PostMsg(ECTMSG::EXP_STOP); //} if (cvui::button(140, 30, "STOP")) { - _pEct->PDSMovie()->StopMovie(); - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_STOP); + Ect()->PDSMovie()->StopMovie(); + Ect()->PWorker()->PostMsg((int)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); + cvui::printf("View Gaze %.0f, %.0f", Ect()->PEyeTrack()->GetGazeV().x, Ect()->PEyeTrack()->GetGazeV().y); + //cvui::printf("Img Gaze %.2f, %.2f", Ect()->GetGazeI().x, Ect()->GetGazeI().y); + cvui::text(Ect()->PMarker()->IsDetected() ? "AR Markers OK" : "AR Markers NG"); + cvui::printf("Battery %d %%", Ect()->PTobiiREST()->GetBatteryLevel()); + if (Ect()->PStimulus()->GetStimNo() >= 0) { + cvui::printf("%s", Ect()->PStimulus()->GetStimFile().c_str()); + cvui::printf("Showing %.1f s", Ect()->PStimulus()->GetStimTime() / 1000.F); } bool snapshot = cvui::button(140, 30, "SNAPSHOT"); if (cvui::button(140, 30, "QUIT")) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END); + Ect()->PWorker()->PostMsg((int)ECTMSG::SOFTWARE_END); } - if (_pEct->PBitalMonitor()->IsUseDevice()) { + if (Ect()->PBitalMonitor()->IsUseDevice()) { cvui::printf("HeartBeat (bpm)"); - cvui::text(cv::format("%3d", _pEct->PBitalMonitor()->GetHB()), 2.0); + cvui::text(cv::format("%3d", Ect()->PBitalMonitor()->GetHB()), 2.0); } else { cvui::printf("HeartBeat no device"); } @@ -150,8 +150,8 @@ cvui::image(_MainFrame, DISP_IMAGE_POS.x, DISP_IMAGE_POS.y, dispResized); // 動画の末尾まで再生チェック - if (_pEct->PDSMovie()->IsReachToEnd()) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::MOVIE_END); + if (Ect()->PDSMovie()->IsReachToEnd()) { + Ect()->PWorker()->PostMsg((int)ECTMSG::MOVIE_END); } // メイン画面表示 @@ -159,8 +159,8 @@ cv::imshow(WIN_MAIN, _MainFrame); // 全画面表示 - if (_pEct->PWorker()->IsNewFullScreenImg() && !_pEct->PDSMovie()->IsPlaying()) { - this->MakeFullDispBuffer(_pEct->PWorker()->GetFullScreenImg()); + if (Ect()->PWorker()->IsNewFullScreenImg() && !Ect()->PDSMovie()->IsPlaying()) { + this->MakeFullDispBuffer(Ect()->PWorker()->GetFullScreenImg()); cv::imshow(WIN_DISP, _FullDispBuf); } @@ -169,7 +169,7 @@ // ウインドウ×ボタンで閉じた時 if (_hWndMain != ::FindWindowA(NULL, WIN_MAIN.c_str())) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END); + Ect()->PWorker()->PostMsg((int)ECTMSG::SOFTWARE_END); } Sleep(1); @@ -184,22 +184,22 @@ case WM_CHAR: // キー入力 if (msg.wParam == 27) { #ifdef _DEBUG - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SOFTWARE_END); + Ect()->PWorker()->PostMsg((int)ECTMSG::SOFTWARE_END); #endif } nkc::wut::DebugPrintf(_T("Key '%c'\n"), msg.wParam); break; case (int)ECTMSG::MOVIE_START: - if (!_pEct->PDSMovie()->PlayMovie(_pEct->PStimulus()->GetMovie())) { - _pEct->PWorker()->EventLog((_T("Can't play movie file :") - + _pEct->PStimulus()->GetMovie()).c_str()); - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SYSTEM_ERROR); + if (!Ect()->PDSMovie()->PlayMovie(Ect()->PStimulus()->GetMovie())) { + Ect()->PWorker()->EventLog((_T("Can't play movie file :") + + Ect()->PStimulus()->GetMovie()).c_str()); + Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR); } break; case (int)ECTMSG::MOVIE_STOP: - _pEct->PDSMovie()->StopMovie(); + Ect()->PDSMovie()->StopMovie(); break; default: @@ -229,3 +229,8 @@ cv::resize(img, roi, roi.size()); buf.copyTo(_FullDispBuf); } + +// FPS表示 +void ECTrainerGUI::FPS(double fps) { + nkc::wut::DebugPrintf(_T("[GUI] %.1f fps\n"), fps); +} diff --git a/ECTrainer2/ECTrainerGUI.h b/ECTrainer2/ECTrainerGUI.h index 231d331..1bd5c8e 100644 --- a/ECTrainer2/ECTrainerGUI.h +++ b/ECTrainer2/ECTrainerGUI.h @@ -27,14 +27,16 @@ cv::Mat _blank; // ブランク HWND _hWndMain; // メインウインドウのハンドル + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } // 基本処理 bool Routine(); // イベント処理 bool EventProc(MSG& msg); // 全画面表示バッファの生成 void MakeFullDispBuffer(cv::Mat img); - - std::string ClassName() { return "GUI"; } // FPS表示 + // FPS表示 + void FPS(double fps); public: // コンストラクタ diff --git a/ECTrainer2/EyeTrack.cpp b/ECTrainer2/EyeTrack.cpp index 0b9e49f..bc3b290 100644 --- a/ECTrainer2/EyeTrack.cpp +++ b/ECTrainer2/EyeTrack.cpp @@ -1,5 +1,6 @@ #include #include +#include "BaseProcess.h" #include "EyeTrack.h" #include "ECTrainer.h" #include "KeepAlive.h" @@ -16,6 +17,7 @@ , _gpCx(MEAN_BUF_SIZE) , _gpCy(MEAN_BUF_SIZE) { + _pupilD.l = _pupilD.r = 0; } // メインループ @@ -65,7 +67,7 @@ #elif defined(EYEDEVICE_NONE) _GazeV.Put(cv::Point2f(-1.F, -1.F)); - Sleep(10); + ::Sleep(10); return true; #endif } diff --git a/ECTrainer2/EyeTrack.h b/ECTrainer2/EyeTrack.h index 7f1bfa9..681255b 100644 --- a/ECTrainer2/EyeTrack.h +++ b/ECTrainer2/EyeTrack.h @@ -1,10 +1,12 @@ #pragma once -#include "BaseProcess.h" #include "nkcOpenCV.h" #include "RingBuffer.h" #include "MeanBuffer.h" +class ECTrainer; +class BaseProcess; + // LRテンプレート template class LR { @@ -23,7 +25,8 @@ cv::Point2f _gazePoint; MeanBuffer _gpCx, _gpCy; // 移動平均バッファ - //std::string ClassName() { return "EyeTrack"; } // FPS表示 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } public: // コンストラクタ diff --git a/ECTrainer2/ImageProc.cpp b/ECTrainer2/ImageProc.cpp index c623983..c56b474 100644 --- a/ECTrainer2/ImageProc.cpp +++ b/ECTrainer2/ImageProc.cpp @@ -13,13 +13,13 @@ bool ImageProc::Routine() { // シーンカメラのフレーム更新待ち - if (!_pEct->PSceneCamera()->IsNew()) { + if (!Ect()->PSceneCamera()->IsNew()) { Sleep(1); return false; } - cv::Mat img = _pEct->PSceneCamera()->GetImage().clone(); - _pEct->PMarker()->Detect(img); + cv::Mat img = Ect()->PSceneCamera()->GetImage().clone(); + Ect()->PMarker()->Detect(img); _OutputBuf.Put(img); return true; diff --git a/ECTrainer2/ImageProc.h b/ECTrainer2/ImageProc.h index b12f27c..49f62a1 100644 --- a/ECTrainer2/ImageProc.h +++ b/ECTrainer2/ImageProc.h @@ -10,7 +10,8 @@ { nkc::RingBuffer _OutputBuf; - //std::string ClassName() { return "ImageProc"; } // FPS表示 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } public: ImageProc(ECTrainer* pEct); diff --git a/ECTrainer2/KeepAlive.cpp b/ECTrainer2/KeepAlive.cpp index 25ed735..4d99cbf 100644 --- a/ECTrainer2/KeepAlive.cpp +++ b/ECTrainer2/KeepAlive.cpp @@ -1,15 +1,17 @@ #include #include #include -#include "KeepAlive.h" +#include "BaseProcess.h" #include "ECTrainer.h" +#include "KeepAlive.h" #include "nkcWinUtils.h" // コンストラクタ KeepAlive::KeepAlive(ECTrainer* pEct) : BaseProcess(pEct) , _sent(false) , _socket(NULL) - , _count(0) { + , _count(0) +{ _socketAddrLen = sizeof(_socketAddr); memset((char*)&_socketAddr, 0, _socketAddrLen); } diff --git a/ECTrainer2/KeepAlive.h b/ECTrainer2/KeepAlive.h index 7a55773..2fc05bd 100644 --- a/ECTrainer2/KeepAlive.h +++ b/ECTrainer2/KeepAlive.h @@ -1,6 +1,8 @@ #pragma once -#include "BaseProcess.h" + +class BaseProcess; +class ECTrainer; class KeepAlive : public BaseProcess { const int PORT = 49152; // The port on which to listen for incoming data @@ -12,7 +14,8 @@ int _socketAddrLen; // アドレス情報のサイズ int _count; // ループカウント - //std::string ClassName() { return "KeepAlive"; } // FPS表示 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } public: static const int SOCKET_BUF_LEN = 512; // Socket通信バッファサイズ diff --git a/ECTrainer2/SceneCamera.cpp b/ECTrainer2/SceneCamera.cpp index 1633843..cf38a6b 100644 --- a/ECTrainer2/SceneCamera.cpp +++ b/ECTrainer2/SceneCamera.cpp @@ -1,12 +1,11 @@  #include #include - #include "ECTrainer.h" #include "SceneCamera.h" #include "Worker.h" #include "ECTrainerGUI.h" -//#include "nkcWinUtils.h" +#include "nkcWinUtils.h" // コンストラクタ SceneCamera::SceneCamera(ECTrainer* pEct) : BaseProcess(pEct) @@ -19,14 +18,14 @@ // 視界カメラに接続 #if defined(EYEDEVICE_GLASS2) _SceneCam.open("rtsp://" TOBII_ADDR ":8554/live/scene"); - _pEct->PWorker()->EventLog(_T("Tobii Glasses camera connected.")); + Ect()->PWorker()->EventLog(_T("Tobii Glasses camera connected.")); #elif defined(EYEDEVICE_NONE) _SceneCam.open(0); - _pEct->PWorker()->EventLog(_T("Generic camera connected.")); + Ect()->PWorker()->EventLog(_T("Generic camera connected.")); #endif if (!_SceneCam.isOpened()) { MessageBox(NULL, _T("Cannot connect camera."), NULL, 0); - _pEct->PWorker()->EventLog(_T("Cannot connect camera.")); + Ect()->PWorker()->EventLog(_T("Cannot connect camera.")); return false; } // 画像サイズを保存 @@ -44,7 +43,12 @@ cv::Mat scene; _SceneCam >> scene; _buffer.Put(scene); - Sleep(15); // Tobiiのシーンカメラは25FPS 15msのSleepは問題ない + ::Sleep(15); // Tobiiのシーンカメラは25FPS 15msのSleepは問題ない return true; } + +// FPS表示 +void SceneCamera::FPS(double fps) { + nkc::wut::DebugPrintf(_T("[SceneCamera] %.1f fps\n"), fps); +} diff --git a/ECTrainer2/SceneCamera.h b/ECTrainer2/SceneCamera.h index a74462e..c57f9d4 100644 --- a/ECTrainer2/SceneCamera.h +++ b/ECTrainer2/SceneCamera.h @@ -12,7 +12,10 @@ nkc::RingBuffer _buffer; cv::Size _sceneSize; - std::string ClassName() { return "SceneCamera"; } // FPS表示 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } + // FPS表示 + void FPS(double fps); public: SceneCamera(ECTrainer* pEct); diff --git a/ECTrainer2/Stimulus.cpp b/ECTrainer2/Stimulus.cpp index b7e1d3c..c230a15 100644 --- a/ECTrainer2/Stimulus.cpp +++ b/ECTrainer2/Stimulus.cpp @@ -1,6 +1,7 @@ #include "BaseProcess.h" #include "Stimulus.h" #include "ECTrainer.h" +#include "ECTrainerGUI.h" #include "Worker.h" // コンストラクタ @@ -12,7 +13,6 @@ , _TotalExpTime(0) { _pMovieObject = new MovieObject; - //_Movie.Put(L"STOP"); } Stimulus::~Stimulus() { @@ -122,13 +122,13 @@ if (_StimInfoSet[_StimNo].type == 2) { if (_cap.isOpened()) _cap.release(); - ((BaseProcess*)_pEct->PECTrainerGUI())->PostMsg(ECTMSG::MOVIE_STOP); + Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_STOP); } if (_StimNo + 1 < (int)_StimInfoSet.size()) { SetStimulus(_StimNo + 1); - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_NEXT); + Ect()->PWorker()->PostMsg((int)ECTMSG::EXP_NEXT); } else { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::EXP_END); + Ect()->PWorker()->PostMsg((int)ECTMSG::EXP_END); } return true; } @@ -147,14 +147,14 @@ } else { _cap.open(_StimInfoSet[newStimNo].smallmovie); if (!_cap.isOpened()) { - _pEct->PWorker()->EventLog((_T("Can't open small movie file: ") + + Ect()->PWorker()->EventLog((_T("Can't open small movie file: ") + nkc::wut::Multi2Wide(_StimInfoSet[newStimNo].filepath)).c_str()); - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SYSTEM_ERROR); + Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR); return false; } _Movie.Put(nkc::wut::Multi2Wide(_StimInfoSet[newStimNo].filepath)); - ((BaseProcess*)_pEct->PECTrainerGUI())->PostMsg(ECTMSG::MOVIE_START); + Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_START); } _StimTimer.Reset(); @@ -166,9 +166,9 @@ bool Stimulus::SetImage(cv::String imageFile) { auto img = cv::imread(imageFile); if (img.empty()) { - _pEct->PWorker()->EventLog((_T("Can't open image file: ") + + Ect()->PWorker()->EventLog((_T("Can't open image file: ") + nkc::wut::Multi2Wide(imageFile)).c_str()); - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::SYSTEM_ERROR); + Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR); return false; } _Display.Put(img); @@ -189,6 +189,11 @@ return _pMovieObject->GetElements(eTime); } +// FPS表示 +void Stimulus::FPS(double fps) { + nkc::wut::DebugPrintf(_T("[Stimulus] %.1f fps\n"), fps); +} + // 画像にマーカーを描画 //void Stimulus::StimWithMarker() { diff --git a/ECTrainer2/Stimulus.h b/ECTrainer2/Stimulus.h index 8ace699..fcae521 100644 --- a/ECTrainer2/Stimulus.h +++ b/ECTrainer2/Stimulus.h @@ -11,6 +11,7 @@ class BaseProcess; class Marker; +class ECTrainer; enum class STIM_PAGE; struct StimInfo { @@ -45,6 +46,8 @@ cv::VideoCapture _cap; // 動画オブジェクト float _TotalExpTime; + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } // 刺激を設定 bool SetStimulus(int newStimNo); // 動画オブジェクトの読み込み @@ -57,11 +60,11 @@ bool MoveNext(); // 画像読み込み bool SetImage(cv::String imageFile); + // FPS表示 + void FPS(double fps); // 画像にマーカーを描画 //void StimWithMarker(); - std::string ClassName() { return "Stimulus"; } // FPS表示 - public: // コンストラクタ Stimulus(ECTrainer* pEct); diff --git a/ECTrainer2/TobiiREST.cpp b/ECTrainer2/TobiiREST.cpp index 65ce0f4..d76eb1f 100644 --- a/ECTrainer2/TobiiREST.cpp +++ b/ECTrainer2/TobiiREST.cpp @@ -34,8 +34,8 @@ catch (const std::exception & e) { UNREFERENCED_PARAMETER(e); // e.what() がマルチバイト文字列なので使えない - _pEct->PWorker()->EventLog(_T("Can't connect Tobii Device.")); - _pEct->MsgBox(_T("Can't connect Tobii Device."), MB_ICONERROR); + Ect()->PWorker()->EventLog(_T("Can't connect Tobii Device.")); + Ect()->MsgBox(_T("Can't connect Tobii Device."), MB_ICONERROR); return false; } } @@ -62,7 +62,7 @@ if (StartCalib()) { QueryCalibResult(); } else { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_ERR); + Ect()->PWorker()->PostMsg((int)ECTMSG::CALIB_ERR); } break; } @@ -114,16 +114,16 @@ std::wstring status = _T("calibrated"); #endif if (status == _T("failed")) { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_FAILED); + Ect()->PWorker()->PostMsg((int)ECTMSG::CALIB_FAILED); } else { - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_OK); + Ect()->PWorker()->PostMsg((int)ECTMSG::CALIB_OK); } return true; } catch (const std::exception & e) { UNREFERENCED_PARAMETER(e); // e.what() がマルチバイト文字列なので使えない - ((BaseProcess*)_pEct->PWorker())->PostMsg(ECTMSG::CALIB_ERR); + Ect()->PWorker()->PostMsg((int)ECTMSG::CALIB_ERR); nkc::wut::DebugPrintf(_T("Error in TobiiREST::QueryCalibResult()\n")); return false; } diff --git a/ECTrainer2/TobiiREST.h b/ECTrainer2/TobiiREST.h index 2e5f942..366516a 100644 --- a/ECTrainer2/TobiiREST.h +++ b/ECTrainer2/TobiiREST.h @@ -3,6 +3,8 @@ #include "REST_Handler.h" #include "BaseProcess.h" +class ECTrainer; + #define SERVER _T("http://" TOBII_ADDR) class TobiiREST : public BaseProcess @@ -16,8 +18,13 @@ DWORD _lastBatteryQuery; // 前回のバッテリー情報取得時間 + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } + // キャリブレーション開始 bool StartCalib(); + // キャリブレーション結果を取得 bool QueryCalibResult(); + // バッテリー残量を取得 bool QueryBatteryLevel(); // メッセージ処理 virtual bool EventProc(MSG& msg); diff --git a/ECTrainer2/Worker.cpp b/ECTrainer2/Worker.cpp index 6e45852..2bfe2ec 100644 --- a/ECTrainer2/Worker.cpp +++ b/ECTrainer2/Worker.cpp @@ -6,6 +6,8 @@ #include "BitalMonitor.h" #include "Marker.h" #include "MovieObject.h" +#include "TobiiREST.h" +#include "ECTrainerGUI.h" #include "nkcWinUtils.h" #include "HPTimer.h" @@ -41,20 +43,20 @@ bool Worker::Routine() { // 視線情報更新を待つ - if (!_pEct->PEyeTrack()->IsNewGazeV()) { + if (!Ect()->PEyeTrack()->IsNewGazeV()) { Sleep(0); return false; } - cv::Point2f gazeV = (_pEct->PEyeTrack()->GetGazeV()); - int stimNo = _pEct->PStimulus()->GetStimNo(); + cv::Point2f gazeV = (Ect()->PEyeTrack()->GetGazeV()); + int stimNo = Ect()->PStimulus()->GetStimNo(); // 注視点を画像座標に変換 - auto gazeI = gazeV.x < 0 ? cv::Point2f(-1, -1) : _pEct->PMarker()->ConvV2I(gazeV); + auto gazeI = gazeV.x < 0 ? cv::Point2f(-1, -1) : Ect()->PMarker()->ConvV2I(gazeV); // 表示画像取得 - if (_StimImage.empty() || _pEct->PStimulus()->IsNewDisplay()) { - _StimImage = _pEct->PStimulus()->GetDisplay(); + if (_StimImage.empty() || Ect()->PStimulus()->IsNewDisplay()) { + _StimImage = Ect()->PStimulus()->GetDisplay(); _FullScreenImage.Put(_StimImage); } @@ -63,10 +65,10 @@ double dtMin = 0; int fb = 0; double frameInterval = _pContactTimer->Interval(); - std::vector elems = _pEct->PStimulus()->GetMovieObject(); + std::vector elems = Ect()->PStimulus()->GetMovieObject(); if (stimNo >= 0) { cv::Mat stimImg = _StimImage.clone(); - float scale = (float)stimImg.cols / _pEct->PMarker()->IMGSIZE.width; + float scale = (float)stimImg.cols / Ect()->PMarker()->IMGSIZE.width; if (elems.size() > 0) { for (int e = 0; e < elems.size(); e++) { // ターゲット描画 @@ -92,7 +94,7 @@ _ContactTime += frameInterval; if (_ContactTime >= (double)(FEEDBACK_TIME * _FBLevel)) { - this->PostMsg((ECTMSG)((int)ECTMSG::FB_OK + _FBLevel - 1)); + this->PostMsg((int)ECTMSG::FB_OK + _FBLevel - 1); fb = _FBLevel++; if (_FBLevel > 5) this->ResetParams(); // 最終FB後にリセット } @@ -108,15 +110,15 @@ if (stimNo >= 0 && _fpLogData != NULL) { _ftprintf(_fpLogData, _T("%.3f"), _pExpTimer->Elapse() / 1000.); // 経過時間 _ftprintf(_fpLogData, _T(",%d"), stimNo); // 刺激データ番号 - _ftprintf(_fpLogData, _T(",%.3f"), _pEct->PStimulus()->GetStimTime() / 1000.); // 刺激提示の経過時間 + _ftprintf(_fpLogData, _T(",%.3f"), Ect()->PStimulus()->GetStimTime() / 1000.); // 刺激提示の経過時間 _ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeV.x, gazeV.y); // 注視点(視野カメラ座標) _ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeI.x, gazeI.y); // 注視点(画像座標) _ftprintf(_fpLogData, _T(",%d"), hit + 1); // ターゲット判定 _ftprintf(_fpLogData, _T(",%.2f"), _ContactTime / 1000.); // 目標コンタクト時間 _ftprintf(_fpLogData, _T(",%d"), fb); // フィードバック - _ftprintf(_fpLogData, _T(",%d"), _pEct->PBitalMonitor()->GetRR()); // バイタル出力(RR間隔) + _ftprintf(_fpLogData, _T(",%d"), Ect()->PBitalMonitor()->GetRR()); // バイタル出力(RR間隔) // ホモグラフィ行列出力 - cv::Mat h = _pEct->PMarker()->GetHomography(); + cv::Mat h = Ect()->PMarker()->GetHomography(); if (!h.empty() && h.rows == 3 && h.cols == 3) { double* ptr = h.ptr(0); for (int i = 0; i < 9; i++) { @@ -145,28 +147,28 @@ case (int)ECTMSG::SOFTWARE_END: // ソフトウェア終了 this->EventLog(_T("Software End")); - _pEct->StopApp(); + Ect()->StopApp(); break; case (int)ECTMSG::CALIB_START: // キャリブレーション開始 if (_AppStatus == APP_STATUS::IDLE) { _AppStatus = APP_STATUS::CALIB; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_START); - ((BaseProcess*)_pEct->PTobiiREST())->PostMsg(ECTMSG::CALIB_START); + Ect()->PStimulus()->PostMsg((int)ECTMSG::CALIB_START); + Ect()->PTobiiREST()->PostMsg((int)ECTMSG::CALIB_START); this->EventLog(_T("Calibration Start")); } break; case (int)ECTMSG::CALIB_OK: // キャリブレーション成功 _AppStatus = APP_STATUS::IDLE; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_OK); + Ect()->PStimulus()->PostMsg((int)ECTMSG::CALIB_OK); this->EventLog(_T("Calibration Success")); break; case (int)ECTMSG::CALIB_FAILED: // キャリブレーション失敗 case (int)ECTMSG::CALIB_ERR: // キャリブレーションエラー _AppStatus = APP_STATUS::IDLE; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_FAILED); + Ect()->PStimulus()->PostMsg((int)ECTMSG::CALIB_FAILED); this->EventLog(_T("Calibration Failed")); break; @@ -176,7 +178,7 @@ this->EventLog(_T("Experiment Start")); _pExpTimer->Reset(); this->ResetParams(); - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_START); + Ect()->PStimulus()->PostMsg((int)ECTMSG::EXP_START); _AppStatus = APP_STATUS::STIM; } break; @@ -185,7 +187,7 @@ if (_AppStatus == APP_STATUS::STIM) { this->CloseDataLog(); _AppStatus = APP_STATUS::IDLE; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_STOP); + Ect()->PStimulus()->PostMsg((int)ECTMSG::EXP_STOP); this->EventLog(_T("Experiment Stopped")); } break; @@ -194,7 +196,7 @@ if (_AppStatus == APP_STATUS::STIM) { this->CloseDataLog(); _AppStatus = APP_STATUS::IDLE; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_END); + Ect()->PStimulus()->PostMsg((int)ECTMSG::EXP_END); this->EventLog(_T("Experiment Finished")); } break; @@ -207,8 +209,8 @@ case (int)ECTMSG::SYSTEM_ERROR: // システムエラー this->EventLog(_T("System Error")); _AppStatus = APP_STATUS::IDLE; - ((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::SYSTEM_ERROR); - ((BaseProcess*)_pEct->PECTrainerGUI())->PostMsg(ECTMSG::MOVIE_STOP); + Ect()->PStimulus()->PostMsg((int)ECTMSG::SYSTEM_ERROR); + Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_STOP); break; case (int)ECTMSG::FB_OK: // フィードバック:OK @@ -250,7 +252,7 @@ auto filename = DATA_LOG_FILE + nkc::wut::Multi2Wide(nkc::wut::DateTimeStr()) + _T(".csv"); if (_tfopen_s(&_fpLogData, filename.c_str(), _T("w")) != 0) { - _pEct->MsgBox(_T("Can't open data log file."), MB_ICONERROR); + Ect()->MsgBox(_T("Can't open data log file."), MB_ICONERROR); return; } @@ -272,7 +274,7 @@ FILE* fp = NULL; // イベントログファイルポインタ if (_tfopen_s(&fp, EVENT_LOG_FILE, _T("a")) != 0) { - _pEct->MsgBox(_T("Can't open event log file."), MB_ICONERROR); + Ect()->MsgBox(_T("Can't open event log file."), MB_ICONERROR); return; } @@ -296,3 +298,8 @@ double Worker::GetExpTime() { return _AppStatus == APP_STATUS::STIM ? _pExpTimer->Elapse() : 0; } + +// FPS表示 +void Worker::FPS(double fps) { + nkc::wut::DebugPrintf(_T("[Worker] %.1f fps\n"), fps); +} diff --git a/ECTrainer2/Worker.h b/ECTrainer2/Worker.h index 3acd5b2..5ec7f5c 100644 --- a/ECTrainer2/Worker.h +++ b/ECTrainer2/Worker.h @@ -27,7 +27,6 @@ const TCHAR* DATA_LOG_FILE = _T("LogData"); const TCHAR* EVENT_LOG_FILE = _T("LogEvent.txt"); const int FEEDBACK_TIME = 3000; // フィードバックの時間(1レベル)msec - //const int NOHIT_TIME = 100; // 視線が外れたと判定する時間 msec APP_STATUS _AppStatus; // アプリケーション状態 FILE* _fpLogData; // データログファイルポインタ nkc::HPTimer* _pExpTimer; // 実験経過タイマー @@ -38,8 +37,9 @@ cv::Mat _StimImage; // 刺激画像 nkc::RingBuffer _FullScreenImage; // 全画面用画像 int _FBLevel; // フィードバックレベル 1~5 - //bool _IsInside; // 視線がターゲットにあるか + // ECTrainerインスタンス取得 + ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } // 基本処理 bool Routine(); // イベント処理 @@ -50,8 +50,8 @@ void OpenDataLog(); // データログファイルを開く void CloseDataLog(); - - std::string ClassName() { return "Worker"; } // FPS表示 + // FPS表示 + void FPS(double fps); public: // コンストラクタ