diff --git a/ECTrainer2/ECTrainerGUI.cpp b/ECTrainer2/ECTrainerGUI.cpp index 3bb3090..ff2cca9 100644 --- a/ECTrainer2/ECTrainerGUI.cpp +++ b/ECTrainer2/ECTrainerGUI.cpp @@ -75,7 +75,7 @@ cv::Mat sceneBuf = Ect()->PImageProc()->GetImage(); if (sceneBuf.empty()) { sceneBuf = _blank; - nkc::wut::DebugPrintf(_T("no scene buffer\n")); + //nkc::wut::DebugPrintf(_T("no scene buffer\n")); } // 表示バッファ生成(実験中はWorkerクラスの生成画像,非実験中はStimulusクラスの画像を使う) @@ -84,7 +84,7 @@ cv::Mat dispBuf = Ect()->PWorker()->GetTargetImg().clone(); if (dispBuf.empty()) { dispBuf = _blank; - nkc::wut::DebugPrintf(_T("no display buffer\n")); + //nkc::wut::DebugPrintf(_T("no display buffer\n")); } // 注視点の表示 diff --git a/ECTrainer2/StimConfig.txt b/ECTrainer2/StimConfig.txt index 0fbd555..46da9c0 100644 --- a/ECTrainer2/StimConfig.txt +++ b/ECTrainer2/StimConfig.txt @@ -1,9 +1,9 @@ ���,�h���i�摜�E����j,�񎦎��ԁi�b�j,�^�[�Q�b�g���,�k������ -1,../images/SoftCalib1.png,5,_,_ -1,../images/SoftCalib2.png,5,_,_ -1,../images/SoftCalib3.png,5,_,_ -1,../images/SoftCalib4.png,5,_,_ +3,../images/SoftCalib/SoftCalib1.png,3,../images/SoftCalib/SoftCalib1.csv,_ +3,../images/SoftCalib/SoftCalib2.png,3,../images/SoftCalib/SoftCalib2.csv,_ +3,../images/SoftCalib/SoftCalib3.png,3,../images/SoftCalib/SoftCalib3.csv,_ +3,../images/SoftCalib/SoftCalib4.png,3,../images/SoftCalib/SoftCalib4.csv,_ 1,../images/visit01/visit01_001.png,5,_,_ 2,../movies/visit01/visit01_001.avi,10,../movies/visit01/visit01_001.csv,../movies/visit01/visit01_001vga.avi diff --git a/ECTrainer2/Stimulus.cpp b/ECTrainer2/Stimulus.cpp index 95720ac..10e7c03 100644 --- a/ECTrainer2/Stimulus.cpp +++ b/ECTrainer2/Stimulus.cpp @@ -63,7 +63,7 @@ } // 動画を読み込む - if (_StimInfoSet[_StimNo].type == 2 && _cap.isOpened()) { + if (_StimInfoSet[_StimNo].type == StimInfo::TYPE_MOVIE && _cap.isOpened()) { _cap.set(cv::CAP_PROP_POS_MSEC, eTime); cv::Mat frame; _cap >> frame; @@ -121,7 +121,7 @@ bool Stimulus::MoveNext() { if (_StimNo < 0) return false; - if (_StimInfoSet[_StimNo].type == 2) { + if (_StimInfoSet[_StimNo].type == StimInfo::TYPE_MOVIE) { if (_cap.isOpened()) _cap.release(); Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_STOP); } @@ -143,12 +143,10 @@ this->LoadMovieObject(newStimNo); } - if (_StimInfoSet[newStimNo].type == 1) { - this->SetImage(_StimInfoSet[newStimNo].filepath); - } else { + if (_StimInfoSet[newStimNo].type == StimInfo::TYPE_MOVIE) { _cap.open(_StimInfoSet[newStimNo].smallmovie); if (!_cap.isOpened()) { - Ect()->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()); Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR); @@ -156,6 +154,8 @@ } _Movie.Put(nkc::wut::Multi2Wide(_StimInfoSet[newStimNo].filepath)); Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_START); + } else { + this->SetImage(_StimInfoSet[newStimNo].filepath); } _StimTimer.Reset(); diff --git a/ECTrainer2/Stimulus.h b/ECTrainer2/Stimulus.h index fc3f398..95398b3 100644 --- a/ECTrainer2/Stimulus.h +++ b/ECTrainer2/Stimulus.h @@ -15,6 +15,7 @@ enum class STIM_PAGE; struct StimInfo { + enum {TYPE_IMAGE = 1, TYPE_MOVIE = 2, TYPE_CALIB = 3}; int type; // 1:画像 2:動画 std::string filepath; // 提示ファイル名(パス付き) std::string filename; // 提示ファイル名(パスなし) @@ -92,4 +93,6 @@ std::vector GetMovieObject(float eTime = -1.f); // 合計実験時間を取得 float GetTotalExpTime() { return _TotalExpTime; } + // ソフトウェアキャリブレーション実行中かどうか + bool IsSoftCalib() { return _StimNo < 0 ? false : _StimInfoSet[_StimNo].type == StimInfo::TYPE_CALIB; } }; diff --git a/ECTrainer2/Worker.cpp b/ECTrainer2/Worker.cpp index ded681f..9d85af6 100644 --- a/ECTrainer2/Worker.cpp +++ b/ECTrainer2/Worker.cpp @@ -21,6 +21,7 @@ , _FullScreenImage() , _FBLevel(1) , _TrainingLevel(1) + , _Shift(0, 0) { _pExpTimer = new nkc::HPTimer(); _pContactTimer = new nkc::HPTimer(); @@ -51,7 +52,7 @@ int stimNo = Ect()->PStimulus()->GetStimNo(); // 注視点を画像座標に変換 - auto gazeI = gazeV.x < 0 ? cv::Point2f(-1, -1) : Ect()->PMarker()->ConvV2I(gazeV); + auto gazeI = gazeV.x < 0 ? cv::Point2f(-1, -1) : Ect()->PMarker()->ConvV2I(gazeV) - _Shift; // 表示画像取得 if (_StimImage.empty() || Ect()->PStimulus()->IsNewDisplay()) { @@ -91,21 +92,40 @@ // 画像上の注視点描画 cv::circle(stimImg, gazeI * scale, (int)(20 * scale), CV_RGB(0, 0, 255), 3); - if (hit >= 0) { - _ContactTime += frameInterval; + if (Ect()->PStimulus()->IsSoftCalib()) { + // ソフトウェアキャリブレーション + if (Ect()->PStimulus()->GetStimTime() >= SHIFT_LOG_STARTTIME && elems.size() > 0) { + auto shift = gazeI - cv::Point2f(elems[0].x, elems[0].y); + _ShiftLog.push_back(shift ); + //nkc::wut::DebugPrintf(_T("Shift %.1f,%.1f\n"), shift.x, shift.y); + } + } else { + if (hit >= 0) { + _ContactTime += frameInterval; - if (_ContactTime >= (double)(FEEDBACK_TIME * _FBLevel)) { - this->PostMsg((int)ECTMSG::FB_OK + _FBLevel - 1); - fb = _FBLevel++; - if (_FBLevel > 5) { - _TrainingLevel++; - this->ResetParams(); // 最終FB後にリセット + if (_ContactTime >= (double)(FEEDBACK_TIME * _FBLevel)) { + this->PostMsg((int)ECTMSG::FB_OK + _FBLevel - 1); + fb = _FBLevel++; + if (_FBLevel > 5) { + _TrainingLevel++; + this->ResetParams(); // 最終FB後にリセット + } } } } } _TargetImage.Put(stimImg); + + // ずれ解析 + if (!Ect()->PStimulus()->IsSoftCalib() && _ShiftLog.size() > 0) { + cv::Point2f sum(0,0); + for (auto& x : _ShiftLog) sum += x; + _Shift.x = sum.x / _ShiftLog.size(); + _Shift.y = sum.y / _ShiftLog.size(); + nkc::wut::DebugPrintf(_T("Mean Shift %.1f,%.1f\n"), _Shift.x, _Shift.y); + _ShiftLog.clear(); + } } else { _TargetImage.Put(_StimImage); } @@ -116,6 +136,9 @@ _ftprintf(_fpLogData, _T(",%d"), stimNo); // 刺激データ番号 _ftprintf(_fpLogData, _T(",%.3f"), Ect()->PStimulus()->GetStimTime() / 1000.); // 刺激提示の経過時間 _ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeV.x, gazeV.y); // 注視点(視野カメラ座標) + auto logShift = _ShiftLog.size() > 0 && Ect()->PStimulus()->GetStimTime() >= SHIFT_LOG_STARTTIME + && gazeI.x >= 0 ? _ShiftLog.back() : _Shift; + _ftprintf(_fpLogData, _T(",%.1f,%.1f"), logShift.x, logShift.y); // ずれ _ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeI.x, gazeI.y); // 注視点(画像座標) _ftprintf(_fpLogData, _T(",%d"), hit + 1); // ターゲット判定 _ftprintf(_fpLogData, _T(",%.2f"), _ContactTime / 1000.); // 目標コンタクト時間 @@ -261,7 +284,7 @@ } // ヘッダ行 - _ftprintf(_fpLogData, _T("time,stimNo,stimTime,gazeVx,gazeVy,gazeIx,gazeIy,target,contact time,Feedback,RR," + _ftprintf(_fpLogData, _T("time,stimNo,stimTime,gazeVx,gazeVy,shift x,shift y,gazeIx,gazeIy,target,contact time,Feedback,RR," "H11,H12,H13,H21,H22,H23,H31,H32,H33\n")); } diff --git a/ECTrainer2/Worker.h b/ECTrainer2/Worker.h index e5619bb..77c2141 100644 --- a/ECTrainer2/Worker.h +++ b/ECTrainer2/Worker.h @@ -27,6 +27,7 @@ const TCHAR* DATA_LOG_FILE = _T("LogData"); const TCHAR* EVENT_LOG_FILE = _T("LogEvent.txt"); const int FEEDBACK_TIME = 3000; // フィードバックの時間(1レベル)msec + const int SHIFT_LOG_STARTTIME = 2000; // 注視点ずれ記録開始時間 const float TRAINING_LEVEL_EFFECT = 0.1F; // トレーニングレベルによる目標領域縮小量 APP_STATUS _AppStatus; // アプリケーション状態 FILE* _fpLogData; // データログファイルポインタ @@ -40,6 +41,8 @@ int _FBLevel; // フィードバックレベル 1~5 HANDLE _Trigger; // 処理開始トリガ int _TrainingLevel; // トレーニングレベル(高いほど難度UP) + std::vector _ShiftLog; // 注視点のずれ記録 + cv::Point2f _Shift; // 注視点のずれ // ECTrainerインスタンス取得 ECTrainer* Ect() { return (ECTrainer*)_pUserdata; } @@ -81,4 +84,6 @@ void SetTrigger() { ::SetEvent(_Trigger); } // トレーニングレベル取得 int GetTrainingLevel() { return _TrainingLevel; } + // ずれ記録のクリア + void ClearShift() { _ShiftLog.clear(); } };