#include "BaseProcess.h"
#include "Stimulus.h"
#include "ECTrainer.h"
#include "ECTrainerGUI.h"
#include "Worker.h"
// コンストラクタ
Stimulus::Stimulus(ECTrainer* pEct) : BaseProcess(pEct)
, _StimNo(-1)
, _pMovieObject(NULL)
, _Display()
, _Movie()
, _TotalExpTime(0)
, _Stage(0)
{
_pMovieObject = new MovieObject;
}
Stimulus::~Stimulus() {
nkc::wut::SafeDelete((void**)&_pMovieObject);
}
// 初期化
bool Stimulus::Init() {
_Display.Put(cv::imread(OPENING_FILE));
// 設定ファイルをテーブルに読み込み
nkc::STR_TABLE table;
auto configFile = cv::format(STIM_CONFIG_FILE.c_str(), Ect()->Visit());
if (!nkc::wut::ReadTable(configFile, table)) {
std::cerr << "Can't open config file : " << configFile << std::endl;
return false;
}
// テーブルから構造体へ格納
_TotalExpTime = 0;
_StimInfoSet.clear();
_StageMax = 0;
for (int r = 0; r < table.size(); r++) {
if (table[r].size() != 6 || atoi(table[r][0].c_str()) < 1) continue;
StimInfo st;
st.type = atoi(table[r][0].c_str());
st.filepath = table[r][1];
int idx = (int)st.filepath.find_last_of("/\\");
st.filename = idx == std::string::npos ? st.filepath : st.filepath.substr(idx + 1);
st.dulation = (float)atof(table[r][2].c_str());
st.csvfile = table[r][3];
st.smallmovie = table[r][4];
st.stage = atoi(table[r][5].c_str());
if (st.stage > _StageMax) _StageMax = st.stage;
if (st.filepath.size() > 0) {
_StimInfoSet.push_back(st);
_TotalExpTime += st.dulation;
}
}
if (_StimInfoSet.size() < 1) {
std::cerr << "Config file error : " << configFile << std::endl;
return false;
}
return true;
}
// 基本処理
bool Stimulus::Routine() {
if (_StimNo >= 0) {
double eTime = _StimTimer.Elapse();
// 刺激切り替えタイムキーパー
if (eTime / 1000. > (double)_StimInfoSet[_StimNo].dulation) {
this->MoveNext();
}
// 動画を読み込む
if (_StimInfoSet[_StimNo].type == StimInfo::TYPE_MOVIE && _cap.isOpened()) {
_cap.set(cv::CAP_PROP_POS_MSEC, eTime);
cv::Mat frame;
_cap >> frame;
if (!frame.empty()) _Display.Put(frame);
}
}
Sleep(15);
return true;
}
// イベント処理
bool Stimulus::EventProc(MSG& msg) {
switch (msg.message) {
case (int)ECTMSG::CALIB_START: // キャリブレーション開始
this->SetImage(CALIB_FILE);
break;
case (int)ECTMSG::CALIB_OK: // キャリブレーション成功
this->SetImage(CALIB_COMPLETE_FILE);
break;
case (int)ECTMSG::CALIB_FAILED: // キャリブレーション失敗
case (int)ECTMSG::CALIB_ERR: // キャリブレーションエラー
this->SetImage(CALIB_FAILED_FILE);
break;
case (int)ECTMSG::EXP_START: // 実験開始
this->StartExp();
break;
case (int)ECTMSG::EXP_STOP: // 実験停止
_StimNo = -1;
this->SetImage(EXP_STOP_FILE);
break;
case (int)ECTMSG::EXP_END: // 実験終了
_StimNo = -1;
this->SetImage(EXP_DONE_FILE);
break;
case (int)ECTMSG::SYSTEM_ERROR: // システムエラー
_StimNo = -1;
this->SetImage(ERROR_FILE);
break;
case (int)ECTMSG::MOVIE_END: // 動画再生が末尾に到達
this->MoveNext();
break;
}
return true;
}
// 実験開始
void Stimulus::StartExp() {
int start = 0;
// 刺激ステージを検索
if (*Ect()->PWorker()->StartStagePtr() > 0) {
for (int i = 0; i < _StimInfoSet.size(); i++) {
if (_StimInfoSet[i].stage == *Ect()->PWorker()->StartStagePtr()) start = i;
}
}
this->SetStimulus(start);
}
// 次の刺激へ移動
bool Stimulus::MoveNext() {
if (_StimNo < 0) return false;
if (_StimInfoSet[_StimNo].type == StimInfo::TYPE_MOVIE) {
if (_cap.isOpened()) _cap.release();
Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_STOP);
}
if (_StimNo + 1 < (int)_StimInfoSet.size()) {
SetStimulus(_StimNo + 1);
Ect()->PWorker()->PostMsg((int)ECTMSG::EXP_NEXT);
} else {
Ect()->PWorker()->PostMsg((int)ECTMSG::EXP_END);
}
return true;
}
// 刺激提示
bool Stimulus::SetStimulus(int newStimNo) {
if (newStimNo < 0 || newStimNo >= _StimInfoSet.size()) return false;
_pMovieObject->Clear();
if (_StimInfoSet[newStimNo].csvfile[0] != '_') {
this->LoadMovieObject(newStimNo);
}
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: ") +
nkc::wut::Multi2Wide(_StimInfoSet[newStimNo].filepath)).c_str());
Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR);
return false;
}
_Movie.Put(nkc::wut::Multi2Wide(_StimInfoSet[newStimNo].filepath));
Ect()->PECTrainerGUI()->PostMsg((int)ECTMSG::MOVIE_START);
} else {
this->SetImage(_StimInfoSet[newStimNo].filepath);
}
if (newStimNo == 0 || _StimInfoSet[newStimNo].stage > 0) _Stage = _StimInfoSet[newStimNo].stage;
_StimTimer.Reset();
_StimNo = newStimNo;
return true;
}
// 画像読み込み
bool Stimulus::SetImage(cv::String imageFile) {
auto img = cv::imread(imageFile);
if (img.empty()) {
Ect()->PWorker()->EventLog((_T("Can't open image file: ") +
nkc::wut::Multi2Wide(imageFile)).c_str());
Ect()->PWorker()->PostMsg((int)ECTMSG::SYSTEM_ERROR);
return false;
}
_Display.Put(img);
return true;
}
// 動画オブジェクトの読み込み
void Stimulus::LoadMovieObject(int stimNo) {
int nFrames = _pMovieObject->SetData(_StimInfoSet[stimNo].csvfile);
nkc::wut::DebugPrintf(_T("Movie object '%s' - %d frames read.\n"),
nkc::wut::Multi2Wide(_StimInfoSet[stimNo].csvfile).c_str(), nFrames);
}
// ターゲット情報を取得
std::vector<Element> Stimulus::GetMovieObject(float eTime) {
if (eTime < 0) eTime = (float)_StimTimer.Elapse() / 1000.F;
return _pMovieObject->GetElements(eTime);
}
// FPS表示
void Stimulus::FPS(double fps) {
nkc::wut::DebugPrintf(_T("[Stimulus] %.1f fps\n"), fps);
}
// 画像にマーカーを描画
//void Stimulus::StimWithMarker() {
// cv::Mat img = cv::imread(_StimImages[_StimNo].filename);
// _pMarker->Generate(img.size());
// _pMarker->DrawMarker(img);
// img.copyTo(_DispBuffer);
//}