PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/samples/cpp/stitching_detailed.cpp

https://gitlab.com/gaurav1981/opencv
C++ | 817 lines | 684 code | 70 blank | 63 comment | 258 complexity | 91865179877b17ac80f5ccd4f7301afd MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  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. //
  42. //M*/
  43. #include <iostream>
  44. #include <fstream>
  45. #include <string>
  46. #include "opencv2/opencv_modules.hpp"
  47. #include <opencv2/core/utility.hpp>
  48. #include "opencv2/highgui.hpp"
  49. #include "opencv2/stitching/detail/autocalib.hpp"
  50. #include "opencv2/stitching/detail/blenders.hpp"
  51. #include "opencv2/stitching/detail/camera.hpp"
  52. #include "opencv2/stitching/detail/exposure_compensate.hpp"
  53. #include "opencv2/stitching/detail/matchers.hpp"
  54. #include "opencv2/stitching/detail/motion_estimators.hpp"
  55. #include "opencv2/stitching/detail/seam_finders.hpp"
  56. #include "opencv2/stitching/detail/util.hpp"
  57. #include "opencv2/stitching/detail/warpers.hpp"
  58. #include "opencv2/stitching/warpers.hpp"
  59. using namespace std;
  60. using namespace cv;
  61. using namespace cv::detail;
  62. static void printUsage()
  63. {
  64. cout <<
  65. "Rotation model images stitcher.\n\n"
  66. "stitching_detailed img1 img2 [...imgN] [flags]\n\n"
  67. "Flags:\n"
  68. " --preview\n"
  69. " Run stitching in the preview mode. Works faster than usual mode,\n"
  70. " but output image will have lower resolution.\n"
  71. " --try_cuda (yes|no)\n"
  72. " Try to use CUDA. The default value is 'no'. All default values\n"
  73. " are for CPU mode.\n"
  74. " --try_ocl (yes|no)\n"
  75. " Try to use OpenCL. The default value is 'no'. All default values\n"
  76. " are for CPU mode.\n"
  77. "\nMotion Estimation Flags:\n"
  78. " --work_megapix <float>\n"
  79. " Resolution for image registration step. The default is 0.6 Mpx.\n"
  80. " --features (surf|orb)\n"
  81. " Type of features used for images matching. The default is surf.\n"
  82. " --match_conf <float>\n"
  83. " Confidence for feature matching step. The default is 0.65 for surf and 0.3 for orb.\n"
  84. " --conf_thresh <float>\n"
  85. " Threshold for two images are from the same panorama confidence.\n"
  86. " The default is 1.0.\n"
  87. " --ba (reproj|ray)\n"
  88. " Bundle adjustment cost function. The default is ray.\n"
  89. " --ba_refine_mask (mask)\n"
  90. " Set refinement mask for bundle adjustment. It looks like 'x_xxx',\n"
  91. " where 'x' means refine respective parameter and '_' means don't\n"
  92. " refine one, and has the following format:\n"
  93. " <fx><skew><ppx><aspect><ppy>. The default mask is 'xxxxx'. If bundle\n"
  94. " adjustment doesn't support estimation of selected parameter then\n"
  95. " the respective flag is ignored.\n"
  96. " --wave_correct (no|horiz|vert)\n"
  97. " Perform wave effect correction. The default is 'horiz'.\n"
  98. " --save_graph <file_name>\n"
  99. " Save matches graph represented in DOT language to <file_name> file.\n"
  100. " Labels description: Nm is number of matches, Ni is number of inliers,\n"
  101. " C is confidence.\n"
  102. "\nCompositing Flags:\n"
  103. " --warp (plane|cylindrical|spherical|fisheye|stereographic|compressedPlaneA2B1|compressedPlaneA1.5B1|compressedPlanePortraitA2B1|compressedPlanePortraitA1.5B1|paniniA2B1|paniniA1.5B1|paniniPortraitA2B1|paniniPortraitA1.5B1|mercator|transverseMercator)\n"
  104. " Warp surface type. The default is 'spherical'.\n"
  105. " --seam_megapix <float>\n"
  106. " Resolution for seam estimation step. The default is 0.1 Mpx.\n"
  107. " --seam (no|voronoi|gc_color|gc_colorgrad)\n"
  108. " Seam estimation method. The default is 'gc_color'.\n"
  109. " --compose_megapix <float>\n"
  110. " Resolution for compositing step. Use -1 for original resolution.\n"
  111. " The default is -1.\n"
  112. " --expos_comp (no|gain|gain_blocks)\n"
  113. " Exposure compensation method. The default is 'gain_blocks'.\n"
  114. " --blend (no|feather|multiband)\n"
  115. " Blending method. The default is 'multiband'.\n"
  116. " --blend_strength <float>\n"
  117. " Blending strength from [0,100] range. The default is 5.\n"
  118. " --output <result_img>\n"
  119. " The default is 'result.jpg'.\n";
  120. }
  121. // Default command line args
  122. vector<String> img_names;
  123. bool preview = false;
  124. bool try_cuda = false;
  125. bool try_ocl = false;
  126. double work_megapix = 0.6;
  127. double seam_megapix = 0.1;
  128. double compose_megapix = -1;
  129. float conf_thresh = 1.f;
  130. string features_type = "surf";
  131. string ba_cost_func = "ray";
  132. string ba_refine_mask = "xxxxx";
  133. bool do_wave_correct = true;
  134. WaveCorrectKind wave_correct = detail::WAVE_CORRECT_HORIZ;
  135. bool save_graph = false;
  136. std::string save_graph_to;
  137. string warp_type = "spherical";
  138. int expos_comp_type = ExposureCompensator::GAIN_BLOCKS;
  139. float match_conf = 0.3f;
  140. string seam_find_type = "gc_color";
  141. int blend_type = Blender::MULTI_BAND;
  142. float blend_strength = 5;
  143. string result_name = "result.jpg";
  144. static int parseCmdArgs(int argc, char** argv)
  145. {
  146. if (argc == 1)
  147. {
  148. printUsage();
  149. return -1;
  150. }
  151. for (int i = 1; i < argc; ++i)
  152. {
  153. if (string(argv[i]) == "--help" || string(argv[i]) == "/?")
  154. {
  155. printUsage();
  156. return -1;
  157. }
  158. else if (string(argv[i]) == "--preview")
  159. {
  160. preview = true;
  161. }
  162. else if (string(argv[i]) == "--try_cuda")
  163. {
  164. if (string(argv[i + 1]) == "no")
  165. try_cuda = false;
  166. else if (string(argv[i + 1]) == "yes")
  167. try_cuda = true;
  168. else
  169. {
  170. cout << "Bad --try_cuda flag value\n";
  171. return -1;
  172. }
  173. i++;
  174. }
  175. else if (string(argv[i]) == "--try_ocl")
  176. {
  177. if (string(argv[i + 1]) == "no")
  178. try_ocl = false;
  179. else if (string(argv[i + 1]) == "yes")
  180. try_ocl = true;
  181. else
  182. {
  183. cout << "Bad --try_ocl flag value\n";
  184. return -1;
  185. }
  186. i++;
  187. }
  188. else if (string(argv[i]) == "--work_megapix")
  189. {
  190. work_megapix = atof(argv[i + 1]);
  191. i++;
  192. }
  193. else if (string(argv[i]) == "--seam_megapix")
  194. {
  195. seam_megapix = atof(argv[i + 1]);
  196. i++;
  197. }
  198. else if (string(argv[i]) == "--compose_megapix")
  199. {
  200. compose_megapix = atof(argv[i + 1]);
  201. i++;
  202. }
  203. else if (string(argv[i]) == "--result")
  204. {
  205. result_name = argv[i + 1];
  206. i++;
  207. }
  208. else if (string(argv[i]) == "--features")
  209. {
  210. features_type = argv[i + 1];
  211. if (features_type == "orb")
  212. match_conf = 0.3f;
  213. i++;
  214. }
  215. else if (string(argv[i]) == "--match_conf")
  216. {
  217. match_conf = static_cast<float>(atof(argv[i + 1]));
  218. i++;
  219. }
  220. else if (string(argv[i]) == "--conf_thresh")
  221. {
  222. conf_thresh = static_cast<float>(atof(argv[i + 1]));
  223. i++;
  224. }
  225. else if (string(argv[i]) == "--ba")
  226. {
  227. ba_cost_func = argv[i + 1];
  228. i++;
  229. }
  230. else if (string(argv[i]) == "--ba_refine_mask")
  231. {
  232. ba_refine_mask = argv[i + 1];
  233. if (ba_refine_mask.size() != 5)
  234. {
  235. cout << "Incorrect refinement mask length.\n";
  236. return -1;
  237. }
  238. i++;
  239. }
  240. else if (string(argv[i]) == "--wave_correct")
  241. {
  242. if (string(argv[i + 1]) == "no")
  243. do_wave_correct = false;
  244. else if (string(argv[i + 1]) == "horiz")
  245. {
  246. do_wave_correct = true;
  247. wave_correct = detail::WAVE_CORRECT_HORIZ;
  248. }
  249. else if (string(argv[i + 1]) == "vert")
  250. {
  251. do_wave_correct = true;
  252. wave_correct = detail::WAVE_CORRECT_VERT;
  253. }
  254. else
  255. {
  256. cout << "Bad --wave_correct flag value\n";
  257. return -1;
  258. }
  259. i++;
  260. }
  261. else if (string(argv[i]) == "--save_graph")
  262. {
  263. save_graph = true;
  264. save_graph_to = argv[i + 1];
  265. i++;
  266. }
  267. else if (string(argv[i]) == "--warp")
  268. {
  269. warp_type = string(argv[i + 1]);
  270. i++;
  271. }
  272. else if (string(argv[i]) == "--expos_comp")
  273. {
  274. if (string(argv[i + 1]) == "no")
  275. expos_comp_type = ExposureCompensator::NO;
  276. else if (string(argv[i + 1]) == "gain")
  277. expos_comp_type = ExposureCompensator::GAIN;
  278. else if (string(argv[i + 1]) == "gain_blocks")
  279. expos_comp_type = ExposureCompensator::GAIN_BLOCKS;
  280. else
  281. {
  282. cout << "Bad exposure compensation method\n";
  283. return -1;
  284. }
  285. i++;
  286. }
  287. else if (string(argv[i]) == "--seam")
  288. {
  289. if (string(argv[i + 1]) == "no" ||
  290. string(argv[i + 1]) == "voronoi" ||
  291. string(argv[i + 1]) == "gc_color" ||
  292. string(argv[i + 1]) == "gc_colorgrad" ||
  293. string(argv[i + 1]) == "dp_color" ||
  294. string(argv[i + 1]) == "dp_colorgrad")
  295. seam_find_type = argv[i + 1];
  296. else
  297. {
  298. cout << "Bad seam finding method\n";
  299. return -1;
  300. }
  301. i++;
  302. }
  303. else if (string(argv[i]) == "--blend")
  304. {
  305. if (string(argv[i + 1]) == "no")
  306. blend_type = Blender::NO;
  307. else if (string(argv[i + 1]) == "feather")
  308. blend_type = Blender::FEATHER;
  309. else if (string(argv[i + 1]) == "multiband")
  310. blend_type = Blender::MULTI_BAND;
  311. else
  312. {
  313. cout << "Bad blending method\n";
  314. return -1;
  315. }
  316. i++;
  317. }
  318. else if (string(argv[i]) == "--blend_strength")
  319. {
  320. blend_strength = static_cast<float>(atof(argv[i + 1]));
  321. i++;
  322. }
  323. else if (string(argv[i]) == "--output")
  324. {
  325. result_name = argv[i + 1];
  326. i++;
  327. }
  328. else
  329. img_names.push_back(argv[i]);
  330. }
  331. if (preview)
  332. {
  333. compose_megapix = 0.6;
  334. }
  335. return 0;
  336. }
  337. int main(int argc, char* argv[])
  338. {
  339. #if ENABLE_LOG
  340. int64 app_start_time = getTickCount();
  341. #endif
  342. cv::setBreakOnError(true);
  343. int retval = parseCmdArgs(argc, argv);
  344. if (retval)
  345. return retval;
  346. // Check if have enough images
  347. int num_images = static_cast<int>(img_names.size());
  348. if (num_images < 2)
  349. {
  350. LOGLN("Need more images");
  351. return -1;
  352. }
  353. double work_scale = 1, seam_scale = 1, compose_scale = 1;
  354. bool is_work_scale_set = false, is_seam_scale_set = false, is_compose_scale_set = false;
  355. LOGLN("Finding features...");
  356. #if ENABLE_LOG
  357. int64 t = getTickCount();
  358. #endif
  359. Ptr<FeaturesFinder> finder;
  360. if (features_type == "surf")
  361. {
  362. #ifdef HAVE_OPENCV_NONFREE
  363. if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0)
  364. finder = makePtr<SurfFeaturesFinderGpu>();
  365. else
  366. #endif
  367. finder = makePtr<SurfFeaturesFinder>();
  368. }
  369. else if (features_type == "orb")
  370. {
  371. finder = makePtr<OrbFeaturesFinder>();
  372. }
  373. else
  374. {
  375. cout << "Unknown 2D features type: '" << features_type << "'.\n";
  376. return -1;
  377. }
  378. Mat full_img, img;
  379. vector<ImageFeatures> features(num_images);
  380. vector<Mat> images(num_images);
  381. vector<Size> full_img_sizes(num_images);
  382. double seam_work_aspect = 1;
  383. for (int i = 0; i < num_images; ++i)
  384. {
  385. full_img = imread(img_names[i]);
  386. full_img_sizes[i] = full_img.size();
  387. if (full_img.empty())
  388. {
  389. LOGLN("Can't open image " << img_names[i]);
  390. return -1;
  391. }
  392. if (work_megapix < 0)
  393. {
  394. img = full_img;
  395. work_scale = 1;
  396. is_work_scale_set = true;
  397. }
  398. else
  399. {
  400. if (!is_work_scale_set)
  401. {
  402. work_scale = min(1.0, sqrt(work_megapix * 1e6 / full_img.size().area()));
  403. is_work_scale_set = true;
  404. }
  405. resize(full_img, img, Size(), work_scale, work_scale);
  406. }
  407. if (!is_seam_scale_set)
  408. {
  409. seam_scale = min(1.0, sqrt(seam_megapix * 1e6 / full_img.size().area()));
  410. seam_work_aspect = seam_scale / work_scale;
  411. is_seam_scale_set = true;
  412. }
  413. (*finder)(img, features[i]);
  414. features[i].img_idx = i;
  415. LOGLN("Features in image #" << i+1 << ": " << features[i].keypoints.size());
  416. resize(full_img, img, Size(), seam_scale, seam_scale);
  417. images[i] = img.clone();
  418. }
  419. finder->collectGarbage();
  420. full_img.release();
  421. img.release();
  422. LOGLN("Finding features, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
  423. LOG("Pairwise matching");
  424. #if ENABLE_LOG
  425. t = getTickCount();
  426. #endif
  427. vector<MatchesInfo> pairwise_matches;
  428. BestOf2NearestMatcher matcher(try_cuda, match_conf);
  429. matcher(features, pairwise_matches);
  430. matcher.collectGarbage();
  431. LOGLN("Pairwise matching, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
  432. // Check if we should save matches graph
  433. if (save_graph)
  434. {
  435. LOGLN("Saving matches graph...");
  436. ofstream f(save_graph_to.c_str());
  437. f << matchesGraphAsString(img_names, pairwise_matches, conf_thresh);
  438. }
  439. // Leave only images we are sure are from the same panorama
  440. vector<int> indices = leaveBiggestComponent(features, pairwise_matches, conf_thresh);
  441. vector<Mat> img_subset;
  442. vector<String> img_names_subset;
  443. vector<Size> full_img_sizes_subset;
  444. for (size_t i = 0; i < indices.size(); ++i)
  445. {
  446. img_names_subset.push_back(img_names[indices[i]]);
  447. img_subset.push_back(images[indices[i]]);
  448. full_img_sizes_subset.push_back(full_img_sizes[indices[i]]);
  449. }
  450. images = img_subset;
  451. img_names = img_names_subset;
  452. full_img_sizes = full_img_sizes_subset;
  453. // Check if we still have enough images
  454. num_images = static_cast<int>(img_names.size());
  455. if (num_images < 2)
  456. {
  457. LOGLN("Need more images");
  458. return -1;
  459. }
  460. HomographyBasedEstimator estimator;
  461. vector<CameraParams> cameras;
  462. if (!estimator(features, pairwise_matches, cameras))
  463. {
  464. cout << "Homography estimation failed.\n";
  465. return -1;
  466. }
  467. for (size_t i = 0; i < cameras.size(); ++i)
  468. {
  469. Mat R;
  470. cameras[i].R.convertTo(R, CV_32F);
  471. cameras[i].R = R;
  472. LOGLN("Initial intrinsics #" << indices[i]+1 << ":\n" << cameras[i].K());
  473. }
  474. Ptr<detail::BundleAdjusterBase> adjuster;
  475. if (ba_cost_func == "reproj") adjuster = makePtr<detail::BundleAdjusterReproj>();
  476. else if (ba_cost_func == "ray") adjuster = makePtr<detail::BundleAdjusterRay>();
  477. else
  478. {
  479. cout << "Unknown bundle adjustment cost function: '" << ba_cost_func << "'.\n";
  480. return -1;
  481. }
  482. adjuster->setConfThresh(conf_thresh);
  483. Mat_<uchar> refine_mask = Mat::zeros(3, 3, CV_8U);
  484. if (ba_refine_mask[0] == 'x') refine_mask(0,0) = 1;
  485. if (ba_refine_mask[1] == 'x') refine_mask(0,1) = 1;
  486. if (ba_refine_mask[2] == 'x') refine_mask(0,2) = 1;
  487. if (ba_refine_mask[3] == 'x') refine_mask(1,1) = 1;
  488. if (ba_refine_mask[4] == 'x') refine_mask(1,2) = 1;
  489. adjuster->setRefinementMask(refine_mask);
  490. if (!(*adjuster)(features, pairwise_matches, cameras))
  491. {
  492. cout << "Camera parameters adjusting failed.\n";
  493. return -1;
  494. }
  495. // Find median focal length
  496. vector<double> focals;
  497. for (size_t i = 0; i < cameras.size(); ++i)
  498. {
  499. LOGLN("Camera #" << indices[i]+1 << ":\n" << cameras[i].K());
  500. focals.push_back(cameras[i].focal);
  501. }
  502. sort(focals.begin(), focals.end());
  503. float warped_image_scale;
  504. if (focals.size() % 2 == 1)
  505. warped_image_scale = static_cast<float>(focals[focals.size() / 2]);
  506. else
  507. warped_image_scale = static_cast<float>(focals[focals.size() / 2 - 1] + focals[focals.size() / 2]) * 0.5f;
  508. if (do_wave_correct)
  509. {
  510. vector<Mat> rmats;
  511. for (size_t i = 0; i < cameras.size(); ++i)
  512. rmats.push_back(cameras[i].R);
  513. waveCorrect(rmats, wave_correct);
  514. for (size_t i = 0; i < cameras.size(); ++i)
  515. cameras[i].R = rmats[i];
  516. }
  517. LOGLN("Warping images (auxiliary)... ");
  518. #if ENABLE_LOG
  519. t = getTickCount();
  520. #endif
  521. vector<Point> corners(num_images);
  522. vector<Mat> masks_warped(num_images);
  523. vector<Mat> images_warped(num_images);
  524. vector<Size> sizes(num_images);
  525. vector<Mat> masks(num_images);
  526. // Preapre images masks
  527. for (int i = 0; i < num_images; ++i)
  528. {
  529. masks[i].create(images[i].size(), CV_8U);
  530. masks[i].setTo(Scalar::all(255));
  531. }
  532. // Warp images and their masks
  533. Ptr<WarperCreator> warper_creator;
  534. if (try_ocl)
  535. {
  536. if (warp_type == "plane")
  537. warper_creator = makePtr<cv::PlaneWarperOcl>();
  538. else if (warp_type == "cylindrical")
  539. warper_creator = makePtr<cv::CylindricalWarperOcl>();
  540. else if (warp_type == "spherical")
  541. warper_creator = makePtr<cv::SphericalWarperOcl>();
  542. }
  543. #ifdef HAVE_OPENCV_CUDAWARPING
  544. else if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0)
  545. {
  546. if (warp_type == "plane")
  547. warper_creator = makePtr<cv::PlaneWarperGpu>();
  548. else if (warp_type == "cylindrical")
  549. warper_creator = makePtr<cv::CylindricalWarperGpu>();
  550. else if (warp_type == "spherical")
  551. warper_creator = makePtr<cv::SphericalWarperGpu>();
  552. }
  553. else
  554. #endif
  555. {
  556. if (warp_type == "plane")
  557. warper_creator = makePtr<cv::PlaneWarper>();
  558. else if (warp_type == "cylindrical")
  559. warper_creator = makePtr<cv::CylindricalWarper>();
  560. else if (warp_type == "spherical")
  561. warper_creator = makePtr<cv::SphericalWarper>();
  562. else if (warp_type == "fisheye")
  563. warper_creator = makePtr<cv::FisheyeWarper>();
  564. else if (warp_type == "stereographic")
  565. warper_creator = makePtr<cv::StereographicWarper>();
  566. else if (warp_type == "compressedPlaneA2B1")
  567. warper_creator = makePtr<cv::CompressedRectilinearWarper>(2.0f, 1.0f);
  568. else if (warp_type == "compressedPlaneA1.5B1")
  569. warper_creator = makePtr<cv::CompressedRectilinearWarper>(1.5f, 1.0f);
  570. else if (warp_type == "compressedPlanePortraitA2B1")
  571. warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(2.0f, 1.0f);
  572. else if (warp_type == "compressedPlanePortraitA1.5B1")
  573. warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(1.5f, 1.0f);
  574. else if (warp_type == "paniniA2B1")
  575. warper_creator = makePtr<cv::PaniniWarper>(2.0f, 1.0f);
  576. else if (warp_type == "paniniA1.5B1")
  577. warper_creator = makePtr<cv::PaniniWarper>(1.5f, 1.0f);
  578. else if (warp_type == "paniniPortraitA2B1")
  579. warper_creator = makePtr<cv::PaniniPortraitWarper>(2.0f, 1.0f);
  580. else if (warp_type == "paniniPortraitA1.5B1")
  581. warper_creator = makePtr<cv::PaniniPortraitWarper>(1.5f, 1.0f);
  582. else if (warp_type == "mercator")
  583. warper_creator = makePtr<cv::MercatorWarper>();
  584. else if (warp_type == "transverseMercator")
  585. warper_creator = makePtr<cv::TransverseMercatorWarper>();
  586. }
  587. if (!warper_creator)
  588. {
  589. cout << "Can't create the following warper '" << warp_type << "'\n";
  590. return 1;
  591. }
  592. Ptr<RotationWarper> warper = warper_creator->create(static_cast<float>(warped_image_scale * seam_work_aspect));
  593. for (int i = 0; i < num_images; ++i)
  594. {
  595. Mat_<float> K;
  596. cameras[i].K().convertTo(K, CV_32F);
  597. float swa = (float)seam_work_aspect;
  598. K(0,0) *= swa; K(0,2) *= swa;
  599. K(1,1) *= swa; K(1,2) *= swa;
  600. corners[i] = warper->warp(images[i], K, cameras[i].R, INTER_LINEAR, BORDER_REFLECT, images_warped[i]);
  601. sizes[i] = images_warped[i].size();
  602. warper->warp(masks[i], K, cameras[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warped[i]);
  603. }
  604. vector<Mat> images_warped_f(num_images);
  605. for (int i = 0; i < num_images; ++i)
  606. images_warped[i].convertTo(images_warped_f[i], CV_32F);
  607. LOGLN("Warping images, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
  608. Ptr<ExposureCompensator> compensator = ExposureCompensator::createDefault(expos_comp_type);
  609. compensator->feed(corners, images_warped, masks_warped);
  610. Ptr<SeamFinder> seam_finder;
  611. if (seam_find_type == "no")
  612. seam_finder = makePtr<detail::NoSeamFinder>();
  613. else if (seam_find_type == "voronoi")
  614. seam_finder = makePtr<detail::VoronoiSeamFinder>();
  615. else if (seam_find_type == "gc_color")
  616. {
  617. #ifdef HAVE_OPENCV_CUDA
  618. if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0)
  619. seam_finder = makePtr<detail::GraphCutSeamFinderGpu>(GraphCutSeamFinderBase::COST_COLOR);
  620. else
  621. #endif
  622. seam_finder = makePtr<detail::GraphCutSeamFinder>(GraphCutSeamFinderBase::COST_COLOR);
  623. }
  624. else if (seam_find_type == "gc_colorgrad")
  625. {
  626. #ifdef HAVE_OPENCV_CUDA
  627. if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0)
  628. seam_finder = makePtr<detail::GraphCutSeamFinderGpu>(GraphCutSeamFinderBase::COST_COLOR_GRAD);
  629. else
  630. #endif
  631. seam_finder = makePtr<detail::GraphCutSeamFinder>(GraphCutSeamFinderBase::COST_COLOR_GRAD);
  632. }
  633. else if (seam_find_type == "dp_color")
  634. seam_finder = makePtr<detail::DpSeamFinder>(DpSeamFinder::COLOR);
  635. else if (seam_find_type == "dp_colorgrad")
  636. seam_finder = makePtr<detail::DpSeamFinder>(DpSeamFinder::COLOR_GRAD);
  637. if (!seam_finder)
  638. {
  639. cout << "Can't create the following seam finder '" << seam_find_type << "'\n";
  640. return 1;
  641. }
  642. seam_finder->find(images_warped_f, corners, masks_warped);
  643. // Release unused memory
  644. images.clear();
  645. images_warped.clear();
  646. images_warped_f.clear();
  647. masks.clear();
  648. LOGLN("Compositing...");
  649. #if ENABLE_LOG
  650. t = getTickCount();
  651. #endif
  652. Mat img_warped, img_warped_s;
  653. Mat dilated_mask, seam_mask, mask, mask_warped;
  654. Ptr<Blender> blender;
  655. //double compose_seam_aspect = 1;
  656. double compose_work_aspect = 1;
  657. for (int img_idx = 0; img_idx < num_images; ++img_idx)
  658. {
  659. LOGLN("Compositing image #" << indices[img_idx]+1);
  660. // Read image and resize it if necessary
  661. full_img = imread(img_names[img_idx]);
  662. if (!is_compose_scale_set)
  663. {
  664. if (compose_megapix > 0)
  665. compose_scale = min(1.0, sqrt(compose_megapix * 1e6 / full_img.size().area()));
  666. is_compose_scale_set = true;
  667. // Compute relative scales
  668. //compose_seam_aspect = compose_scale / seam_scale;
  669. compose_work_aspect = compose_scale / work_scale;
  670. // Update warped image scale
  671. warped_image_scale *= static_cast<float>(compose_work_aspect);
  672. warper = warper_creator->create(warped_image_scale);
  673. // Update corners and sizes
  674. for (int i = 0; i < num_images; ++i)
  675. {
  676. // Update intrinsics
  677. cameras[i].focal *= compose_work_aspect;
  678. cameras[i].ppx *= compose_work_aspect;
  679. cameras[i].ppy *= compose_work_aspect;
  680. // Update corner and size
  681. Size sz = full_img_sizes[i];
  682. if (std::abs(compose_scale - 1) > 1e-1)
  683. {
  684. sz.width = cvRound(full_img_sizes[i].width * compose_scale);
  685. sz.height = cvRound(full_img_sizes[i].height * compose_scale);
  686. }
  687. Mat K;
  688. cameras[i].K().convertTo(K, CV_32F);
  689. Rect roi = warper->warpRoi(sz, K, cameras[i].R);
  690. corners[i] = roi.tl();
  691. sizes[i] = roi.size();
  692. }
  693. }
  694. if (abs(compose_scale - 1) > 1e-1)
  695. resize(full_img, img, Size(), compose_scale, compose_scale);
  696. else
  697. img = full_img;
  698. full_img.release();
  699. Size img_size = img.size();
  700. Mat K;
  701. cameras[img_idx].K().convertTo(K, CV_32F);
  702. // Warp the current image
  703. warper->warp(img, K, cameras[img_idx].R, INTER_LINEAR, BORDER_REFLECT, img_warped);
  704. // Warp the current image mask
  705. mask.create(img_size, CV_8U);
  706. mask.setTo(Scalar::all(255));
  707. warper->warp(mask, K, cameras[img_idx].R, INTER_NEAREST, BORDER_CONSTANT, mask_warped);
  708. // Compensate exposure
  709. compensator->apply(img_idx, corners[img_idx], img_warped, mask_warped);
  710. img_warped.convertTo(img_warped_s, CV_16S);
  711. img_warped.release();
  712. img.release();
  713. mask.release();
  714. dilate(masks_warped[img_idx], dilated_mask, Mat());
  715. resize(dilated_mask, seam_mask, mask_warped.size());
  716. mask_warped = seam_mask & mask_warped;
  717. if (!blender)
  718. {
  719. blender = Blender::createDefault(blend_type, try_cuda);
  720. Size dst_sz = resultRoi(corners, sizes).size();
  721. float blend_width = sqrt(static_cast<float>(dst_sz.area())) * blend_strength / 100.f;
  722. if (blend_width < 1.f)
  723. blender = Blender::createDefault(Blender::NO, try_cuda);
  724. else if (blend_type == Blender::MULTI_BAND)
  725. {
  726. MultiBandBlender* mb = dynamic_cast<MultiBandBlender*>(blender.get());
  727. mb->setNumBands(static_cast<int>(ceil(log(blend_width)/log(2.)) - 1.));
  728. LOGLN("Multi-band blender, number of bands: " << mb->numBands());
  729. }
  730. else if (blend_type == Blender::FEATHER)
  731. {
  732. FeatherBlender* fb = dynamic_cast<FeatherBlender*>(blender.get());
  733. fb->setSharpness(1.f/blend_width);
  734. LOGLN("Feather blender, sharpness: " << fb->sharpness());
  735. }
  736. blender->prepare(corners, sizes);
  737. }
  738. // Blend the current image
  739. blender->feed(img_warped_s, mask_warped, corners[img_idx]);
  740. }
  741. Mat result, result_mask;
  742. blender->blend(result, result_mask);
  743. LOGLN("Compositing, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
  744. imwrite(result_name, result);
  745. LOGLN("Finished, total time: " << ((getTickCount() - app_start_time) / getTickFrequency()) << " sec");
  746. return 0;
  747. }