diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80dd262 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +log/ diff --git a/ECTrainer1/ECTrainer1.rc b/ECTrainer1/ECTrainer1.rc index 182b8d8..5287b42 100644 --- a/ECTrainer1/ECTrainer1.rc +++ b/ECTrainer1/ECTrainer1.rc Binary files differ diff --git a/ECTrainer1/ECTrainer1Dlg.cpp b/ECTrainer1/ECTrainer1Dlg.cpp index bc667a7..25392b1 100644 --- a/ECTrainer1/ECTrainer1Dlg.cpp +++ b/ECTrainer1/ECTrainer1Dlg.cpp @@ -7,11 +7,17 @@ #include "ECTrainer1.h" #include "ECTrainer1Dlg.h" #include "afxdialogex.h" +#include +#include #ifdef _DEBUG #define new DEBUG_NEW #endif +const char* FILE_LIST[] = { "man2", "manL1", "manL2", "manL1", "manL3", "manL1" , "manL4", "manL1", + "manS1", "manS2", "manS1", "manS3", "manS1" , "manS4", "manS1" }; +const int NUM_FILES = sizeof(FILE_LIST) / sizeof(char*); + // アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ @@ -55,7 +61,7 @@ : CDialogEx(IDD_ECTRAINER1_DIALOG, pParent) , _bRunning(false) , _pCameraThread(NULL) -{ + , _message(_T("")) { _hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } @@ -63,6 +69,7 @@ void CECTrainer1Dlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); + DDX_Text(pDX, IDC_EDT_MSG, _message); } // メッセージマップ @@ -71,6 +78,7 @@ ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_CLOSE() + ON_BN_CLICKED(IDC_BTN_START, &CECTrainer1Dlg::OnBnClickedBtnStart) END_MESSAGE_MAP() @@ -111,6 +119,9 @@ StartImageThreads(); StartDataThreads(); + _message = _T("待機中"); + UpdateData(FALSE); + return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。 } @@ -149,6 +160,16 @@ else { CDialogEx::OnPaint(); + + if (_mode > 0) { + GetDlgItem(IDC_BTN_START)->SetWindowTextW(_T("中断")); + _message = _T("試験中"); + UpdateData(FALSE); + } else { + GetDlgItem(IDC_BTN_START)->SetWindowTextW(_T("開始")); + _message = _T("待機中"); + UpdateData(FALSE); + } } } @@ -217,7 +238,7 @@ _pImageThread->m_bAutoDelete = TRUE; // スレッド終了時にオブジェクトを自動的に破棄 _pImageThread->ResumeThread(); // スレッドを開始 - // 画像処理スレッド生成 + // 刺激スレッド生成 _pTargetThread = AfxBeginThread(CallTargetThreadProc, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL); if (!_pTargetThread) return false; @@ -253,40 +274,40 @@ // カメラスレッド本体 void CECTrainer1Dlg::CameraThreadProc() { - TRACE(_T("CameraThreadProc start\n")); + OutputDebugString(_T("CameraThreadProc start\n")); VideoCapture cam("rtsp://" ADDR ":8554/live/scene"); if (!cam.isOpened()) { MessageBox(_T("cannot open camera " ADDR)); return; } + Mat frame; + cam >> frame; + _frameSize = frame.size(); _write = 0, _read = 0; do { cam >> _frames[_write]; _read = _write; - _write = (++_write) % FRAMES; + _write = (++_write) % FRAMES; } while (_bRunning); - TRACE(_T("CameraThreadProc end\n")); + OutputDebugString(_T("CameraThreadProc end\n")); } // 画像処理スレッド本体 void CECTrainer1Dlg::ImageThreadProc() { - TRACE(_T("ImageThreadProc start\n")); + OutputDebugString(_T("ImageThreadProc start\n")); - //const cv::aruco::PREDEFINED_DICTIONARY_NAME dictionary_name = cv::aruco::DICT_4X4_50; cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); Point2f cornerPosW[] = { {5, 155}, {215, 155}, {5, 5}, {215, 5}, {5, 60}, {5, 105}, {215, 60}, {215, 105}, {54, 5}, {54, 60} }; int lastRead = 0; do { - while (_read == lastRead && _bRunning); + while (_read == lastRead && _bRunning) Sleep(1); Mat img = _frames[_read].clone(); - Point2f gpC = Point2f(_gpC.x * img.cols, _gpC.y * img.rows); - // マーカーの検出 std::vector mids; std::vector> corners; @@ -306,58 +327,84 @@ //putText(img, msg, corners[i][0], FONT_HERSHEY_COMPLEX, 1.0, CV_RGB(255, 0, 0)); } if (cornerC.size() >= 4) { - Mat h = findHomography(cornerC, cornerW); - if (!h.empty()) { + _homography = findHomography(cornerC, cornerW); + if (!_homography.empty()) { putText(img, "H", Point(5, 20), FONT_HERSHEY_COMPLEX, 1.0, CV_RGB(255, 0, 0), 1); - - if (_gpC.x > 0 && _gpC.y > 0) { - Mat gpCe = (cv::Mat_(3, 1) << gpC.x, gpC.y, 1); - Mat gpWe = h * gpCe; - double* pgpWe = gpWe.ptr(0); - _gpW = Point2f((float)(*pgpWe / *(pgpWe + 2)), (float)(*(pgpWe + 1) / *(pgpWe + 2))); - - //TRACE("gpC %d,%d gpWe %.1f,%.1f,%.1f gpW %.1f,%.1f \n", - // (int)gpC.x, (int)gpC.y, *pgpWe, *(pgpWe + 1), *(pgpWe + 2), - // *pgpWe / *(pgpWe + 2), *(pgpWe + 1) / *(pgpWe + 2)); - } } } if (_gpC.x > 0 && _gpC.y > 0) { - circle(img, gpC, 10, CV_RGB(255, 0, 0), 2); + circle(img, _gpC, 10, CV_RGB(255, 0, 0), 2); } ShowImage(IDC_IMAGE, img); } while (_bRunning); - TRACE(_T("ImageThreadProc end\n")); + OutputDebugString(_T("ImageThreadProc end\n")); } // 刺激スレッド本体 void CECTrainer1Dlg::TargetThreadProc() { - TRACE(_T("TargetThreadProc start\n")); + OutputDebugString(_T("TargetThreadProc start\n")); Size targetW = Size(240, 180); - //Mat target = imread("manL1.png"); - namedWindow("target", WINDOW_NORMAL); + Mat target = imread(GetTargetFilename()); + Mat logimg = target.clone(); + int logimgCount = 0; + int curMode = _mode; + namedWindow("target", WINDOW_AUTOSIZE); - Mat target; - cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); - aruco::drawMarker(dictionary, 1, 100, target, 1); + //Mat target; + //cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); + //aruco::drawMarker(dictionary, 1, 100, target, 1); + ULONGLONG start = GetTickCount64(); do { Mat tdisp = target.clone(); + Point2f gpW = Point2f(_gpW.x * target.size().width / targetW.width, + _gpW.y * target.size().height / targetW.height); if (_gpW.x > 0 && _gpW.x < targetW.width && _gpW.y > 0 && _gpW.y < targetW.height) { - Point2f gpW = Point2f(_gpW.x * target.size().width / targetW.width, _gpW.y * target.size().height / targetW.height); - circle(tdisp, gpW, 10, CV_RGB(0, 0, 255), 2); + if (GetTickCount64() > start + NOLOG) circle(logimg, gpW, 6, CV_RGB(0, 0, 255), 2); + if (_mode < 1) circle(tdisp, gpW, 10, CV_RGB(0, 0, 255), 2); } imshow("target", tdisp); waitKey(1); + + if (_mode != curMode) { + if (_mode == 1) logimgCount = 1; + else { + char writefn[256]; + sprintf_s(writefn, 256, "_img%02d", logimgCount++); + imwrite(_logfile + writefn + ".png", logimg); + } + + target = imread(GetTargetFilename()); + logimg = target.clone(); + curMode = _mode; + start = GetTickCount64(); + } + if (_mode > 0) { + if (GetTickCount64() > start + DURATION) { + if (++_mode >= NUM_FILES) { + _mode = 0; + Invalidate(); + } + } + } } while (_bRunning); - TRACE(_T("TargetThreadProc end\n")); + OutputDebugString(_T("TargetThreadProc end\n")); +} + +// 刺激画像ファイル名取得 +String CECTrainer1Dlg::GetTargetFilename() { + int i = _mode < NUM_FILES ? _mode : 0; + String fname = "../images/"; + fname.append(FILE_LIST[i]); + fname.append(".png"); + return fname; } // データスレッド開始 @@ -420,7 +467,7 @@ // Keep Aliveスレッド本体 void CECTrainer1Dlg::KeepAliveThreadProc() { - TRACE(_T("KeepAliveThreadProc start\n")); + OutputDebugString(_T("KeepAliveThreadProc start\n")); const int KEEP_ALIVE_WAIT_COUNT = 20; // x 100ms int slen = sizeof(_socketAddr); @@ -439,18 +486,20 @@ closesocket(_socket); WSACleanup(); - TRACE(_T("KeepAliveThreadProc end\n")); + OutputDebugString(_T("KeepAliveThreadProc end\n")); } // データスレッド本体 void CECTrainer1Dlg::DataThreadProc() { - TRACE(_T("DataThreadProc start\n")); + OutputDebugString(_T("DataThreadProc start\n")); int slen = sizeof(_socketAddr); char buf[BUFLEN]; int lastGidx = 0; Point2f gp; + std::ofstream ofs; + int64 start = 0; Sleep(500); // 最初のKeepAlive送信待ち @@ -467,7 +516,38 @@ GazeData gd(buf); if (gd.gidx > lastGidx) { - _gpC = gp; + if (gp.x > 0 && gp.y > 0 && !_homography.empty()) { + // 座標変換 + _gpC = Point2f(gp.x * _frameSize.width, gp.y * _frameSize.height); + Mat gpCe = (cv::Mat_(3, 1) << _gpC.x, _gpC.y, 1); + Mat gpWe = _homography * gpCe; + double* pgpWe = gpWe.ptr(0); + _gpW = Point2f((float)(*pgpWe / *(pgpWe + 2)), (float)(*(pgpWe + 1) / *(pgpWe + 2))); + + //TRACE("gpC %d,%d gpWe %.1f,%.1f,%.1f gpW %.1f,%.1f \n", + // (int)gpC.x, (int)gpC.y, *pgpWe, *(pgpWe + 1), *(pgpWe + 2), + // *pgpWe / *(pgpWe + 2), *(pgpWe + 1) / *(pgpWe + 2)); + + // ログ出力 + if (_mode > 0) { + if (!ofs) { + SYSTEMTIME st; + GetLocalTime(&st); + char filename[256]; + sprintf_s(filename, 256, "../log/log%04d%02d%02d_%02d%02d%02d", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + _logfile = filename; + ofs.open(_logfile + ".csv"); + ofs << "time(ms),img,x,y" << std::endl; + start = getTickCount(); + } + ofs << (getTickCount() - start) * 1000. / getTickFrequency() << "," << _mode << "," + << _gpW.x << "," << _gpW.y << std::endl; + } else { + if (ofs) ofs.close(); + } + } + gp = Point(0, 0); lastGidx = gd.gidx; } @@ -476,10 +556,19 @@ //_gp = gp; //TRACE("%d, %f, %f\n", gd.gidx, gd.gpx, gd.gpy); } + } while (_bRunning); closesocket(_socket); WSACleanup(); - TRACE(_T("DataThreadProc end\n")); + OutputDebugString(_T("DataThreadProc end\n")); +} + +// 開始ボタン +void CECTrainer1Dlg::OnBnClickedBtnStart() +{ + if (_mode == 0) _mode = 1; + else _mode = 0; + Invalidate(); } diff --git a/ECTrainer1/ECTrainer1Dlg.h b/ECTrainer1/ECTrainer1Dlg.h index 04b0389..27f9d46 100644 --- a/ECTrainer1/ECTrainer1Dlg.h +++ b/ECTrainer1/ECTrainer1Dlg.h @@ -31,6 +31,8 @@ static const int PORT = 49152; //The port on which to listen for incoming data static const int BUFLEN = 512; //Max length of buffer static const int FRAMES = 2; + static const int DURATION = 3000; // Time for one image (ms) + static const int NOLOG = 1000; // Time for no logging (ms) HICON _hIcon; bool _bRunning; @@ -44,7 +46,11 @@ Point2f _gpC; Point2f _gpW; Mat _frames[FRAMES]; + Mat _homography; + Size _frameSize; int _write, _read; + int _mode = 0; + std::string _logfile; // 生成された、メッセージ割り当て関数 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート @@ -56,6 +62,7 @@ void ShowImage(int id, Mat &img); bool StartImageThreads(); bool StartDataThreads(); + String GetTargetFilename(); public: static UINT CallCameraThreadProc(LPVOID pParam); static UINT CallImageThreadProc(LPVOID pParam); @@ -68,6 +75,8 @@ void KeepAliveThreadProc(); void DataThreadProc(); afx_msg void OnClose(); + afx_msg void OnBnClickedBtnStart(); + CString _message; }; // diff --git a/ECTrainer1/manL1.png b/ECTrainer1/manL1.png deleted file mode 100644 index 8167af3..0000000 --- a/ECTrainer1/manL1.png +++ /dev/null Binary files differ diff --git a/ECTrainer1/resource.h b/ECTrainer1/resource.h index ae06403..15d307a 100644 --- a/ECTrainer1/resource.h +++ b/ECTrainer1/resource.h @@ -9,6 +9,9 @@ #define IDR_MAINFRAME 128 #define IDC_IMAGE 1002 #define IDC_IMAGE2 1003 +#define IDC_BTN_START 1003 +#define IDC_EDIT1 1004 +#define IDC_EDT_MSG 1004 // Next default values for new objects // @@ -16,7 +19,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 131 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_CONTROL_VALUE 1005 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/images/man1.jpg b/images/man1.jpg new file mode 100644 index 0000000..0d8f84b --- /dev/null +++ b/images/man1.jpg Binary files differ diff --git a/images/man2.png b/images/man2.png new file mode 100644 index 0000000..05c541b --- /dev/null +++ b/images/man2.png Binary files differ diff --git a/images/manL1.png b/images/manL1.png new file mode 100644 index 0000000..8167af3 --- /dev/null +++ b/images/manL1.png Binary files differ diff --git a/images/manL2.png b/images/manL2.png new file mode 100644 index 0000000..d37157f --- /dev/null +++ b/images/manL2.png Binary files differ diff --git a/images/manL3.png b/images/manL3.png new file mode 100644 index 0000000..1b84370 --- /dev/null +++ b/images/manL3.png Binary files differ diff --git a/images/manL4.png b/images/manL4.png new file mode 100644 index 0000000..68a6be2 --- /dev/null +++ b/images/manL4.png Binary files differ diff --git a/images/manS1.png b/images/manS1.png new file mode 100644 index 0000000..3d5f2a0 --- /dev/null +++ b/images/manS1.png Binary files differ diff --git a/images/manS2.png b/images/manS2.png new file mode 100644 index 0000000..4ad81e1 --- /dev/null +++ b/images/manS2.png Binary files differ diff --git a/images/manS3.png b/images/manS3.png new file mode 100644 index 0000000..afb705c --- /dev/null +++ b/images/manS3.png Binary files differ diff --git a/images/manS4.png b/images/manS4.png new file mode 100644 index 0000000..7e17283 --- /dev/null +++ b/images/manS4.png Binary files differ