test_ecc.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /*M///////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. //
  5. // By downloading, copying, installing or using the software you agree to this license.
  6. // If you do not agree to this license, do not download, install,
  7. // copy or use the software.
  8. //
  9. //
  10. // License Agreement
  11. // For Open Source Computer Vision Library
  12. //
  13. // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
  14. // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
  15. // Third party copyrights are property of their respective owners.
  16. //
  17. // Redistribution and use in source and binary forms, with or without modification,
  18. // are permitted provided that the following conditions are met:
  19. //
  20. // * Redistribution's of source code must retain the above copyright notice,
  21. // this list of conditions and the following disclaimer.
  22. //
  23. // * Redistribution's in binary form must reproduce the above copyright notice,
  24. // this list of conditions and the following disclaimer in the documentation
  25. // and/or other materials provided with the distribution.
  26. //
  27. // * The name of the copyright holders may not be used to endorse or promote products
  28. // derived from this software without specific prior written permission.
  29. //
  30. // This software is provided by the copyright holders and contributors "as is" and
  31. // any express or implied warranties, including, but not limited to, the implied
  32. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  33. // In no event shall the Intel Corporation or contributors be liable for any direct,
  34. // indirect, incidental, special, exemplary, or consequential damages
  35. // (including, but not limited to, procurement of substitute goods or services;
  36. // loss of use, data, or profits; or business interruption) however caused
  37. // and on any theory of liability, whether in contract, strict liability,
  38. // or tort (including negligence or otherwise) arising in any way out of
  39. // the use of this software, even if advised of the possibility of such damage.
  40. //
  41. //M*/
  42. #include "test_precomp.hpp"
  43. namespace opencv_test { namespace {
  44. class CV_ECC_BaseTest : public cvtest::BaseTest
  45. {
  46. public:
  47. CV_ECC_BaseTest();
  48. protected:
  49. double computeRMS(const Mat& mat1, const Mat& mat2);
  50. bool isMapCorrect(const Mat& mat);
  51. double MAX_RMS_ECC;//upper bound for RMS error
  52. int ntests;//number of tests per motion type
  53. int ECC_iterations;//number of iterations for ECC
  54. double ECC_epsilon; //we choose a negative value, so that
  55. // ECC_iterations are always executed
  56. };
  57. CV_ECC_BaseTest::CV_ECC_BaseTest()
  58. {
  59. MAX_RMS_ECC=0.1;
  60. ntests = 3;
  61. ECC_iterations = 50;
  62. ECC_epsilon = -1; //-> negative value means that ECC_Iterations will be executed
  63. }
  64. bool CV_ECC_BaseTest::isMapCorrect(const Mat& map)
  65. {
  66. bool tr = true;
  67. float mapVal;
  68. for(int i =0; i<map.rows; i++)
  69. for(int j=0; j<map.cols; j++){
  70. mapVal = map.at<float>(i, j);
  71. tr = tr & (!cvIsNaN(mapVal) && (fabs(mapVal) < 1e9));
  72. }
  73. return tr;
  74. }
  75. double CV_ECC_BaseTest::computeRMS(const Mat& mat1, const Mat& mat2){
  76. CV_Assert(mat1.rows == mat2.rows);
  77. CV_Assert(mat1.cols == mat2.cols);
  78. Mat errorMat;
  79. subtract(mat1, mat2, errorMat);
  80. return sqrt(errorMat.dot(errorMat)/(mat1.rows*mat1.cols));
  81. }
  82. class CV_ECC_Test_Translation : public CV_ECC_BaseTest
  83. {
  84. public:
  85. CV_ECC_Test_Translation();
  86. protected:
  87. void run(int);
  88. bool testTranslation(int);
  89. };
  90. CV_ECC_Test_Translation::CV_ECC_Test_Translation(){}
  91. bool CV_ECC_Test_Translation::testTranslation(int from)
  92. {
  93. Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
  94. if (img.empty())
  95. {
  96. ts->printf( ts->LOG, "test image can not be read");
  97. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
  98. return false;
  99. }
  100. Mat testImg;
  101. resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
  102. cv::RNG rng = ts->get_rng();
  103. int progress=0;
  104. for (int k=from; k<ntests; k++){
  105. ts->update_context( this, k, true );
  106. progress = update_progress(progress, k, ntests, 0);
  107. Mat translationGround = (Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
  108. 0, 1, (rng.uniform(10.f, 20.f)));
  109. Mat warpedImage;
  110. warpAffine(testImg, warpedImage, translationGround,
  111. Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
  112. Mat mapTranslation = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
  113. findTransformECC(warpedImage, testImg, mapTranslation, 0,
  114. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
  115. if (!isMapCorrect(mapTranslation)){
  116. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  117. return false;
  118. }
  119. if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
  120. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  121. ts->printf( ts->LOG, "RMS = %f",
  122. computeRMS(mapTranslation, translationGround));
  123. return false;
  124. }
  125. }
  126. return true;
  127. }
  128. void CV_ECC_Test_Translation::run(int from)
  129. {
  130. if (!testTranslation(from))
  131. return;
  132. ts->set_failed_test_info(cvtest::TS::OK);
  133. }
  134. class CV_ECC_Test_Euclidean : public CV_ECC_BaseTest
  135. {
  136. public:
  137. CV_ECC_Test_Euclidean();
  138. protected:
  139. void run(int);
  140. bool testEuclidean(int);
  141. };
  142. CV_ECC_Test_Euclidean::CV_ECC_Test_Euclidean() { }
  143. bool CV_ECC_Test_Euclidean::testEuclidean(int from)
  144. {
  145. Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
  146. if (img.empty())
  147. {
  148. ts->printf( ts->LOG, "test image can not be read");
  149. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
  150. return false;
  151. }
  152. Mat testImg;
  153. resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
  154. cv::RNG rng = ts->get_rng();
  155. int progress = 0;
  156. for (int k=from; k<ntests; k++){
  157. ts->update_context( this, k, true );
  158. progress = update_progress(progress, k, ntests, 0);
  159. double angle = CV_PI/30 + CV_PI*rng.uniform((double)-2.f, (double)2.f)/180;
  160. Mat euclideanGround = (Mat_<float>(2,3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)),
  161. sin(angle), cos(angle), (rng.uniform(10.f, 20.f)));
  162. Mat warpedImage;
  163. warpAffine(testImg, warpedImage, euclideanGround,
  164. Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
  165. Mat mapEuclidean = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
  166. findTransformECC(warpedImage, testImg, mapEuclidean, 1,
  167. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
  168. if (!isMapCorrect(mapEuclidean)){
  169. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  170. return false;
  171. }
  172. if (computeRMS(mapEuclidean, euclideanGround)>MAX_RMS_ECC){
  173. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  174. ts->printf( ts->LOG, "RMS = %f",
  175. computeRMS(mapEuclidean, euclideanGround));
  176. return false;
  177. }
  178. }
  179. return true;
  180. }
  181. void CV_ECC_Test_Euclidean::run(int from)
  182. {
  183. if (!testEuclidean(from))
  184. return;
  185. ts->set_failed_test_info(cvtest::TS::OK);
  186. }
  187. class CV_ECC_Test_Affine : public CV_ECC_BaseTest
  188. {
  189. public:
  190. CV_ECC_Test_Affine();
  191. protected:
  192. void run(int);
  193. bool testAffine(int);
  194. };
  195. CV_ECC_Test_Affine::CV_ECC_Test_Affine(){}
  196. bool CV_ECC_Test_Affine::testAffine(int from)
  197. {
  198. Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
  199. if (img.empty())
  200. {
  201. ts->printf( ts->LOG, "test image can not be read");
  202. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
  203. return false;
  204. }
  205. Mat testImg;
  206. resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
  207. cv::RNG rng = ts->get_rng();
  208. int progress = 0;
  209. for (int k=from; k<ntests; k++){
  210. ts->update_context( this, k, true );
  211. progress = update_progress(progress, k, ntests, 0);
  212. Mat affineGround = (Mat_<float>(2,3) << (1-rng.uniform(-0.05f, 0.05f)),
  213. (rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
  214. (rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),
  215. (rng.uniform(10.f, 20.f)));
  216. Mat warpedImage;
  217. warpAffine(testImg, warpedImage, affineGround,
  218. Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
  219. Mat mapAffine = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
  220. findTransformECC(warpedImage, testImg, mapAffine, 2,
  221. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
  222. if (!isMapCorrect(mapAffine)){
  223. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  224. return false;
  225. }
  226. if (computeRMS(mapAffine, affineGround)>MAX_RMS_ECC){
  227. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  228. ts->printf( ts->LOG, "RMS = %f",
  229. computeRMS(mapAffine, affineGround));
  230. return false;
  231. }
  232. }
  233. return true;
  234. }
  235. void CV_ECC_Test_Affine::run(int from)
  236. {
  237. if (!testAffine(from))
  238. return;
  239. ts->set_failed_test_info(cvtest::TS::OK);
  240. }
  241. class CV_ECC_Test_Homography : public CV_ECC_BaseTest
  242. {
  243. public:
  244. CV_ECC_Test_Homography();
  245. protected:
  246. void run(int);
  247. bool testHomography(int);
  248. };
  249. CV_ECC_Test_Homography::CV_ECC_Test_Homography(){}
  250. bool CV_ECC_Test_Homography::testHomography(int from)
  251. {
  252. Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
  253. if (img.empty())
  254. {
  255. ts->printf( ts->LOG, "test image can not be read");
  256. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
  257. return false;
  258. }
  259. Mat testImg;
  260. resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
  261. cv::RNG rng = ts->get_rng();
  262. int progress = 0;
  263. for (int k=from; k<ntests; k++){
  264. ts->update_context( this, k, true );
  265. progress = update_progress(progress, k, ntests, 0);
  266. Mat homoGround = (Mat_<float>(3,3) << (1-rng.uniform(-0.05f, 0.05f)),
  267. (rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
  268. (rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)),
  269. (rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
  270. Mat warpedImage;
  271. warpPerspective(testImg, warpedImage, homoGround,
  272. Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
  273. Mat mapHomography = Mat::eye(3, 3, CV_32F);
  274. findTransformECC(warpedImage, testImg, mapHomography, 3,
  275. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
  276. if (!isMapCorrect(mapHomography)){
  277. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  278. return false;
  279. }
  280. if (computeRMS(mapHomography, homoGround)>MAX_RMS_ECC){
  281. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  282. ts->printf( ts->LOG, "RMS = %f",
  283. computeRMS(mapHomography, homoGround));
  284. return false;
  285. }
  286. }
  287. return true;
  288. }
  289. void CV_ECC_Test_Homography::run(int from)
  290. {
  291. if (!testHomography(from))
  292. return;
  293. ts->set_failed_test_info(cvtest::TS::OK);
  294. }
  295. class CV_ECC_Test_Mask : public CV_ECC_BaseTest
  296. {
  297. public:
  298. CV_ECC_Test_Mask();
  299. protected:
  300. void run(int);
  301. bool testMask(int);
  302. };
  303. CV_ECC_Test_Mask::CV_ECC_Test_Mask(){}
  304. bool CV_ECC_Test_Mask::testMask(int from)
  305. {
  306. Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
  307. if (img.empty())
  308. {
  309. ts->printf( ts->LOG, "test image can not be read");
  310. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
  311. return false;
  312. }
  313. Mat scaledImage;
  314. resize(img, scaledImage, Size(216, 216), 0, 0, INTER_LINEAR_EXACT );
  315. Mat_<float> testImg;
  316. scaledImage.convertTo(testImg, testImg.type());
  317. cv::RNG rng = ts->get_rng();
  318. int progress=0;
  319. for (int k=from; k<ntests; k++){
  320. ts->update_context( this, k, true );
  321. progress = update_progress(progress, k, ntests, 0);
  322. Mat translationGround = (Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
  323. 0, 1, (rng.uniform(10.f, 20.f)));
  324. Mat warpedImage;
  325. warpAffine(testImg, warpedImage, translationGround,
  326. Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
  327. Mat mapTranslation = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
  328. Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
  329. for (int i=testImg.rows*2/3; i<testImg.rows; i++) {
  330. for (int j=testImg.cols*2/3; j<testImg.cols; j++) {
  331. testImg(i, j) = 0;
  332. mask(i, j) = 0;
  333. }
  334. }
  335. findTransformECC(warpedImage, testImg, mapTranslation, 0,
  336. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon), mask);
  337. if (!isMapCorrect(mapTranslation)){
  338. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  339. return false;
  340. }
  341. if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
  342. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  343. ts->printf( ts->LOG, "RMS = %f",
  344. computeRMS(mapTranslation, translationGround));
  345. return false;
  346. }
  347. // Test with non-default gaussian blur.
  348. findTransformECC(warpedImage, testImg, mapTranslation, 0,
  349. TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon), mask, 1);
  350. if (!isMapCorrect(mapTranslation)){
  351. ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
  352. return false;
  353. }
  354. if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
  355. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  356. ts->printf( ts->LOG, "RMS = %f",
  357. computeRMS(mapTranslation, translationGround));
  358. return false;
  359. }
  360. }
  361. return true;
  362. }
  363. void CV_ECC_Test_Mask::run(int from)
  364. {
  365. if (!testMask(from))
  366. return;
  367. ts->set_failed_test_info(cvtest::TS::OK);
  368. }
  369. TEST(Video_ECC_Test_Compute, accuracy)
  370. {
  371. Mat testImg = (Mat_<float>(3, 3) << 1, 0, 0, 1, 0, 0, 1, 0, 0);
  372. Mat warpedImage = (Mat_<float>(3, 3) << 0, 1, 0, 0, 1, 0, 0, 1, 0);
  373. Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
  374. double ecc = computeECC(warpedImage, testImg, mask);
  375. EXPECT_NEAR(ecc, -0.5f, 1e-5f);
  376. }
  377. TEST(Video_ECC_Test_Compute, bug_14657)
  378. {
  379. /*
  380. * Simple test case - a 2 x 2 matrix with 10, 10, 10, 6. When the mean (36 / 4 = 9) is subtracted,
  381. * it results in 1, 1, 1, 0 for the unsigned int case - compare to 1, 1, 1, -3 in the signed case.
  382. * For this reason, when the same matrix was provided as the input and the template, we didn't get 1 as expected.
  383. */
  384. Mat img = (Mat_<uint8_t>(2, 2) << 10, 10, 10, 6);
  385. EXPECT_NEAR(computeECC(img, img), 1.0f, 1e-5f);
  386. }
  387. TEST(Video_ECC_Translation, accuracy) { CV_ECC_Test_Translation test; test.safe_run();}
  388. TEST(Video_ECC_Euclidean, accuracy) { CV_ECC_Test_Euclidean test; test.safe_run(); }
  389. TEST(Video_ECC_Affine, accuracy) { CV_ECC_Test_Affine test; test.safe_run(); }
  390. TEST(Video_ECC_Homography, accuracy) { CV_ECC_Test_Homography test; test.safe_run(); }
  391. TEST(Video_ECC_Mask, accuracy) { CV_ECC_Test_Mask test; test.safe_run(); }
  392. }} // namespace