test_trackers.impl.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. /*
  5. * The Evaluation Methodologies are partially based on:
  6. * ====================================================================================================================
  7. * [OTB] Y. Wu, J. Lim, and M.-H. Yang, "Online object tracking: A benchmark," in Computer Vision and Pattern Recognition (CVPR), 2013
  8. *
  9. */
  10. enum BBTransformations
  11. {
  12. NoTransform = 0,
  13. CenterShiftLeft = 1,
  14. CenterShiftRight = 2,
  15. CenterShiftUp = 3,
  16. CenterShiftDown = 4,
  17. CornerShiftTopLeft = 5,
  18. CornerShiftTopRight = 6,
  19. CornerShiftBottomLeft = 7,
  20. CornerShiftBottomRight = 8,
  21. Scale_0_8 = 9,
  22. Scale_0_9 = 10,
  23. Scale_1_1 = 11,
  24. Scale_1_2 = 12
  25. };
  26. namespace {
  27. std::vector<std::string> splitString(const std::string& s_, const std::string& delimiter)
  28. {
  29. std::string s = s_;
  30. std::vector<string> token;
  31. size_t pos = 0;
  32. while ((pos = s.find(delimiter)) != std::string::npos)
  33. {
  34. token.push_back(s.substr(0, pos));
  35. s.erase(0, pos + delimiter.length());
  36. }
  37. token.push_back(s);
  38. return token;
  39. }
  40. float calcDistance(const Rect& a, const Rect& b)
  41. {
  42. Point2f p_a((float)(a.x + a.width / 2), (float)(a.y + a.height / 2));
  43. Point2f p_b((float)(b.x + b.width / 2), (float)(b.y + b.height / 2));
  44. Point2f diff = p_a - p_b;
  45. return sqrt(diff.dot(diff));
  46. }
  47. float calcOverlap(const Rect& a, const Rect& b)
  48. {
  49. float rectIntersectionArea = (float)(a & b).area();
  50. return rectIntersectionArea / (a.area() + b.area() - rectIntersectionArea);
  51. }
  52. } // namespace
  53. template <typename Tracker, typename ROI_t = Rect2d>
  54. class TrackerTest
  55. {
  56. public:
  57. TrackerTest(const Ptr<Tracker>& tracker, const string& video, float distanceThreshold,
  58. float overlapThreshold, int shift = NoTransform, int segmentIdx = 1, int numSegments = 10);
  59. ~TrackerTest() {}
  60. void run();
  61. protected:
  62. void checkDataTest();
  63. void distanceAndOverlapTest();
  64. Ptr<Tracker> tracker;
  65. string video;
  66. std::vector<Rect> bbs;
  67. int startFrame;
  68. string suffix;
  69. string prefix;
  70. float overlapThreshold;
  71. float distanceThreshold;
  72. int segmentIdx;
  73. int shift;
  74. int numSegments;
  75. int gtStartFrame;
  76. int endFrame;
  77. vector<int> validSequence;
  78. private:
  79. Rect applyShift(const Rect& bb);
  80. };
  81. template <typename Tracker, typename ROI_t>
  82. TrackerTest<Tracker, ROI_t>::TrackerTest(const Ptr<Tracker>& _tracker, const string& _video, float _distanceThreshold,
  83. float _overlapThreshold, int _shift, int _segmentIdx, int _numSegments)
  84. : tracker(_tracker)
  85. , video(_video)
  86. , overlapThreshold(_overlapThreshold)
  87. , distanceThreshold(_distanceThreshold)
  88. , segmentIdx(_segmentIdx)
  89. , shift(_shift)
  90. , numSegments(_numSegments)
  91. {
  92. // nothing
  93. }
  94. template <typename Tracker, typename ROI_t>
  95. Rect TrackerTest<Tracker, ROI_t>::applyShift(const Rect& bb_)
  96. {
  97. Rect bb = bb_;
  98. Point center(bb.x + (bb.width / 2), bb.y + (bb.height / 2));
  99. int xLimit = bb.x + bb.width - 1;
  100. int yLimit = bb.y + bb.height - 1;
  101. int h = 0;
  102. int w = 0;
  103. float ratio = 1.0;
  104. switch (shift)
  105. {
  106. case CenterShiftLeft:
  107. bb.x = bb.x - (int)ceil(0.1 * bb.width);
  108. break;
  109. case CenterShiftRight:
  110. bb.x = bb.x + (int)ceil(0.1 * bb.width);
  111. break;
  112. case CenterShiftUp:
  113. bb.y = bb.y - (int)ceil(0.1 * bb.height);
  114. break;
  115. case CenterShiftDown:
  116. bb.y = bb.y + (int)ceil(0.1 * bb.height);
  117. break;
  118. case CornerShiftTopLeft:
  119. bb.x = (int)cvRound(bb.x - 0.1 * bb.width);
  120. bb.y = (int)cvRound(bb.y - 0.1 * bb.height);
  121. bb.width = xLimit - bb.x + 1;
  122. bb.height = yLimit - bb.y + 1;
  123. break;
  124. case CornerShiftTopRight:
  125. xLimit = (int)cvRound(xLimit + 0.1 * bb.width);
  126. bb.y = (int)cvRound(bb.y - 0.1 * bb.height);
  127. bb.width = xLimit - bb.x + 1;
  128. bb.height = yLimit - bb.y + 1;
  129. break;
  130. case CornerShiftBottomLeft:
  131. bb.x = (int)cvRound(bb.x - 0.1 * bb.width);
  132. yLimit = (int)cvRound(yLimit + 0.1 * bb.height);
  133. bb.width = xLimit - bb.x + 1;
  134. bb.height = yLimit - bb.y + 1;
  135. break;
  136. case CornerShiftBottomRight:
  137. xLimit = (int)cvRound(xLimit + 0.1 * bb.width);
  138. yLimit = (int)cvRound(yLimit + 0.1 * bb.height);
  139. bb.width = xLimit - bb.x + 1;
  140. bb.height = yLimit - bb.y + 1;
  141. break;
  142. case Scale_0_8:
  143. ratio = 0.8f;
  144. w = (int)(ratio * bb.width);
  145. h = (int)(ratio * bb.height);
  146. bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
  147. break;
  148. case Scale_0_9:
  149. ratio = 0.9f;
  150. w = (int)(ratio * bb.width);
  151. h = (int)(ratio * bb.height);
  152. bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
  153. break;
  154. case 11:
  155. //scale 1.1
  156. ratio = 1.1f;
  157. w = (int)(ratio * bb.width);
  158. h = (int)(ratio * bb.height);
  159. bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
  160. break;
  161. case 12:
  162. //scale 1.2
  163. ratio = 1.2f;
  164. w = (int)(ratio * bb.width);
  165. h = (int)(ratio * bb.height);
  166. bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
  167. break;
  168. default:
  169. break;
  170. }
  171. return bb;
  172. }
  173. template <typename Tracker, typename ROI_t>
  174. void TrackerTest<Tracker, ROI_t>::distanceAndOverlapTest()
  175. {
  176. bool initialized = false;
  177. int fc = (startFrame - gtStartFrame);
  178. bbs.at(fc) = applyShift(bbs.at(fc));
  179. Rect currentBBi = bbs.at(fc);
  180. ROI_t currentBB(currentBBi);
  181. float sumDistance = 0;
  182. float sumOverlap = 0;
  183. string folder = cvtest::TS::ptr()->get_data_path() + "/" + TRACKING_DIR + "/" + video + "/" + FOLDER_IMG;
  184. string videoPath = folder + "/" + video + ".webm";
  185. VideoCapture c;
  186. c.open(videoPath);
  187. if (!c.isOpened())
  188. throw SkipTestException("Can't open video file");
  189. #if 0
  190. c.set(CAP_PROP_POS_FRAMES, startFrame);
  191. #else
  192. if (startFrame)
  193. std::cout << "startFrame = " << startFrame << std::endl;
  194. for (int i = 0; i < startFrame; i++)
  195. {
  196. Mat dummy_frame;
  197. c >> dummy_frame;
  198. ASSERT_FALSE(dummy_frame.empty()) << i << ": " << videoPath;
  199. }
  200. #endif
  201. for (int frameCounter = startFrame; frameCounter < endFrame; frameCounter++)
  202. {
  203. Mat frame;
  204. c >> frame;
  205. ASSERT_FALSE(frame.empty()) << "frameCounter=" << frameCounter << " video=" << videoPath;
  206. if (!initialized)
  207. {
  208. tracker->init(frame, currentBB);
  209. std::cout << "frame size = " << frame.size() << std::endl;
  210. initialized = true;
  211. }
  212. else if (initialized)
  213. {
  214. if (frameCounter >= (int)bbs.size())
  215. break;
  216. tracker->update(frame, currentBB);
  217. }
  218. float curDistance = calcDistance(currentBB, bbs.at(fc));
  219. float curOverlap = calcOverlap(currentBB, bbs.at(fc));
  220. #ifdef DEBUG_TEST
  221. Mat result;
  222. repeat(frame, 1, 2, result);
  223. rectangle(result, currentBB, Scalar(0, 255, 0), 1);
  224. Rect roi2(frame.cols, 0, frame.cols, frame.rows);
  225. rectangle(result(roi2), bbs.at(fc), Scalar(0, 0, 255), 1);
  226. imshow("result", result);
  227. waitKey(1);
  228. #endif
  229. sumDistance += curDistance;
  230. sumOverlap += curOverlap;
  231. fc++;
  232. }
  233. float meanDistance = sumDistance / (endFrame - startFrame);
  234. float meanOverlap = sumOverlap / (endFrame - startFrame);
  235. EXPECT_LE(meanDistance, distanceThreshold);
  236. EXPECT_GE(meanOverlap, overlapThreshold);
  237. }
  238. template <typename Tracker, typename ROI_t>
  239. void TrackerTest<Tracker, ROI_t>::checkDataTest()
  240. {
  241. FileStorage fs;
  242. fs.open(cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/" + video + ".yml", FileStorage::READ);
  243. fs["start"] >> startFrame;
  244. fs["prefix"] >> prefix;
  245. fs["suffix"] >> suffix;
  246. fs.release();
  247. string gtFile = cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/gt.txt";
  248. std::ifstream gt;
  249. //open the ground truth
  250. gt.open(gtFile.c_str());
  251. ASSERT_TRUE(gt.is_open()) << gtFile;
  252. string line;
  253. int bbCounter = 0;
  254. while (getline(gt, line))
  255. {
  256. bbCounter++;
  257. }
  258. gt.close();
  259. int seqLength = bbCounter;
  260. for (int i = startFrame; i < seqLength; i++)
  261. {
  262. validSequence.push_back(i);
  263. }
  264. //exclude from the images sequence, the frames where the target is occluded or out of view
  265. string omitFile = cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/" + FOLDER_OMIT_INIT + "/" + video + ".txt";
  266. std::ifstream omit;
  267. omit.open(omitFile.c_str());
  268. if (omit.is_open())
  269. {
  270. string omitLine;
  271. while (getline(omit, omitLine))
  272. {
  273. vector<string> tokens = splitString(omitLine, " ");
  274. int s_start = atoi(tokens.at(0).c_str());
  275. int s_end = atoi(tokens.at(1).c_str());
  276. for (int k = s_start; k <= s_end; k++)
  277. {
  278. std::vector<int>::iterator position = std::find(validSequence.begin(), validSequence.end(), k);
  279. if (position != validSequence.end())
  280. validSequence.erase(position);
  281. }
  282. }
  283. }
  284. omit.close();
  285. gtStartFrame = startFrame;
  286. //compute the start and the and for each segment
  287. int numFrame = (int)(validSequence.size() / numSegments);
  288. startFrame += (segmentIdx - 1) * numFrame;
  289. endFrame = startFrame + numFrame;
  290. std::ifstream gt2;
  291. //open the ground truth
  292. gt2.open(gtFile.c_str());
  293. ASSERT_TRUE(gt2.is_open()) << gtFile;
  294. string line2;
  295. int bbCounter2 = 0;
  296. while (getline(gt2, line2))
  297. {
  298. vector<string> tokens = splitString(line2, ",");
  299. Rect bb(atoi(tokens.at(0).c_str()), atoi(tokens.at(1).c_str()), atoi(tokens.at(2).c_str()), atoi(tokens.at(3).c_str()));
  300. ASSERT_EQ((size_t)4, tokens.size()) << "Incorrect ground truth file " << gtFile;
  301. bbs.push_back(bb);
  302. bbCounter2++;
  303. }
  304. gt2.close();
  305. if (segmentIdx == numSegments)
  306. endFrame = (int)bbs.size();
  307. }
  308. template <typename Tracker, typename ROI_t>
  309. void TrackerTest<Tracker, ROI_t>::run()
  310. {
  311. srand(1); // FIXIT remove that, ensure that there is no "rand()" in implementation
  312. ASSERT_TRUE(tracker);
  313. checkDataTest();
  314. //check for failure
  315. if (::testing::Test::HasFatalFailure())
  316. return;
  317. distanceAndOverlapTest();
  318. }