test_hdr.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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) 2013, OpenCV Foundation, all rights reserved.
  14. // Third party copyrights are property of their respective owners.
  15. //
  16. // Redistribution and use in source and binary forms, with or without modification,
  17. // are permitted provided that the following conditions are met:
  18. //
  19. // * Redistribution's of source code must retain the above copyright notice,
  20. // this list of conditions and the following disclaimer.
  21. //
  22. // * Redistribution's in binary form must reproduce the above copyright notice,
  23. // this list of conditions and the following disclaimer in the documentation
  24. // and/or other materials provided with the distribution.
  25. //
  26. // * The name of the copyright holders may not be used to endorse or promote products
  27. // derived from this software without specific prior written permission.
  28. //
  29. // This software is provided by the copyright holders and contributors "as is" and
  30. // any express or implied warranties, including, but not limited to, the implied
  31. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  32. // In no event shall the Intel Corporation or contributors be liable for any direct,
  33. // indirect, incidental, special, exemplary, or consequential damages
  34. // (including, but not limited to, procurement of substitute goods or services;
  35. // loss of use, data, or profits; or business interruption) however caused
  36. // and on any theory of liability, whether in contract, strict liability,
  37. // or tort (including negligence or otherwise) arising in any way out of
  38. // the use of this software, even if advised of the possibility of such damage.
  39. //
  40. //M*/
  41. #include "test_precomp.hpp"
  42. namespace opencv_test { namespace {
  43. void loadImage(string path, Mat &img)
  44. {
  45. img = imread(path, -1);
  46. ASSERT_FALSE(img.empty()) << "Could not load input image " << path;
  47. }
  48. void checkEqual(Mat img0, Mat img1, double threshold, const string& name)
  49. {
  50. double max = 1.0;
  51. minMaxLoc(abs(img0 - img1), NULL, &max);
  52. ASSERT_FALSE(max > threshold) << "max=" << max << " threshold=" << threshold << " method=" << name;
  53. }
  54. static vector<float> DEFAULT_VECTOR;
  55. void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DEFAULT_VECTOR)
  56. {
  57. std::ifstream list_file((path + "list.txt").c_str());
  58. ASSERT_TRUE(list_file.is_open());
  59. string name;
  60. float val;
  61. while(list_file >> name >> val) {
  62. Mat img = imread(path + name);
  63. ASSERT_FALSE(img.empty()) << "Could not load input image " << path + name;
  64. images.push_back(img);
  65. times.push_back(1 / val);
  66. }
  67. list_file.close();
  68. }
  69. void loadResponseCSV(String path, Mat& response)
  70. {
  71. response = Mat(256, 1, CV_32FC3);
  72. std::ifstream resp_file(path.c_str());
  73. for(int i = 0; i < 256; i++) {
  74. for(int c = 0; c < 3; c++) {
  75. resp_file >> response.at<Vec3f>(i)[c];
  76. resp_file.ignore(1);
  77. }
  78. }
  79. resp_file.close();
  80. }
  81. TEST(Photo_Tonemap, regression)
  82. {
  83. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
  84. Mat img, expected, result;
  85. loadImage(test_path + "image.hdr", img);
  86. float gamma = 2.2f;
  87. Ptr<Tonemap> linear = createTonemap(gamma);
  88. linear->process(img, result);
  89. loadImage(test_path + "linear.png", expected);
  90. result.convertTo(result, CV_8UC3, 255);
  91. checkEqual(result, expected, 3, "Simple");
  92. Ptr<TonemapDrago> drago = createTonemapDrago(gamma);
  93. drago->process(img, result);
  94. loadImage(test_path + "drago.png", expected);
  95. result.convertTo(result, CV_8UC3, 255);
  96. checkEqual(result, expected, 3, "Drago");
  97. Ptr<TonemapReinhard> reinhard = createTonemapReinhard(gamma);
  98. reinhard->process(img, result);
  99. loadImage(test_path + "reinhard.png", expected);
  100. result.convertTo(result, CV_8UC3, 255);
  101. checkEqual(result, expected, 3, "Reinhard");
  102. Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma);
  103. mantiuk->process(img, result);
  104. loadImage(test_path + "mantiuk.png", expected);
  105. result.convertTo(result, CV_8UC3, 255);
  106. checkEqual(result, expected, 3, "Mantiuk");
  107. }
  108. TEST(Photo_AlignMTB, regression)
  109. {
  110. const int TESTS_COUNT = 100;
  111. string folder = string(cvtest::TS::ptr()->get_data_path()) + "shared/";
  112. string file_name = folder + "lena.png";
  113. Mat img;
  114. loadImage(file_name, img);
  115. cvtColor(img, img, COLOR_RGB2GRAY);
  116. int max_bits = 5;
  117. int max_shift = 32;
  118. srand(static_cast<unsigned>(time(0)));
  119. int errors = 0;
  120. Ptr<AlignMTB> align = createAlignMTB(max_bits);
  121. RNG rng = theRNG();
  122. for(int i = 0; i < TESTS_COUNT; i++) {
  123. Point shift(rng.uniform(0, max_shift), rng.uniform(0, max_shift));
  124. Mat res;
  125. align->shiftMat(img, res, shift);
  126. Point calc = align->calculateShift(img, res);
  127. errors += (calc != -shift);
  128. }
  129. ASSERT_TRUE(errors < 5) << errors << " errors";
  130. }
  131. TEST(Photo_MergeMertens, regression)
  132. {
  133. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  134. vector<Mat> images;
  135. loadExposureSeq((test_path + "exposures/").c_str() , images);
  136. Ptr<MergeMertens> merge = createMergeMertens();
  137. Mat result, expected;
  138. loadImage(test_path + "merge/mertens.png", expected);
  139. merge->process(images, result);
  140. result.convertTo(result, CV_8UC3, 255);
  141. checkEqual(expected, result, 3, "Mertens");
  142. Mat uniform(100, 100, CV_8UC3);
  143. uniform = Scalar(0, 255, 0);
  144. images.clear();
  145. images.push_back(uniform);
  146. merge->process(images, result);
  147. result.convertTo(result, CV_8UC3, 255);
  148. checkEqual(uniform, result, 1e-2f, "Mertens");
  149. }
  150. TEST(Photo_MergeDebevec, regression)
  151. {
  152. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  153. vector<Mat> images;
  154. vector<float> times;
  155. Mat response;
  156. loadExposureSeq(test_path + "exposures/", images, times);
  157. loadResponseCSV(test_path + "exposures/response.csv", response);
  158. Ptr<MergeDebevec> merge = createMergeDebevec();
  159. Mat result, expected;
  160. loadImage(test_path + "merge/debevec.hdr", expected);
  161. merge->process(images, result, times, response);
  162. Ptr<Tonemap> map = createTonemap();
  163. map->process(result, result);
  164. map->process(expected, expected);
  165. checkEqual(expected, result, 1e-2f, "Debevec");
  166. }
  167. TEST(Photo_MergeRobertson, regression)
  168. {
  169. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  170. vector<Mat> images;
  171. vector<float> times;
  172. loadExposureSeq(test_path + "exposures/", images, times);
  173. Ptr<MergeRobertson> merge = createMergeRobertson();
  174. Mat result, expected;
  175. loadImage(test_path + "merge/robertson.hdr", expected);
  176. merge->process(images, result, times);
  177. const float eps = 6.f;
  178. checkEqual(expected, result, eps, "MergeRobertson");
  179. }
  180. TEST(Photo_MergeDebevec, regression_depth_consistency)
  181. {
  182. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  183. vector<Mat> images8;
  184. vector<float> times;
  185. loadExposureSeq(test_path + "exposures/", images8, times);
  186. vector<Mat> images16(images8.size()), images32(images8.size());
  187. for (size_t i = 0; i < images8.size(); ++i)
  188. {
  189. images8[i].convertTo(images16[i], CV_16UC3, 257.0);
  190. images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0);
  191. }
  192. Ptr<MergeDebevec> merge = createMergeDebevec();
  193. Ptr<Tonemap> map = createTonemap();
  194. Mat hdr8, hdr16, hdr32;
  195. merge->process(images8, hdr8, times);
  196. merge->process(images16, hdr16, times);
  197. merge->process(images32, hdr32, times);
  198. map->process(hdr8, hdr8);
  199. map->process(hdr16, hdr16);
  200. map->process(hdr32, hdr32);
  201. checkEqual(hdr8, hdr16, 2e-2f, "Debevec realdata 16U vs 8U");
  202. checkEqual(hdr8, hdr32, 2e-2f, "Debevec realdata 32F vs 8U");
  203. }
  204. TEST(Photo_MergeRobertson, regression_depth_consistency)
  205. {
  206. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  207. vector<Mat> images8;
  208. vector<float> times;
  209. loadExposureSeq(test_path + "exposures/", images8, times);
  210. vector<Mat> images16(images8.size()), images32(images8.size());
  211. for (size_t i = 0; i < images8.size(); ++i)
  212. {
  213. images8[i].convertTo(images16[i], CV_16UC3, 257.0);
  214. images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0);
  215. }
  216. Ptr<MergeRobertson> merge = createMergeRobertson();
  217. Ptr<Tonemap> map = createTonemap();
  218. Mat hdr8, hdr16, hdr32;
  219. merge->process(images8, hdr8, times);
  220. merge->process(images16, hdr16, times);
  221. merge->process(images32, hdr32, times);
  222. map->process(hdr8, hdr8);
  223. map->process(hdr16, hdr16);
  224. map->process(hdr32, hdr32);
  225. checkEqual(hdr8, hdr16, 3e-2f, "Robertson realdata 16U vs 8U");
  226. checkEqual(hdr8, hdr32, 3e-2f, "Robertson realdata 32F vs 8U");
  227. }
  228. TEST(Photo_CalibrateDebevec, regression)
  229. {
  230. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  231. vector<Mat> images;
  232. vector<float> times;
  233. Mat response, expected;
  234. loadExposureSeq(test_path + "exposures/", images, times);
  235. loadResponseCSV(test_path + "calibrate/debevec.csv", expected);
  236. Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
  237. calibrate->process(images, response, times);
  238. Mat diff = abs(response - expected);
  239. diff = diff.mul(1.0f / response);
  240. double max;
  241. minMaxLoc(diff, NULL, &max);
  242. #if defined(__arm__) || defined(__aarch64__)
  243. ASSERT_LT(max, 0.25);
  244. #else
  245. ASSERT_LT(max, 0.15);
  246. #endif
  247. }
  248. TEST(Photo_CalibrateRobertson, regression)
  249. {
  250. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  251. vector<Mat> images;
  252. vector<float> times;
  253. Mat response, expected;
  254. loadExposureSeq(test_path + "exposures/", images, times);
  255. loadResponseCSV(test_path + "calibrate/robertson.csv", expected);
  256. Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson();
  257. calibrate->process(images, response, times);
  258. checkEqual(expected, response, 1e-1f, "CalibrateRobertson");
  259. }
  260. TEST(Photo_CalibrateRobertson, bug_18180)
  261. {
  262. vector<Mat> images;
  263. vector<cv::String> fn;
  264. string test_path = cvtest::TS::ptr()->get_data_path() + "hdr/exposures/bug_18180/";
  265. for(int i = 1; i <= 4; ++i)
  266. images.push_back(imread(test_path + std::to_string(i) + ".jpg"));
  267. vector<float> times {15.0f, 2.5f, 0.25f, 0.33f};
  268. Mat response, expected;
  269. Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson(2, 0.01f);
  270. calibrate->process(images, response, times);
  271. Mat response_no_nans = response.clone();
  272. patchNaNs(response_no_nans);
  273. // since there should be no NaNs, original response vs. response with NaNs patched should be identical
  274. EXPECT_EQ(0.0, cv::norm(response, response_no_nans, NORM_L2));
  275. }
  276. TEST(Photo_CalibrateDebevec, bug_24966)
  277. {
  278. string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
  279. vector<Mat> all_images;
  280. vector<float> all_times;
  281. loadExposureSeq(test_path + "exposures/", all_images, all_times);
  282. // Use a balanced subset of exposures
  283. vector<int> selected_indices = {1,2,3,4,5};
  284. vector<Mat> images;
  285. vector<float> times;
  286. for (int idx : selected_indices) {
  287. images.push_back(all_images[idx]);
  288. times.push_back(all_times[idx]);
  289. }
  290. // Run CRF estimation for different sample points
  291. vector<int> sample_points = {200,300,400};
  292. vector<Mat> responses;
  293. for (int samples : sample_points) {
  294. Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec(samples);
  295. Mat response;
  296. calibrate->process(images, response, times);
  297. Mat roi = response.rowRange(15, 240); //Checking CRF only in the middle of the image
  298. responses.push_back(roi);
  299. }
  300. // Compare consecutive pairs of CRFs
  301. for (size_t i = 0; i < responses.size()-1; ++i) {
  302. Mat diff = abs(responses[i] - responses[i+1]);
  303. double max_diff;
  304. minMaxLoc(diff, nullptr, &max_diff);
  305. cout << "max_diff = " << max_diff << endl;
  306. #if defined(__aarch64__) && defined(__APPLE__)
  307. ASSERT_LT(max_diff, 10) << "CRF instability detected between samples="
  308. << sample_points[i] << " and " << sample_points[i+1]
  309. << " (max diff = " << max_diff << ")";
  310. #else
  311. ASSERT_LT(max_diff, 5) << "CRF instability detected between samples="
  312. << sample_points[i] << " and " << sample_points[i+1]
  313. << " (max diff = " << max_diff << ")";
  314. #endif
  315. }
  316. }
  317. }} // namespace