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

Generated on 3 Sep 2015 for Hugintrunk by  doxygen 1.4.7