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 #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
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;
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
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;
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 }