PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/toolkits/computer_vision/stitch_main.cpp

https://github.com/michaelkook/GraphLab-2
C++ | 429 lines | 327 code | 36 blank | 66 comment | 108 complexity | a157392e5fe3d9dbea110802fa6cf8d7 MD5 | raw file
Possible License(s): ISC, Apache-2.0
  1. /**
  2. * Copyright (c) 2009 Carnegie Mellon University.
  3. * All rights reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing,
  12. * software distributed under the License is distributed on an "AS
  13. * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  14. * express or implied. See the License for the specific language
  15. * governing permissions and limitations under the License.
  16. *
  17. * For more about this software visit:
  18. *
  19. * http://www.graphlab.ml.cmu.edu
  20. *
  21. */
  22. /**
  23. *
  24. * \brief This file contains an example of graphlab used for stitching
  25. * multiple images into a panorama. The code is based on a example
  26. * stiching application in OpenCV.
  27. *
  28. * \author Dhruv Batra
  29. */
  30. #include "stitch_main.hpp"
  31. void printUsage()
  32. {
  33. cout <<
  34. "Rotation model images stitcher.\n\n"
  35. "stitching_detailed img1 img2 [...imgN] [flags]\n\n"
  36. "Flags:\n"
  37. " --preview\n"
  38. " Run stitching in the preview mode. Works faster than usual mode,\n"
  39. " but output image will have lower resolution.\n"
  40. " --try_gpu (yes|no)\n"
  41. " Try to use GPU. The default value is 'no'. All default values\n"
  42. " are for CPU mode.\n"
  43. "\nMotion Estimation Flags:\n"
  44. " --work_megapix <float>\n"
  45. " Resolution for image registration step. The default is 0.6 Mpx.\n"
  46. " --features (surf|orb)\n"
  47. " Type of features used for images matching. The default is surf.\n"
  48. " --match_conf <float>\n"
  49. " Confidence for feature matching step. The default is 0.65 for surf and 0.3 for orb.\n"
  50. " --conf_thresh <float>\n"
  51. " Threshold for two images are from the same panorama confidence.\n"
  52. " The default is 1.0.\n"
  53. " --ba (reproj|ray)\n"
  54. " Bundle adjustment cost function. The default is ray.\n"
  55. " --ba_refine_mask (mask)\n"
  56. " Set refinement mask for bundle adjustment. It looks like 'x_xxx',\n"
  57. " where 'x' means refine respective parameter and '_' means don't\n"
  58. " refine one, and has the following format:\n"
  59. " <fx><skew><ppx><aspect><ppy>. The default mask is 'xxxxx'. If bundle\n"
  60. " adjustment doesn't support estimation of selected parameter then\n"
  61. " the respective flag is ignored.\n"
  62. " --wave_correct (no|horiz|vert)\n"
  63. " Perform wave effect correction. The default is 'horiz'.\n"
  64. " --save_graph <file_name>\n"
  65. " Save matches graph represented in DOT language to <file_name> file.\n"
  66. " Labels description: Nm is number of matches, Ni is number of inliers,\n"
  67. " C is confidence.\n"
  68. "\nCompositing Flags:\n"
  69. " --warp (plane|cylindrical|spherical|fisheye|stereographic|compressedPlaneA2B1|compressedPlaneA1.5B1|compressedPlanePortraitA2B1|compressedPlanePortraitA1.5B1|paniniA2B1|paniniA1.5B1|paniniPortraitA2B1|paniniPortraitA1.5B1|mercator|transverseMercator)\n"
  70. " Warp surface type. The default is 'spherical'.\n"
  71. " --seam_megapix <float>\n"
  72. " Resolution for seam estimation step. The default is 0.1 Mpx.\n"
  73. " --seam (no|voronoi|gc_color|gc_colorgrad)\n"
  74. " Seam estimation method. The default is 'gc_color'.\n"
  75. " --compose_megapix <float>\n"
  76. " Resolution for compositing step. Use -1 for original resolution.\n"
  77. " The default is -1.\n"
  78. " --expos_comp (no|gain|gain_blocks)\n"
  79. " Exposure compensation method. The default is 'gain_blocks'.\n"
  80. " --blend (no|feather|multiband)\n"
  81. " Blending method. The default is 'multiband'.\n"
  82. " --blend_strength <float>\n"
  83. " Blending strength from [0,100] range. The default is 5.\n"
  84. " --output <result_img>\n"
  85. " The default is 'result.jpg'.\n";
  86. }
  87. // Default command line args
  88. vector<string> img_names;
  89. bool preview = false;
  90. bool try_gpu = false;
  91. double work_megapix = 0.6;
  92. double seam_megapix = 0.1;
  93. double compose_megapix = -1;
  94. float conf_thresh = 1.f;
  95. string features = "surf";
  96. string ba_cost_func = "ray";
  97. string ba_refine_mask = "xxxxx";
  98. bool do_wave_correct = true;
  99. WaveCorrectKind wave_correct = detail::WAVE_CORRECT_HORIZ;
  100. bool save_graph = false;
  101. std::string save_graph_to;
  102. string warp_type = "spherical";
  103. int expos_comp_type = ExposureCompensator::GAIN_BLOCKS;
  104. float match_conf = 0.3f;
  105. string seam_find_type = "gc_color";
  106. int blend_type = Blender::MULTI_BAND;
  107. float blend_strength = 5;
  108. string result_name = "result.jpg";
  109. int parseCmdArgs(int argc, char** argv)
  110. {
  111. if (argc == 1)
  112. {
  113. printUsage();
  114. return -1;
  115. }
  116. for (int i = 1; i < argc; ++i)
  117. {
  118. if (string(argv[i]) == "--help" || string(argv[i]) == "/?")
  119. {
  120. printUsage();
  121. return -1;
  122. }
  123. else if (string(argv[i]) == "--preview")
  124. {
  125. preview = true;
  126. }
  127. else if (string(argv[i]) == "--try_gpu")
  128. {
  129. if (string(argv[i + 1]) == "no")
  130. try_gpu = false;
  131. else if (string(argv[i + 1]) == "yes")
  132. try_gpu = true;
  133. else
  134. {
  135. cout << "Bad --try_gpu flag value\n";
  136. return -1;
  137. }
  138. i++;
  139. }
  140. else if (string(argv[i]) == "--work_megapix")
  141. {
  142. work_megapix = atof(argv[i + 1]);
  143. i++;
  144. }
  145. else if (string(argv[i]) == "--seam_megapix")
  146. {
  147. seam_megapix = atof(argv[i + 1]);
  148. i++;
  149. }
  150. else if (string(argv[i]) == "--compose_megapix")
  151. {
  152. compose_megapix = atof(argv[i + 1]);
  153. i++;
  154. }
  155. else if (string(argv[i]) == "--result")
  156. {
  157. result_name = argv[i + 1];
  158. i++;
  159. }
  160. else if (string(argv[i]) == "--features")
  161. {
  162. features = argv[i + 1];
  163. if (features == "orb")
  164. match_conf = 0.3f;
  165. i++;
  166. }
  167. else if (string(argv[i]) == "--match_conf")
  168. {
  169. match_conf = static_cast<float>(atof(argv[i + 1]));
  170. i++;
  171. }
  172. else if (string(argv[i]) == "--conf_thresh")
  173. {
  174. conf_thresh = static_cast<float>(atof(argv[i + 1]));
  175. i++;
  176. }
  177. else if (string(argv[i]) == "--ba")
  178. {
  179. ba_cost_func = argv[i + 1];
  180. i++;
  181. }
  182. else if (string(argv[i]) == "--ba_refine_mask")
  183. {
  184. ba_refine_mask = argv[i + 1];
  185. if (ba_refine_mask.size() != 5)
  186. {
  187. cout << "Incorrect refinement mask length.\n";
  188. return -1;
  189. }
  190. i++;
  191. }
  192. else if (string(argv[i]) == "--wave_correct")
  193. {
  194. if (string(argv[i + 1]) == "no")
  195. do_wave_correct = false;
  196. else if (string(argv[i + 1]) == "horiz")
  197. {
  198. do_wave_correct = true;
  199. wave_correct = detail::WAVE_CORRECT_HORIZ;
  200. }
  201. else if (string(argv[i + 1]) == "vert")
  202. {
  203. do_wave_correct = true;
  204. wave_correct = detail::WAVE_CORRECT_VERT;
  205. }
  206. else
  207. {
  208. cout << "Bad --wave_correct flag value\n";
  209. return -1;
  210. }
  211. i++;
  212. }
  213. else if (string(argv[i]) == "--save_graph")
  214. {
  215. save_graph = true;
  216. save_graph_to = argv[i + 1];
  217. i++;
  218. }
  219. else if (string(argv[i]) == "--warp")
  220. {
  221. warp_type = string(argv[i + 1]);
  222. i++;
  223. }
  224. else if (string(argv[i]) == "--expos_comp")
  225. {
  226. if (string(argv[i + 1]) == "no")
  227. expos_comp_type = ExposureCompensator::NO;
  228. else if (string(argv[i + 1]) == "gain")
  229. expos_comp_type = ExposureCompensator::GAIN;
  230. else if (string(argv[i + 1]) == "gain_blocks")
  231. expos_comp_type = ExposureCompensator::GAIN_BLOCKS;
  232. else
  233. {
  234. cout << "Bad exposure compensation method\n";
  235. return -1;
  236. }
  237. i++;
  238. }
  239. else if (string(argv[i]) == "--seam")
  240. {
  241. if (string(argv[i + 1]) == "no" ||
  242. string(argv[i + 1]) == "voronoi" ||
  243. string(argv[i + 1]) == "gc_color" ||
  244. string(argv[i + 1]) == "gc_colorgrad")
  245. seam_find_type = argv[i + 1];
  246. else
  247. {
  248. cout << "Bad seam finding method\n";
  249. return -1;
  250. }
  251. i++;
  252. }
  253. else if (string(argv[i]) == "--blend")
  254. {
  255. if (string(argv[i + 1]) == "no")
  256. blend_type = Blender::NO;
  257. else if (string(argv[i + 1]) == "feather")
  258. blend_type = Blender::FEATHER;
  259. else if (string(argv[i + 1]) == "multiband")
  260. blend_type = Blender::MULTI_BAND;
  261. else
  262. {
  263. cout << "Bad blending method\n";
  264. return -1;
  265. }
  266. i++;
  267. }
  268. else if (string(argv[i]) == "--blend_strength")
  269. {
  270. blend_strength = static_cast<float>(atof(argv[i + 1]));
  271. i++;
  272. }
  273. else if (string(argv[i]) == "--output")
  274. {
  275. result_name = argv[i + 1];
  276. i++;
  277. }
  278. else
  279. img_names.push_back(argv[i]);
  280. }
  281. if (preview)
  282. {
  283. compose_megapix = 0.6;
  284. }
  285. return 0;
  286. }
  287. /////////////////////////////////////////////////////////////////////////
  288. Options opts;
  289. int main(int argc, char** argv)
  290. {
  291. ///////////////////////////////////////////////////////
  292. // Set up Graphlab
  293. global_logger().set_log_level(LOG_INFO);
  294. global_logger().set_log_to_console(true);
  295. ///! Initialize control plain using mpi
  296. graphlab::mpi_tools::init(argc, argv);
  297. graphlab::distributed_control dc;
  298. ///////////////////////////////////////////////////////
  299. // Set up OpenCV
  300. cv::setBreakOnError(true);
  301. ///////////////////////////////////////////////////////
  302. // Graphlab parse input
  303. const std::string description = "Image Stitching";
  304. graphlab::command_line_options clopts(description);
  305. string img_dir;
  306. string graph_path;
  307. string output_dir = "stitch_output";
  308. clopts.attach_option("img", img_dir,
  309. "The directory containing the images");
  310. clopts.add_positional("img");
  311. clopts.attach_option("graph", graph_path,
  312. "The path to the adjacency list file (could be the prefix in case of multiple files)");
  313. clopts.add_positional("graph");
  314. clopts.attach_option("output", output_dir,
  315. "The directory in which to save the output");
  316. clopts.attach_option("verbose", opts.verbose,
  317. "Verbosity of Printing: 0 (default, no printing) or 1 (lots).");
  318. clopts.attach_option("work_megapix", opts.work_megapix,
  319. "Resolution for image registration step. The default is 0.6 Mpx.");
  320. if(!clopts.parse(argc, argv))
  321. {
  322. graphlab::mpi_tools::finalize();
  323. return clopts.is_set("help")? EXIT_SUCCESS : EXIT_FAILURE;
  324. }
  325. if(img_dir.empty())
  326. {
  327. logstream(LOG_ERROR) << "No image directory was provided." << std::endl;
  328. return EXIT_FAILURE;
  329. }
  330. if(graph_path.empty())
  331. {
  332. logstream(LOG_ERROR) << "No adjacency file provided." << std::endl;
  333. return EXIT_FAILURE;
  334. }
  335. if (opts.work_megapix < 0 || opts.work_megapix > 0)
  336. {
  337. logstream(LOG_ERROR) << "Inappropriate value for work_megapix." << std::endl;
  338. return EXIT_FAILURE;
  339. }
  340. // display settings
  341. dc.cout()
  342. << "ncpus: " << clopts.get_ncpus() << std::endl
  343. << "scheduler: " << clopts.get_scheduler_type() << std::endl
  344. << "img_dir: " << img_dir << std::endl
  345. << "graph_path: " << graph_path << std::endl
  346. << "work_megapix: " << opts.work_megapix << std::endl
  347. << "verbose: " << opts.verbose << std::endl;
  348. ///////////////////////////////////////////////////////
  349. // Graphlab Graph
  350. graph_type graph(dc, clopts);
  351. // load the graph
  352. //graph.load(img_dir, vertex_loader);
  353. vertex_loader(dc, graph, img_dir);
  354. graph.load(graph_path, edge_loader);
  355. graph.finalize();
  356. ///////////////////////////////////////////////////////
  357. // Computer features in parallel
  358. graph.transform_vertices(compute_features);
  359. ///////////////////////////////////////////////////////
  360. // Match features in parallel
  361. graph.transform_edges(match_features);
  362. ///////////////////////////////////////////////////////
  363. // Graphlab Engine
  364. engine_type engine(dc, graph, clopts);
  365. ///////////////////////////////////////////////////////
  366. // Run everything
  367. engine.signal_all();
  368. graphlab::timer timer;
  369. engine.start();
  370. const double runtime = timer.current_time();
  371. dc.cout()
  372. << "----------------------------------------------------------" << std::endl
  373. << "Final Runtime (seconds): " << runtime
  374. << std::endl
  375. << "Updates executed: " << engine.num_updates() << std::endl
  376. << "Update Rate (updates/second): "
  377. << engine.num_updates() / runtime << std::endl;
  378. // int retval = parseCmdArgs(argc, argv);
  379. // if (retval)
  380. // return retval;
  381. //
  382. // // Check if have enough images
  383. // int num_images = static_cast<int>(img_names.size());
  384. // if (num_images < 2)
  385. // {
  386. // LOGLN("Need more images");
  387. // return -1;
  388. // }
  389. //
  390. // double work_scale = 1, seam_scale = 1, compose_scale = 1;
  391. // bool is_work_scale_set = false, is_seam_scale_set = false, is_compose_scale_set = false;
  392. // LOGLN("Finding features...");
  393. }