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