geocpset.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This program is free software; you can redistribute it and/or
00012  *  modify it under the terms of the GNU General Public
00013  *  License as published by the Free Software Foundation; either
00014  *  version 2 of the License, or (at your option) any later version.
00015  *
00016  *  This software is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  *  General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public
00022  *  License along with this software; if not, write to the Free Software
00023  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  */
00026 
00027 #include <hugin_version.h>
00028 
00029 #include <fstream>
00030 #include <sstream>
00031 #include <getopt.h>
00032 #ifndef WIN32
00033 #include <unistd.h>
00034 #endif
00035 #include <panodata/Panorama.h>
00036 #include <algorithms/basic/CalculateOverlap.h>
00037 #include <algorithms/nona/ComputeImageROI.h>
00038 
00039 using namespace std;
00040 using namespace HuginBase;
00041 using namespace AppBase;
00042 
00043 // check if given point x,y is inside both images img1 and img2, 
00044 // using PTools::Tranform object to prevent creating of this transform for each point
00045 bool CheckAndAddPoint(Panorama& pano, size_t img1, size_t img2, PTools::Transform& transform1, PTools::Transform& transform2, double x, double y)
00046 {
00047     double x1, y1, x2, y2;
00048     if(!transform1.transformImgCoord(x1, y1, x, y))
00049     {
00050         return false;
00051     };
00052     if(!pano.getImage(img1).isInside(vigra::Point2D(x1,y1), true))
00053     {
00054         return false;
00055     };
00056     if(!transform2.transformImgCoord(x2, y2, x, y))
00057     {
00058         return false;
00059     };
00060     if(!pano.getImage(img2).isInside(vigra::Point2D(x2,y2), true))
00061     {
00062         return false;
00063     };
00064     pano.addCtrlPoint(ControlPoint(img1, x1, y1, img2, x2, y2));
00065     return true;
00066 };
00067 
00068 //helper class for sort
00069 bool sortByDistance(FDiff2D p1, FDiff2D p2)
00070 {
00071     return p1.squareLength()<p2.squareLength();
00072 };
00073 
00074 // add geometric control point to overlap of images img1 and img2
00075 void AddGeometricControlPoint(Panorama& pano, size_t img1, size_t img2)
00076 {
00077     PanoramaOptions opts=pano.getOptions();
00078     //reset ROI to prevent unwanted clipping in this algorithm
00079     opts.setROI(vigra::Rect2D(0, 0, opts.getWidth(), opts.getHeight()));
00080     vigra::Rect2D rect1=estimateOutputROI(pano, opts, img1);
00081     vigra::Rect2D rect2=estimateOutputROI(pano, opts, img2);
00082     //get union of both outputs
00083     rect1=rect1 & rect2;
00084     if(rect1.area()>0)
00085     {
00086         PTools::Transform transform1, transform2;
00087         transform1.createTransform(pano.getImage(img1), opts);
00088         transform2.createTransform(pano.getImage(img2), opts);
00089 
00090         FDiff2D mid=(rect1.upperLeft()+rect1.lowerRight())/2.0;
00091         //create grid of points to check
00092         vector<FDiff2D> points;
00093         for(int dx=-5; dx<=5; dx++)
00094         {
00095             for(int dy=-5; dy<=5; dy++)
00096             {
00097                 points.push_back(FDiff2D(dx*rect1.width()/10.0, dy*rect1.height()/10.0));
00098             };
00099         };
00100         //sort by distance
00101         sort(points.begin(), points.end(),sortByDistance);
00102         //now check all points in the grid
00103         for(size_t i=0; i<points.size(); i++)
00104         {
00105             if(CheckAndAddPoint(pano, img1, img2, transform1, transform2, mid.x+points[i].x, mid.y+points[i].y))
00106             {
00107                 return;
00108             };
00109         };
00110     };
00111 };
00112 
00113 // only add control points for images without control points
00114 void SetGeometricControlPointsUnconnected(Panorama& pano, const int minOverlap)
00115 {
00116     //first test: have image control points?
00117     CPVector cp=pano.getCtrlPoints();
00118     UIntSet imgsWithCp;
00119     UIntSet imgsWithoutCp;
00120     for(size_t i=0; i<pano.getNrOfImages(); i++)
00121     {
00122         bool hasControlPoints=false;
00123         for(CPVector::const_iterator it=cp.begin(); it!=cp.end(); it++)
00124         {
00125             if((it->image1Nr==i || it->image2Nr==i) && (it->mode==ControlPoint::X_Y))
00126             {
00127                 hasControlPoints=true;
00128                 break;
00129             };
00130         };
00131         if(hasControlPoints)
00132         {
00133             imgsWithCp.insert(i);
00134         }
00135         else
00136         {
00137             imgsWithoutCp.insert(i);
00138         };
00139     };
00140     // now test if images without control points have a linked position with an image with control point
00141     UIntSet imgsToTest;
00142     for(UIntSet::const_iterator img=imgsWithoutCp.begin(); img!=imgsWithoutCp.end(); img++)
00143     {
00144         const SrcPanoImage& img1=pano.getImage(*img);
00145         bool connected=false;
00146         if(img1.YawisLinked())
00147         {
00148             for(UIntSet::const_iterator img2=imgsWithCp.begin(); img2!=imgsWithCp.end(); img2++)
00149             {
00150                 if(img1.YawisLinkedWith(pano.getImage(*img2)))
00151                 {
00152                     imgsWithCp.insert(*img);
00153                     connected=true;
00154                     break;
00155                 };
00156             };
00157         };
00158         if(!connected)
00159         {
00160             imgsToTest.insert(*img);
00161         };
00162     };
00163 
00164     // have we found unconnected images?
00165     if(imgsToTest.size()==0)
00166     {
00167         cout << "No unconnected images found." << endl
00168              << "No control point added." << endl;
00169         return;
00170     };
00171     cout << endl << "Found " << imgsToTest.size() << " unconnected images." << endl;
00172 
00173     // now find overlapping images
00174     CalculateImageOverlap overlap(&pano);
00175     overlap.limitToImages(imgsToTest);
00176     overlap.calculate(10);
00177     vector<UIntSet> checkedImgPairs(pano.getNrOfImages());
00178     for(UIntSet::const_iterator img=imgsToTest.begin(); img!=imgsToTest.end(); img++)
00179     {
00180         UIntSet overlappingImgs;
00181         const SrcPanoImage& img1=pano.getImage(*img);
00182         // search overlapping images, take linked positions into account
00183         for(size_t i=0; i<pano.getNrOfImages(); i++)
00184         {
00185             if(i==*img)
00186             {
00187                 continue;
00188             };
00189             if(overlap.getOverlap(*img, i)>minOverlap/100.0f)
00190             {
00191                 //ignore overlap for linked images
00192                 bool ignoreImage=false;
00193                 const SrcPanoImage& img2=pano.getImage(i);
00194                 if(img2.YawisLinked())
00195                 {
00196                     for(UIntSet::const_iterator it=overlappingImgs.begin(); it!=overlappingImgs.end(); it++)
00197                     {
00198                         if(img2.YawisLinkedWith(pano.getImage(*it)))
00199                         {
00200                             ignoreImage=true;
00201                             break;
00202                         }
00203                     };
00204                 };
00205                 if(set_contains(checkedImgPairs[*img], i) || set_contains(checkedImgPairs[i], *img))
00206                 {
00207                     ignoreImage=true;
00208                 };
00209                 if(!ignoreImage)
00210                 {
00211                     overlappingImgs.insert(i);
00212                     checkedImgPairs[*img].insert(i);
00213                     checkedImgPairs[i].insert(*img);
00214                 };
00215             };
00216         };
00217         // now add control points
00218         for(UIntSet::const_iterator overlapImg=overlappingImgs.begin(); overlapImg!=overlappingImgs.end(); overlapImg++)
00219         {
00220             AddGeometricControlPoint(pano, *img, *overlapImg);
00221         };
00222     };
00223     cout << "Added " << pano.getCtrlPoints().size() - cp.size() << " control points." << endl;
00224 };
00225 
00226 // only add control points for images without control points
00227 void SetGeometricControlPointsOverlap(Panorama& pano, const int minOverlap)
00228 {
00229     CPVector cp=pano.getCtrlPoints();
00230     // find overlapping images
00231     CalculateImageOverlap overlap(&pano);
00232     overlap.calculate(10);
00233     for(size_t i=0; i<pano.getNrOfImages()-1; i++)
00234     {
00235         UIntSet overlappingImgs;
00236         const SrcPanoImage& img1=pano.getImage(i);
00237         // search overlapping images, take linked positions into account
00238         for(size_t j=i+1; j<pano.getNrOfImages(); j++)
00239         {
00240             //skip linked images
00241             if(img1.YawisLinked())
00242             {
00243                 if(img1.YawisLinkedWith(pano.getImage(j)))
00244                 {
00245                     continue;
00246                 };
00247             };
00248             if(overlap.getOverlap(i, j)>=minOverlap/100.0f)
00249             {
00250                 // we have an overlap, now check if there are control points
00251                 bool hasControlPoints=false;
00252                 for(CPVector::const_iterator it=cp.begin(); it!=cp.end(); it++)
00253                 {
00254                     if(((it->image1Nr==i && it->image2Nr==j) ||
00255                         (it->image1Nr==j && it->image2Nr==i) ) &&
00256                        (it->mode==ControlPoint::X_Y))
00257                     {
00258                         hasControlPoints=true;
00259                         break;
00260                     };
00261                 };
00262                 if(!hasControlPoints)
00263                 {
00264                     //ignore overlap for linked images
00265                     bool ignoreImage=false;
00266                     const SrcPanoImage& img2=pano.getImage(j);
00267                     if(img2.YawisLinked())
00268                     {
00269                         for(UIntSet::const_iterator it=overlappingImgs.begin(); it!=overlappingImgs.end(); it++)
00270                         {
00271                             if(img2.YawisLinkedWith(pano.getImage(*it)))
00272                             {
00273                                 ignoreImage=true;
00274                                 break;
00275                             };
00276                         };
00277                     };
00278                     if(!ignoreImage)
00279                     {
00280                         overlappingImgs.insert(j);
00281                     };
00282                 };
00283             };
00284         };
00285         // now add control points
00286         for(UIntSet::const_iterator overlapImg=overlappingImgs.begin(); overlapImg!=overlappingImgs.end(); overlapImg++)
00287         {
00288             AddGeometricControlPoint(pano, i, *overlapImg);
00289         };
00290     };
00291     cout << endl << "Added " << pano.getCtrlPoints().size() - cp.size() << " control points." << endl;
00292 };
00293 
00294 static void usage(const char* name)
00295 {
00296     cout << name << ": set geometric control points" << endl
00297          << name << " version " << DISPLAY_VERSION << endl
00298          << endl
00299          << "Usage:  " << name << " [options] input.pto" << endl
00300          << endl
00301          << "  Options:" << endl
00302          << "     -o, --output=file.pto  Output Hugin PTO file. Default: <filename>_geo.pto" << endl
00303          << "     -e, --each-overlap     By default, geocpset will only work on the overlap" << endl
00304          << "                            of unconnected images. With this switch it will" << endl
00305          << "                            work on all overlaps without control points." << endl
00306          << "     --minimum-overlap=NUM  Take only these images into account where the" << endl
00307          << "                            overlap is bigger than NUM percent (default 10)." << endl
00308          << "     -h, --help             Shows this help" << endl
00309          << endl;
00310 }
00311 
00312 int main(int argc, char* argv[])
00313 {
00314     // parse arguments
00315     const char* optstring = "o:eh";
00316     enum
00317     {
00318         MINOVERLAP=1000
00319     };
00320 
00321     static struct option longOptions[] =
00322     {
00323         {"output", required_argument, NULL, 'o' },
00324         {"each-overlap", no_argument, NULL, 'e' },
00325         {"min-overlap", required_argument, NULL, MINOVERLAP},
00326         {"help", no_argument, NULL, 'h' },
00327         0
00328     };
00329 
00330     int c;
00331     int optionIndex = 0;
00332     bool eachOverlap=false;
00333     int minOverlap=10;
00334     string output;
00335     while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
00336     {
00337         switch (c)
00338         {
00339             case 'o':
00340                 output = optarg;
00341                 break;
00342             case 'e':
00343                 eachOverlap = true;
00344                 break;
00345             case MINOVERLAP:
00346                 minOverlap=atoi(optarg);
00347                 if(minOverlap<1 || minOverlap>99)
00348                 {
00349                     cerr << "Invalid minimum overlap: " << optarg << endl
00350                          << "Minimum overlap have to be between 1 and 99." << endl;
00351                     return 1;
00352                 };
00353                 break;
00354             case 'h':
00355                 usage(argv[0]);
00356                 return 0;
00357             case ':':
00358                 cerr <<"Option " << longOptions[optionIndex].name << " requires a parameter" << endl;
00359                 return 1;
00360                 break;
00361             case '?':
00362                 break;
00363             default:
00364                 abort ();
00365         }
00366     }
00367 
00368     if (argc - optind == 0)
00369     {
00370         cout << "Error: No project file given." << endl << endl;
00371         return 1;
00372     };
00373     if (argc - optind != 1)
00374     {
00375         cout << "Error: geocpset can only work on one project file at one time" << endl << endl;
00376         return 1;
00377     };
00378 
00379     string input=argv[optind];
00380     // read panorama
00381     Panorama pano;
00382     ifstream prjfile(input.c_str());
00383     if (!prjfile.good())
00384     {
00385         cerr << "could not open script : " << input << endl;
00386         return 1;
00387     }
00388     pano.setFilePrefix(hugin_utils::getPathPrefix(input));
00389     DocumentData::ReadWriteError err = pano.readData(prjfile);
00390     if (err != DocumentData::SUCCESSFUL)
00391     {
00392         cerr << "error while parsing panos tool script: " << input << endl;
00393         cerr << "DocumentData::ReadWriteError code: " << err << endl;
00394         return 1;
00395     }
00396 
00397     if(pano.getNrOfImages()==0)
00398     {
00399         cerr << "error: project file does not contains any image" << endl;
00400         cerr << "aborting processing" << endl;
00401         return 1;
00402     };
00403     if(pano.getNrOfImages()==1)
00404     {
00405         cerr << "error: project file contains only one image" << endl;
00406         cerr << "aborting processing" << endl;
00407         return 1;
00408     };
00409 
00410     cout << "Adding geometric control points..." << endl;
00411     if(eachOverlap)
00412     {
00413         SetGeometricControlPointsOverlap(pano, minOverlap);
00414     }
00415     else
00416     {
00417         SetGeometricControlPointsUnconnected(pano, minOverlap);
00418     };
00419 
00420     //write output
00421     UIntSet imgs;
00422     fill_set(imgs, 0, pano.getNrOfImages()-1);
00423     // Set output .pto filename if not given
00424     if (output=="")
00425     {
00426         output=input.substr(0,input.length()-4).append("_geo.pto");
00427     }
00428     ofstream of(output.c_str());
00429     pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(input));
00430 
00431     cout << endl << "Written output to " << output << endl;
00432     return 0;
00433 }

Generated on Tue Sep 2 01:25:43 2014 for Hugintrunk by  doxygen 1.3.9.1