video_stitcher.cpp
Go to the documentation of this file.
00001 /*
00002  * detector.cpp
00003  *
00004  *  Created on: Nov 17, 2010
00005  *      Author: erublee
00006  */
00007 #include <boost/foreach.hpp>
00008 #include <boost/program_options.hpp>
00009 
00010 #include <opencv2/opencv.hpp>
00011 #include <opencv2/legacy/legacy.hpp>
00012 
00013 #include <iostream>
00014 #include <fstream>
00015 #include <list>
00016 #include <string>
00017 
00018 #include <pano_core/pano_core.h>
00019 #include <ros/ros.h>
00020 
00021 #define LOGI ROS_INFO
00022 #define LOGI_STREAM(x) ROS_INFO_STREAM(x)
00023 
00024 #define LOGE ROS_ERROR
00025 #define LOGE_STREAM(x) ROS_ERROR_STREAM(x)
00026 
00027 using namespace pano;
00028 using namespace std;
00029 using namespace cv;
00030 
00031 #define foreach         BOOST_FOREACH
00032 #define reverse_foreach BOOST_REVERSE_FOREACH
00033 
00034 namespace po = boost::program_options;
00035 
00036 namespace
00037 {
00038 struct Options
00039 {
00040   std::string k_file;
00041   std::string d_file;
00042   std::string directory;
00043   std::string stitch_output;
00044   std::vector<std::string> image_names;
00045 };
00046 list<string> getImageList(istream& input)
00047 {
00048   list<string> imlist;
00049   while (!input.eof() && input.good())
00050   {
00051     string imname;
00052     input >> imname;
00053     if (!input.eof() && input.good())
00054       imlist.push_back(imname);
00055   }
00056   return imlist;
00057 }
00058 }
00059 
00060 int options(int ac, char ** av, Options& opts)
00061 {
00062     // Declare the supported options.
00063   po::options_description desc("Allowed options");
00064   desc.add_options()("help", "Produce help message.")
00065   ("intrinsics,K", po::value<string>(&opts.k_file),"The camera intrinsics file, should be yaml and have atleast \"K:...\". Required.")
00066   ("distortion,D", po::value<string>(&opts.d_file)->default_value(""),"The camera distortion file, should be yaml and have atleast \"D:...\". optional, no distortion assumed if not given.")
00067   ("directory,d",po::value<string>(&opts.directory)->default_value("./"),"The directory that the images to stitch are in")
00068   ("stitch_output,o",po::value<string>(&opts.stitch_output)->default_value("stitched.jpg"),"the full path to the final stitch image, should have image extension. (.jpg,.png)")
00069   ("images", po::value< vector<string> >(&opts.image_names), "input images.");
00070   po::positional_options_description p;
00071   p.add("images", -1);
00072   po::variables_map vm;
00073   po::store(po::command_line_parser(ac, av). options(desc).positional(p).run(), vm);
00074   po::notify(vm);
00075 
00076   if (vm.count("help"))
00077   {
00078     cout << desc << "\n";
00079     return 1;
00080   }
00081 
00082   if (!vm.count("intrinsics"))
00083   {
00084     cout << "Must supply a camera calibration file" << "\n";
00085     cout << desc << endl;
00086     return 1;
00087   }
00088 
00089 
00090   if (!vm.count("images"))
00091   {
00092     cout << "Must supply a list of input images" << "\n";
00093     cout << desc << endl;
00094     return 1;
00095   }
00096   return 0;
00097 
00098 }
00099 struct CanceledException : public std::exception
00100 {
00101   const char * what() const throw ()
00102   {
00103     return "Canceled!";
00104   }
00105 };
00106 class StitchProgressCallable
00107 {
00108 public:
00109   virtual ~StitchProgressCallable()
00110   {
00111   }
00112   virtual int onProgress(int progress){
00113     LOGI_STREAM("Stitch Progress:"<< progress);
00114     return 0;
00115   }
00116 };
00117 
00118 bool solve(const std::string& directory, SVDRSolverParams params, Camera camera, std::vector<std::string> image_names,
00119            StitchProgressCallable* callback = NULL)
00120 {
00121   try
00122   {
00123     Ptr<cv::AdjusterAdapter> adapter;
00124     Ptr<FeatureDetector> detector;
00125 
00126     int levels = 2;
00127     detector = new GriddedDynamicDetectorAdaptor(250, 20, levels, levels, FastAdjuster());
00128 
00129     Ptr<SVDFitter> svdfitter(new SVDFitter(params));
00130 
00131     Ptr<ModelFitter> fitter(reinterpret_cast<const Ptr<ModelFitter>&> (svdfitter));
00132 
00133     MoleculeGlob glob; //the glob stores the pano graph, think of it as the map
00134 
00135     vector<ImageAtom> atoms;
00136     vector<Mat> descriptors;
00137     Images images;
00138 
00139     int i = 0;
00140     int t_s = image_names.size();
00141     Mat img;
00142     camera.initUndistort();
00143     while (image_names.size())
00144     {
00145       string imagename = image_names.back();
00146       string imagename_full = directory + "/" + imagename;
00147       LOGI_STREAM("reading " << imagename_full );
00148 
00149       Extrinsics ext(Mat::eye(3, 3, CV_32F), 200);
00150 
00151       camera.undistort(imread(directory + "/" + imagename), img);
00152       images.load(img, imagename, directory);
00153 
00154       atoms.push_back(ImageAtom(camera, images));
00155 
00156       ImageAtom& atom = atoms.back();
00157       atom.setUid(i++);
00158       atom.extrinsics() = ext;
00159       atom.extrinsics().flag(Extrinsics::ESTIMATED) = false;
00160       atom.extrinsics().val(Extrinsics::CONFIDENCE) = 200;
00161       atom.detect(*detector);
00162       atom.extract(BriefDescriptorExtractor(), BruteForceMatcher<Hamming> ());
00163       //  descriptors.push_back(atom.features().descriptors());
00164       LOGI_STREAM( "found " << atom.features().kpts().size());
00165       glob.addAtomToGlob(fitter, atom)->images().clear();
00166 
00167       LOGI_STREAM( "detected and extracted " << i << "/" << t_s );
00168       if (callback)
00169       {
00170         callback->onProgress(int((float(i) / t_s) * 100));
00171       }
00172       image_names.pop_back();
00173     }
00174 
00175     glob.batchFindAndSetTrinsics();
00176     FileStorage fs(directory + "/glob.yaml.gz", FileStorage::WRITE);
00177     fs << "glob";
00178     glob.serialize(fs);
00179   }
00180   catch (CanceledException e)
00181   {
00182     LOGI("Canceled");
00183     return true;
00184   }
00185   catch (...)
00186   {
00187     LOGE("Fail stitch!");
00188     return false;
00189   }
00190   return true;
00191 
00192 }
00193 struct PairFitterCallback
00194 {
00195   int * i;
00196   int total;
00197   int offset;
00198   StitchProgressCallable* callback_;
00199   PairFitterCallback(StitchProgressCallable* callback, int* i, int total, int offset) :
00200     i(i), total(total), offset(offset), callback_(callback)
00201   {
00202   }
00203   template<typename T>
00204     void operator()(const T&)
00205     {
00206       if (callback_)
00207       {
00208         (*i)++;
00209         int progress = (float(*i) / total) * 100 + offset;
00210         callback_->onProgress(progress);
00211       }
00212     }
00213 };
00214 bool stitch(const std::string& pano_directory_name, const std::string& stitch_name, StitchProgressCallable* callback = NULL)
00215 {
00216   try
00217   {
00218     CallbackEngine cbengine;
00219     string directory = pano_directory_name;
00220     FileStorage fs(directory + "/glob.yaml.gz", FileStorage::READ);
00221 
00222     if (!fs.isOpened())
00223     {
00224       LOGE_STREAM("failed to open : "+ directory + "/glob.yaml.gz");
00225       return false;
00226     }
00227 
00228     MoleculeGlob glob;
00229     glob.deserialize(fs["glob"]);
00230     glob.overideDirectory(directory);
00231 
00232     if (glob.getMolecules().size() < 1)
00233       return false;
00234     LOGI_STREAM("found - " << glob.getMolecules().size() << " molecules");
00235 
00236     Mat blended(Size(4000, 2000), CV_8UC3);
00237 
00238     Ptr<ImageMolecule> molecule;
00239 
00240     molecule = glob.getBiggestMolecule();
00241 
00242     int atom_idx = 0;
00243     cbengine.addCallback<ImageAtom> (0, PairFitterCallback(callback, &atom_idx, molecule->getAtoms().size(), 100));
00244 
00245     LOGI("simple stitching now");
00246     BlenderSimple blender;
00247     blender.cbe = &cbengine;
00248     blender.BlendMolecule(*molecule, blended);
00249 
00250     cbengine.addCallback<int> (0, PairFitterCallback(callback, &atom_idx, 100, 200));
00251     cbengine.callBack(0, 0);
00252     vector<int> write_params(2);
00253     write_params[0] = CV_IMWRITE_JPEG_QUALITY;
00254     write_params[1] = 85; //need low size for emailing
00255 
00256 
00257     imwrite(stitch_name, blended, write_params);
00258   }
00259   catch (CanceledException e)
00260   {
00261     LOGI("Canceled");
00262   }
00263   return true;
00264 }
00265 
00266 int main(int argc, char *argv[])
00267 {
00268   Options opts;
00269   if (options(argc, argv, opts))
00270     return 1;
00271 
00272   Camera camera;
00273   FileStorage fs(opts.k_file, FileStorage::READ);
00274   Mat K;
00275   fs ["K"] >> K;
00276   Mat D;
00277   if(opts.d_file.size()){
00278     FileStorage dfs(opts.d_file, FileStorage::READ);
00279     dfs["D"] >> D;
00280   }
00281   Size sz;
00282   {
00283     Mat image = imread(opts.directory + "/" + opts.image_names[0]);
00284     sz = image.size();
00285   }
00286   camera.setCameraIntrinsics(K,D,sz);
00287 
00288   SVDRSolverParams params;
00289   params.error_thresh = 6;
00290   params.inliers_thresh = 15;
00291   params.maxiters = 100;
00292   params.nNeeded = 2;
00293 
00294   StitchProgressCallable pc;
00295   solve(opts.directory,params,camera,opts.image_names,&pc);
00296   stitch(opts.directory,opts.stitch_output,&pc);
00297 
00298   return 0;
00299 }


pano_core
Author(s): Ethan Rublee
autogenerated on Wed Aug 26 2015 16:34:01