Panorama.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00026 #include "Panorama.h"
00027 
00028 #include "PTScriptParsing.h"
00029 #include "ImageVariableTranslate.h"
00030 #include "StandardImageVariableGroups.h"
00031 #include <panotools/PanoToolsInterface.h>
00032 #include <algorithms/basic/CalculateOverlap.h>
00033 #include <algorithms/basic/LayerStacks.h>
00034 #include <panodata/OptimizerSwitches.h>
00035 
00036 #include <fstream>
00037 #include <typeinfo>
00038 
00039 namespace HuginBase {
00040 
00041 Panorama::Panorama() : dirty(false), m_forceImagesUpdate(false)
00042 {
00043     // init map with ptoptimizer variables.
00044     m_ptoptimizerVarNames.insert("a");
00045     m_ptoptimizerVarNames.insert("b");
00046     m_ptoptimizerVarNames.insert("c");
00047     m_ptoptimizerVarNames.insert("d");
00048     m_ptoptimizerVarNames.insert("e");
00049     m_ptoptimizerVarNames.insert("g");
00050     m_ptoptimizerVarNames.insert("t");
00051     m_ptoptimizerVarNames.insert("v");
00052     m_ptoptimizerVarNames.insert("r");
00053     m_ptoptimizerVarNames.insert("p");
00054     m_ptoptimizerVarNames.insert("y");
00055     m_ptoptimizerVarNames.insert("TrX");
00056     m_ptoptimizerVarNames.insert("TrY");
00057     m_ptoptimizerVarNames.insert("TrZ");
00058     m_ptoptimizerVarNames.insert("Tpy");
00059     m_ptoptimizerVarNames.insert("Tpp");
00060 }
00061 
00062 Panorama::~Panorama()
00063 {
00064     DEBUG_TRACE("dtor");
00065     reset();
00066     DEBUG_TRACE("dtor about to finish");
00067 }
00068 
00069 void Panorama::reset()
00070 {
00071     // delete all images and control points.
00072     state.ctrlPoints.clear();
00073     state.deleteAllImages();
00074     state.options.reset();
00075     state.optvec.clear();
00076     state.optSwitch=0;
00077     state.optPhotoSwitch=0;
00078     state.needsOptimization = false;
00079     AppBase::DocumentData::setDirty(false);
00080     dirty=false;
00081 }
00082 
00083 std::vector<unsigned int> Panorama::getCtrlPointsForImage(unsigned int imgNr) const
00084 {
00085     std::vector<unsigned int> result;
00086     unsigned int i = 0;
00087     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
00088         if ((it->image1Nr == imgNr) || (it->image2Nr == imgNr)) {
00089             result.push_back(i);
00090         }
00091         i++;
00092     }
00093     return result;
00094 }
00095 
00096 CPointVector Panorama::getCtrlPointsVectorForImage(unsigned int imgNr) const
00097 {
00098     CPointVector result;
00099     for(unsigned int i=0;i<state.ctrlPoints.size();i++)
00100     {
00101         ControlPoint point=state.ctrlPoints[i];
00102         if(point.image1Nr==imgNr)
00103         {
00104             result.push_back(std::make_pair(i,point));
00105         }
00106         else
00107         {
00108             if(point.image2Nr==imgNr)
00109             {
00110                 point.mirror();
00111                 result.push_back(std::make_pair(i,point));
00112             };
00113         };
00114     };
00115     return result;
00116 };
00117 
00118 VariableMapVector Panorama::getVariables() const
00119 {
00120     VariableMapVector map;
00121     for (size_t i = 0; i < state.images.size(); i++)
00122     {
00123         map.push_back(state.images[i]->getVariableMap());
00124     }
00125     return map;
00126 }
00127 
00128 const VariableMap Panorama::getImageVariables(unsigned int imgNr) const
00129 {
00130     assert(imgNr < state.images.size());
00131     return state.images[imgNr]->getVariableMap();
00132 }
00133 
00134 
00135 void Panorama::updateCtrlPointErrors(const UIntSet & imgs, const CPVector & cps)
00136 {
00137     unsigned sc = 0;
00138     unsigned ic = 0;
00139     std::map<unsigned int, unsigned int> script2CPMap;
00140     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
00141         if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
00142             script2CPMap[sc] = ic;
00143             sc++;
00144         }
00145         ic++;
00146     }
00147 
00148     // need to have same number of control points!
00149     assert(cps.size() == script2CPMap.size());
00150     unsigned i=0;
00151     for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it) {
00152         imageChanged(script2CPMap[it->image1Nr]);
00153         imageChanged(script2CPMap[it->image2Nr]);
00154         state.ctrlPoints[script2CPMap[i]].error = it->error;
00155         i++;
00156     }
00157 }
00158 
00159 
00160 void Panorama::updateCtrlPointErrors(const CPVector & cps)
00161 {
00162     assert(cps.size() == state.ctrlPoints.size());
00163     unsigned int nrp = cps.size();
00164     for (unsigned int i = 0; i < nrp ; i++) {
00165         imageChanged(state.ctrlPoints[i].image1Nr);
00166         imageChanged(state.ctrlPoints[i].image2Nr);
00167         state.ctrlPoints[i].error = cps[i].error;
00168     }
00169 }
00170 
00171 void Panorama::updateVariables(const VariableMapVector & vars)
00172 {
00173     assert(vars.size() == state.images.size());
00174     unsigned int i = 0;
00175     for (VariableMapVector::const_iterator it = vars.begin(); it != vars.end(); ++it) {
00176         updateVariables(i, *it);
00177         i++;
00178     }
00179 }
00180 
00181 void Panorama::updateVariables(const UIntSet & imgs, const VariableMapVector & vars)
00182 {
00183     VariableMapVector::const_iterator v_it = vars.begin();
00184     for (UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); ++it) {
00185         assert(*it < state.images.size());
00186         updateVariables(*it, *v_it);
00187         ++v_it;
00188     }
00189 }
00190 
00191 void Panorama::updateVariables(unsigned int imgNr, const VariableMap & var)
00192 {
00193     if (imgNr > state.images.size())
00194         return;
00195     for (VariableMap::const_iterator it = var.begin(); it != var.end() ; ++it) {
00196         updateVariable(imgNr,it->second);
00197     }
00198 }
00199 
00200 void Panorama::updateVariable(unsigned int imgNr, const Variable &var)
00201 {
00202     if (imgNr > state.images.size())
00203         return;
00204     // update a single variable, and everything linked to it.
00205     state.images[imgNr]->setVar(var.getName(), var.getValue());
00206     // call imageChanged for the images affected by this.
00207 #define image_variable( name, type, default_value ) \
00208     if (PTOVariableConverterFor##name::checkApplicability(var.getName())) \
00209         {\
00210             for (std::size_t i = 0; i < getNrOfImages(); i++)\
00211             {\
00212                 if (state.images[imgNr]->name##isLinkedWith(*state.images[i]))\
00213                 {\
00214                     imageChanged(i);\
00215                 }\
00216             }\
00217         }\
00218     else 
00219 #include "image_variables.h"
00220 #undef image_variable
00221     {// this is for the final else.
00222         DEBUG_ERROR("Unknown variable " << var.getName());
00223     }
00224     state.needsOptimization = true;
00225 }
00226 
00227 void Panorama::UpdateFocalLength(UIntSet imgs, double newFocalLength)
00228 {
00229     for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
00230     {
00231         state.images[*it]->updateFocalLength(newFocalLength);
00232         imageChanged(*it);
00233     };
00234     //search for images with linked HFOV and mark these also as changed
00235     for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
00236     {
00237         SrcPanoImage *img=state.images[*it];
00238         if(state.images[*it]->HFOVisLinked())
00239         {
00240             for(unsigned int j=0;j<getNrOfImages();j++)
00241             {
00242                 if(*it!=j)
00243                 {
00244                     if(state.images[*it]->HFOVisLinkedWith(*img))
00245                     {
00246                         imageChanged(j);
00247                     };
00248                 };
00249             };
00250         };
00251     };
00252 };
00253 
00254 void Panorama::UpdateCropFactor(UIntSet imgs, double newCropFactor)
00255 {
00256     //store list of focal length, so we can keep the focal length unchanged and change hfov instead
00257     std::vector<double> focalLengthVector(getNrOfImages(),0.0);
00258     for(unsigned i=0;i<getNrOfImages();i++)
00259     {
00260         focalLengthVector[i]=state.images[i]->calcFocalLength(state.images[i]->getProjection(),
00261             state.images[i]->getHFOV(),state.images[i]->getCropFactor(),state.images[i]->getSize());
00262     };
00263     for(UIntSet::const_iterator it=imgs.begin();it!=imgs.end();++it)
00264     {
00265         state.images[*it]->updateCropFactor(focalLengthVector[*it],newCropFactor);
00266         imageChanged(*it);
00267     };
00268 };
00269 
00270 // What counts as changed in terms of the image variable links?
00271 // Should I call imageChanged on every image linked to sourceImg and destImg?
00272 // destImg's variable will change value to sourceImg's, so the images linked to
00273 // destImg should be linked.
00275 #define image_variable( name, type, default_value )\
00276 void Panorama::linkImageVariable##name(unsigned int sourceImgNr, unsigned int destImgNr)\
00277 {\
00278     state.images[destImgNr]->link##name(state.images[sourceImgNr]);\
00279     imageChanged(destImgNr);\
00280     imageChanged(sourceImgNr);\
00281     state.needsOptimization = true;\
00282 }
00283 #include "image_variables.h"
00284 #undef image_variable
00285 
00287 #define image_variable( name, type, default_value )\
00288 void Panorama::unlinkImageVariable##name(unsigned int imgNr)\
00289 {\
00290     state.images[imgNr]->unlink##name();\
00291     imageChanged(imgNr);\
00292     state.needsOptimization = true;\
00293 }
00294 #include "image_variables.h"
00295 #undef image_variable
00296 
00297 void Panorama::setOptimizeVector(const OptimizeVector & optvec)
00298 {
00299     DEBUG_ASSERT(optvec.size() == state.images.size());
00300     state.optvec = optvec;
00301 }
00302 
00303 void Panorama::setOptimizerSwitch(const int newSwitch)
00304 {
00305     if(state.optSwitch!=newSwitch)
00306     {
00307         state.optSwitch=newSwitch;
00308     };
00309 };
00310 
00311 void Panorama::setPhotometricOptimizerSwitch(const int newSwitch)
00312 {
00313     if(state.optPhotoSwitch!=newSwitch)
00314     {
00315         state.optPhotoSwitch=newSwitch;
00316     };
00317 };
00318 
00319 unsigned int Panorama::addImage(const SrcPanoImage &img)
00320 {
00321     unsigned int nr = state.images.size();
00322     state.images.push_back(new SrcPanoImage(img));
00323     // create empty optimisation vector
00324     state.optvec.push_back(std::set<std::string>());
00325     imageChanged(nr);
00326     return nr;
00327 }
00328 
00329 void Panorama::removeImage(unsigned int imgNr)
00330 {
00331     DEBUG_DEBUG("Panorama::removeImage(" << imgNr << ")");
00332     assert(imgNr < state.images.size());
00333 
00334     // remove control points
00335     CPVector::iterator it = state.ctrlPoints.begin();
00336     while (it != state.ctrlPoints.end()) {
00337         if ((it->image1Nr == imgNr) || (it->image2Nr == imgNr)) {
00338             // remove point that refernce to imgNr
00339             it = state.ctrlPoints.erase(it);
00340         } else {
00341             // correct point references
00342             if (it->image1Nr > imgNr) it->image1Nr--;
00343             if (it->image2Nr > imgNr) it->image2Nr--;
00344             ++it;
00345         }
00346     }
00347 
00348     DEBUG_TRACE("Remove variables and image from panorama state")
00349     delete state.images[imgNr];
00350     state.images.erase(state.images.begin() + imgNr);
00351     state.optvec.erase(state.optvec.begin() + imgNr);
00352 
00353         // check if reference image has been moved
00354         if (state.options.optimizeReferenceImage >= state.images.size()) {
00355                 state.options.optimizeReferenceImage = 0;
00356         imageChanged(state.options.optimizeReferenceImage);
00357         }
00358 
00359         if (state.options.colorReferenceImage >= state.images.size()) {
00360                 state.options.colorReferenceImage = 0;
00361         imageChanged(state.options.colorReferenceImage);
00362         }
00363 
00364     // change all other (moved) images
00365     DEBUG_TRACE("flag moved images as dirty");
00366     for (unsigned int i=imgNr; i < state.images.size(); i++) {
00367         imageChanged(i);
00368     }
00369     m_forceImagesUpdate = true;
00370 }
00371 
00372 
00373 void Panorama::setImageFilename(unsigned int i, const std::string & fname)
00374 {
00375     DEBUG_ASSERT(i < state.images.size());
00376     state.images[i]->setFilename(fname);
00377     imageChanged(i);
00378     m_forceImagesUpdate = true;
00379 }
00380 
00381 unsigned int Panorama::addCtrlPoint(const ControlPoint & point )
00382 {
00383     unsigned int nr = state.ctrlPoints.size();
00384     state.ctrlPoints.push_back(point);
00385     imageChanged(point.image1Nr);
00386     imageChanged(point.image2Nr);
00387     state.needsOptimization = true;
00388     return nr;
00389 }
00390 
00391 void Panorama::removeCtrlPoint(unsigned int pNr)
00392 {
00393     DEBUG_ASSERT(pNr < state.ctrlPoints.size());
00394     ControlPoint & point = state.ctrlPoints[pNr];
00395     unsigned int i1 = point.image1Nr;
00396     unsigned int i2 = point.image2Nr;
00397     state.ctrlPoints.erase(state.ctrlPoints.begin() + pNr);
00398 
00399     // update line control points
00400     updateLineCtrlPoints();
00401     imageChanged(i1);
00402     imageChanged(i2);
00403     state.needsOptimization = true;
00404 }
00405 
00406 void Panorama::removeDuplicateCtrlPoints()
00407 {
00408     std::set<std::string> listOfCPs;
00409     std::set<unsigned int> duplicateCPs;
00410     for(unsigned int i=0; i<state.ctrlPoints.size();i++)
00411     {
00412         std::string s=state.ctrlPoints[i].getCPString();
00413         std::pair<std::set<std::string>::iterator,bool> it=listOfCPs.insert(s);
00414         if(it.second==false)
00415         {
00416             duplicateCPs.insert(i);
00417         };
00418     }
00419     //now remove duplicate control points, mark affected images as changed
00420     if(!duplicateCPs.empty())
00421     {
00422         for(std::set<unsigned int>::reverse_iterator it=duplicateCPs.rbegin();it!=duplicateCPs.rend();++it)
00423         {
00424             ControlPoint cp=state.ctrlPoints[*it];
00425             imageChanged(cp.image1Nr);
00426             imageChanged(cp.image2Nr);
00427             removeCtrlPoint(*it);
00428         };
00429     };
00430     updateLineCtrlPoints();
00431 }
00432 
00433 
00434 void Panorama::changeControlPoint(unsigned int pNr, const ControlPoint & point)
00435 {
00436     assert(pNr < state.ctrlPoints.size());
00437 
00438     // change notify for all involved images
00439     imageChanged(state.ctrlPoints[pNr].image1Nr);
00440     imageChanged(state.ctrlPoints[pNr].image2Nr);
00441     imageChanged(point.image1Nr);
00442     imageChanged(point.image2Nr);
00443     state.needsOptimization = true;
00444 
00445     state.ctrlPoints[pNr] = point;
00446     updateLineCtrlPoints();
00447 }
00448 
00449 void Panorama::setCtrlPoints(const CPVector & points)
00450 {
00451     for (CPVector::const_iterator it = state.ctrlPoints.begin();
00452          it != state.ctrlPoints.end(); ++it)
00453     {
00454         imageChanged(it->image1Nr);
00455         imageChanged(it->image2Nr);
00456     }
00457 
00458     state.ctrlPoints = points;
00459 
00460     for (CPVector::const_iterator it = state.ctrlPoints.begin();
00461          it != state.ctrlPoints.end(); ++it)
00462     {
00463         imageChanged(it->image1Nr);
00464         imageChanged(it->image2Nr);
00465     }
00466     state.needsOptimization = true;
00467     updateLineCtrlPoints();
00468 }
00469 
00470 // close holes in line control points
00471 void Panorama::updateLineCtrlPoints()
00472 {
00473     // sort all line control points
00474     std::map<int, int> lines;
00475     for (CPVector::const_iterator it = state.ctrlPoints.begin();
00476          it != state.ctrlPoints.end(); ++it)
00477     {
00478         if (it->mode > 2)
00479             lines[it->mode] = 0;
00480     }
00481     int i=3;
00482     for (std::map<int,int >::iterator it = lines.begin(); it != lines.end(); ++it) 
00483     {
00484         (*it).second = i;
00485         i++;
00486     }
00487 
00488     for (CPVector::iterator it = state.ctrlPoints.begin();
00489          it != state.ctrlPoints.end(); ++it)
00490     {
00491         if (it->mode > 2) {
00492             int newmode = lines[it->mode];
00493             if (it->mode != newmode) {
00494                 it->mode = newmode;
00495                 imageChanged(it->image1Nr);
00496                 imageChanged(it->image2Nr);
00497             }
00498         }
00499     }
00500 }
00501 
00502 
00503 void Panorama::printPanoramaScript(std::ostream & o,
00504                                    const OptimizeVector & optvars,
00505                                    const PanoramaOptions & output,
00506                                    const UIntSet & imgs,
00507                                    bool forPTOptimizer,
00508                                    const std::string & stripPrefix) const
00509 {
00510 #ifdef __unix__
00511     // set numeric locale to C, for correct number output
00512     char * t = setlocale(LC_NUMERIC,NULL);
00513     char * old_locale = (char*) malloc(strlen(t)+1);
00514     strcpy(old_locale, t);
00515     setlocale(LC_NUMERIC,"C");
00516 #endif
00517 
00518     if (forPTOptimizer) {
00519         o << "# PTOptimizer script, written by hugin" << std::endl
00520           << std::endl;
00521     } else {
00522         o << "# hugin project file" << std::endl;
00523         o << "#hugin_ptoversion 2" << std::endl;
00524     }
00525     // output options..
00526 
00527     output.printScriptLine(o, forPTOptimizer);
00528 
00529     // map from script img nr -> pano image nr
00530     std::map<unsigned int, unsigned int> imageNrMap;
00531     o << std::endl
00532       << "# image lines" << std::endl;
00533     
00534     // somewhere to store the v lines, which give the variables to be optimised.
00535     std::stringstream vlines;
00536     std::stringstream masklines;
00537     unsigned int ic = 0;
00538     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
00539          ++imgNrIt)
00540     {
00541         unsigned int imgNr = *imgNrIt;
00542         imageNrMap[imgNr] = ic;
00543         const SrcPanoImage & img = *state.images[imgNr];
00544         VariableMap vars;
00545 
00546         // print special comment line with hugin GUI data
00547         o << "#-hugin ";
00548         if (img.getCropMode() != BaseSrcPanoImage::NO_CROP) {
00549             if (img.getAutoCenterCrop())
00550                 o << " autoCenterCrop=1";
00551         }
00552         o << " cropFactor=" << img.getCropFactor() ;
00553         if (! img.getActive()) {
00554             o << " disabled";
00555         }
00556         o << std::endl;
00557 
00558         o << "i w" << img.getSize().width() << " h" << img.getSize().height()
00559           <<" f" << img.getProjection() << " ";
00560 
00561         // print variables with links
00562 /* Individually do all the variables specified by each SrcPanoImg variable.
00563  * Clear the list after each SrcPanoImg variable.
00564  * If there is any links to previous images, write that in the script.
00565  * If there are no links to previous images, write the value instead.
00566  * Each variable in SrcPanoImg may produce any number of variables in the map,
00567  *      but the linking properties are shared.
00568  * Additionally, when we are writing variables by value which are set to be
00569  * optimised, we should remember them so we can write them later as 'v' lines.
00570  */
00571 #define image_variable( name, type, default_value )\
00572         PTOVariableConverterFor##name::addToVariableMap(state.images[imgNr]->get##name##IV(), vars);\
00573         if (!vars.empty())\
00574         {\
00575             bool linking = false;\
00576             std::size_t link_target;\
00577             if (ic!=0)\
00578             {\
00579                 if (state.images[imgNr]->name##isLinked())\
00580                 {\
00581                     for (link_target = 0; link_target < imgNr; link_target++)\
00582                     {\
00583                         if (set_contains(imgs,link_target) && state.images[imgNr]->name##isLinkedWith(*state.images[link_target]))\
00584                         {\
00585                             linking = true;\
00586                             break;\
00587                         }\
00588                     }\
00589                 }\
00590             }\
00591             for (VariableMap::const_iterator vit = vars.begin();\
00592              vit != vars.end(); ++vit)\
00593             {\
00594                 if (forPTOptimizer && !set_contains(m_ptoptimizerVarNames,vit->first))\
00595                     continue;\
00596                 else if (linking)\
00597                 {\
00598                     o << vit->first << "=" << imageNrMap[link_target] << " ";\
00599                 } else {\
00600                     if (set_contains(optvars[imgNr], vit->first))\
00601                     {\
00602                         vlines << "v " << vit->first << imageNrMap[imgNr] << std::endl;\
00603                     }\
00604                     if (((vit->first == "a" && set_contains(optvars[imgNr], "a") )|| \
00605                                 (vit->first == "b" && set_contains(optvars[imgNr], "b") )|| \
00606                                 (vit->first == "c" && set_contains(optvars[imgNr], "c") )|| \
00607                                 (vit->first == "TrX" && set_contains(optvars[imgNr], "TrX") )|| \
00608                                 (vit->first == "TrY" && set_contains(optvars[imgNr], "TrY") )|| \
00609                                 (vit->first == "TrZ" && set_contains(optvars[imgNr], "TrZ") )|| \
00610                                 (vit->first == "Tpy" && set_contains(optvars[imgNr], "Tpy") )|| \
00611                                 (vit->first == "Tpp" && set_contains(optvars[imgNr], "Tpp") )\
00612                                )\
00613                                && forPTOptimizer && vit->second.getValue() == 0.0) \
00614                     {\
00615                         o << vit->first << 1e-5 << " ";\
00616                     } else if (( (vit->first == "r" && set_contains(optvars[imgNr], "r") ) || \
00617                                  (vit->first == "p" && set_contains(optvars[imgNr], "p") ) || \
00618                                  (vit->first == "y" && set_contains(optvars[imgNr], "y") ) \
00619                                )\
00620                                && forPTOptimizer && fabs(vit->second.getValue()) < 1e-13)\
00621                     {\
00622                         o << vit->first << 0 << " ";\
00623                     } else {\
00624                         vit->second.print(o) << " ";\
00625                     }\
00626                 }\
00627             }\
00628         }\
00629         vars.clear();
00630 #include "image_variables.h"
00631 #undef image_variable
00632 
00633         if (img.getCropMode()!=SrcPanoImage::NO_CROP) {
00634             // print crop parameters
00635             vigra::Rect2D c = img.getCropRect();
00636             o << " S" << c.left() << "," << c.right() << "," << c.top() << "," << c.bottom();
00637         }
00638 
00639         if (!forPTOptimizer) {
00640 
00641             if (img.getVigCorrMode() != SrcPanoImage::VIGCORR_NONE) {
00642                 o << " Vm" << img.getVigCorrMode();
00643             }
00644 
00645             if (img.getFlatfieldFilename().size() > 0) {
00646                 o << " Vf\"" << img.getFlatfieldFilename() << "\"";
00647             }
00648             if (img.getResponseType() > 0) {
00649                 o << " Rt" << img.getResponseType();
00650             }
00651 
00652             if(img.hasMasks())
00653                 img.printMaskLines(masklines,ic);
00654         }
00655 
00656 #if 0
00657 //panotools paramters, currently not used
00658         o << " u" << output.featherWidth
00659           << (img.getMorph() ? " o" : "");
00660 #endif
00661         std::string fname = img.getFilename();
00662         if (stripPrefix.size() > 0) {
00663             // strip prefix from image names.
00664             // check if the prefix is acutally the same
00665             std::string tmp = fname.substr(0,stripPrefix.size());
00666             if (tmp.compare(stripPrefix) == 0) {
00667                 DEBUG_DEBUG("striping " << stripPrefix << " from " << fname);
00668                 fname = fname.erase(0,stripPrefix.size());
00669                 DEBUG_DEBUG("after stripping: " <<  fname);
00670             } else {
00671                 DEBUG_DEBUG(stripPrefix << " does not match " << fname);
00672             }
00673         }
00674         o << " n\"" << fname << "\"" << std::endl;
00675         ic++;
00676     }
00677 
00678     o << std::endl << std::endl
00679       << "# specify variables that should be optimized" << std::endl
00680       << vlines.str()
00681       << "v" << std::endl; // empty v line to work around libpano13 bug.
00682     
00683     o << std::endl << std::endl
00684       << "# control points" << std::endl;
00685     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
00686                 if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
00687                 o << "c n" << imageNrMap[it->image1Nr]
00688                       << " N" << imageNrMap[it->image2Nr]
00689                           << " x" << it->x1 << " y" << it->y1
00690                   << " X" << it->x2 << " Y" << it->y2
00691               << " t" << it->mode << std::endl;
00692         }
00693     }
00694     o << std::endl;
00695 
00696     if(masklines.str().length()>0)
00697         o << "# masks" << std::endl
00698             << masklines.str()
00699             << std::endl;
00700 
00701     // special line with hugins options.
00702     o << "#hugin_optimizeReferenceImage " << output.optimizeReferenceImage << std::endl;
00703     o << "#hugin_blender ";
00704     switch (output.blendMode) {
00705         case PanoramaOptions::NO_BLEND:
00706             o << "none" << endl;
00707             break;
00708         case PanoramaOptions::PTBLENDER_BLEND:
00709             o << "PTblender" << endl;
00710             break;
00711         case PanoramaOptions::SMARTBLEND_BLEND:
00712             o << "smartblend" << endl;
00713             break;
00714         case PanoramaOptions::PTMASKER_BLEND:
00715             o << "PTmasker" << endl;
00716             break;
00717         case PanoramaOptions::INTERNAL_BLEND:
00718             o << "internal" << endl;
00719             break;
00720         default:
00721         case PanoramaOptions::ENBLEND_BLEND:
00722             o << "enblend" << endl;
00723             break;
00724     }
00725     
00726     o << "#hugin_remapper ";
00727     switch (output.remapper) {
00728         case PanoramaOptions::PTMENDER:
00729             o << "PTmender" << endl;
00730             break;
00731         default:
00732         case PanoramaOptions::NONA:
00733             o << "nona" << endl;
00734             break;
00735     }
00736     
00737     o << "#hugin_enblendOptions " << output.enblendOptions << endl;
00738     o << "#hugin_enfuseOptions " << output.enfuseOptions << endl;
00739     o << "#hugin_hdrmergeOptions " << output.hdrmergeOptions << endl;
00740 
00741     o << "#hugin_outputLDRBlended " << (output.outputLDRBlended ? "true" : "false") << endl;
00742     o << "#hugin_outputLDRLayers " << (output.outputLDRLayers ? "true" : "false") << endl;
00743     o << "#hugin_outputLDRExposureRemapped " << (output.outputLDRExposureRemapped ? "true" : "false") << endl;
00744     o << "#hugin_outputLDRExposureLayers " << (output.outputLDRExposureLayers ? "true" : "false") << endl;
00745     o << "#hugin_outputLDRExposureBlended " << (output.outputLDRExposureBlended ? "true" : "false") << endl;
00746     o << "#hugin_outputLDRStacks " << (output.outputLDRStacks ? "true" : "false") << endl;
00747     o << "#hugin_outputLDRExposureLayersFused " << (output.outputLDRExposureLayersFused ? "true" : "false") << endl;
00748     o << "#hugin_outputHDRBlended " << (output.outputHDRBlended ? "true" : "false") << endl;
00749     o << "#hugin_outputHDRLayers " << (output.outputHDRLayers ? "true" : "false") << endl;
00750     o << "#hugin_outputHDRStacks " << (output.outputHDRStacks ? "true" : "false") << endl;    
00751 
00752     o << "#hugin_outputLayersCompression " << output.outputLayersCompression << endl;
00753     o << "#hugin_outputImageType " << output.outputImageType << endl;
00754     o << "#hugin_outputImageTypeCompression " << output.outputImageTypeCompression << endl;
00755     o << "#hugin_outputJPEGQuality " << output.quality << endl;
00756     o << "#hugin_outputImageTypeHDR " << output.outputImageTypeHDR << endl;
00757     o << "#hugin_outputImageTypeHDRCompression " << output.outputImageTypeHDRCompression << endl;
00758 
00759     o << "#hugin_outputStacksMinOverlap " << std::setprecision(3) << output.outputStacksMinOverlap << endl;
00760     o << "#hugin_outputLayersExposureDiff " << std::setprecision(2) << output.outputLayersExposureDiff << endl;
00761 
00762     if(optvars==getOptimizeVector())
00763     {
00764         o << "#hugin_optimizerMasterSwitch " << getOptimizerSwitch() << endl;
00765         o << "#hugin_optimizerPhotoMasterSwitch " << getPhotometricOptimizerSwitch() << endl;
00766     }
00767     else
00768     {
00769         o << "#hugin_optimizerMasterSwitch 0" << endl;
00770         o << "#hugin_optimizerPhotoMasterSwitch 0" << endl;
00771     };
00772 
00773 #ifdef __unix__
00774     // reset locale
00775     setlocale(LC_NUMERIC,old_locale);
00776     free(old_locale);
00777 #endif
00778 }
00779 
00780 
00781 void Panorama::printStitcherScript(std::ostream & o,
00782                                    const PanoramaOptions & target,
00783                                    const UIntSet & imgs) const
00784 {
00785 #ifdef __unix__
00786     // set numeric locale to C, for correct number output
00787     char * t = setlocale(LC_NUMERIC,NULL);
00788     char * old_locale = (char*) malloc(strlen(t)+1);
00789     strcpy(old_locale, t);
00790     setlocale(LC_NUMERIC,"C");
00791 #endif
00792 
00793     o << "# PTStitcher script, written by hugin" << std::endl
00794       << std::endl;
00795     // output options..
00796     target.printScriptLine(o, true);
00797     o << std::endl
00798       << "# output image lines" << std::endl;
00799     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end(); ++imgNrIt) {
00800         unsigned int imgNr = *imgNrIt;
00801         const SrcPanoImage & img = *state.images[imgNr];
00802 // DGSW FIXME - Unreferenced
00803 //              const Lens & lens = state.lenses[lensNr];
00804         const VariableMap & vars = state.images[imgNr]->getVariableMap();
00805 
00806         o << "o w" << img.getSize().width() << " h" << img.getSize().height()
00807           <<" f" << img.getProjection() << " ";
00808         // print variables, without links
00809         VariableMap::const_iterator vit;
00810         for(vit = vars.begin(); vit != vars.end();  ++vit)
00811         {
00812             if (!set_contains(m_ptoptimizerVarNames,vit->first)) {
00813                 continue;
00814             }
00815             vit->second.print(o) << " ";
00816         }
00817 #if 0
00818 // panotools parameters, currently not used 
00819         o << " u" << img.getFeatureWidth() 
00820           << (img.getMorph() ? " o" : "");
00821 #endif
00822         o << " n\"" << img.getFilename() << "\"";
00823         if (img.getCropMode()!=SrcPanoImage::NO_CROP) {
00824             // print crop parameters
00825             vigra::Rect2D c = img.getCropRect();
00826             o << " S" << c.left() << "," << c.right() << "," << c.top() << "," << c.bottom();
00827         }
00828         o << std::endl;
00829     }
00830     o << std::endl;
00831 #ifdef __unix__
00832     // reset locale
00833     setlocale(LC_NUMERIC,old_locale);
00834     free(old_locale);
00835 #endif
00836 
00837 }
00838 
00839 void Panorama::parseOptimizerScript(std::istream & i, const UIntSet & imgs,
00840                                     VariableMapVector & imgVars, CPVector & CPs) const
00841 {
00842     DEBUG_TRACE("");
00843 #ifdef __unix__
00844     // set numeric locale to C, for correct number output
00845     char * t = setlocale(LC_NUMERIC,NULL);
00846     char * old_locale = (char*) malloc(strlen(t)+1);
00847     strcpy(old_locale, t);
00848     setlocale(LC_NUMERIC,"C");
00849 #endif
00850 
00851     unsigned int ic=0;
00852     std::map<unsigned int, unsigned int> script2ImgMap;
00853     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
00854          ++imgNrIt)
00855     {
00856         unsigned int imgNr = *imgNrIt;
00857         script2ImgMap[ic] = imgNr;
00858         ic++;
00859     }
00860     ic = 0;
00861     unsigned int sc = 0;
00862     std::map<unsigned int, unsigned int> script2CPMap;
00863     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
00864         if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
00865             script2CPMap[sc] = ic;
00866             sc++;
00867         }
00868         ic++;
00869     }
00870 
00871 
00872 
00873     // 0 = read output (image lines), 1 = read control point distances
00874     int state = 0;
00875     std::string line;
00876     unsigned int lineNr = 0;
00877     unsigned int scriptImgCounter = 0;
00878     unsigned int scriptCPCounter = 0;
00879 //    VariableMapVector::iterator varIt = imgVars.begin();
00880 //    CPVector::iterator pointIt = CPs.begin();
00881 
00882 // DGSW FIXME - Unreferenced
00883 //      int pnr=0;
00884 
00885     while (!i.eof()) {
00886         std::getline(i, line);
00887         lineNr++;
00888         switch (state) {
00889         case 0:
00890         {
00891             // we are reading the output lines:
00892             // o f3 r0 p0 y0 v89.2582 a-0.027803 b0.059851 c-0.073115 d10.542470 e16.121145 u10 -buf
00893             if ((line.compare("# Control Points: Distance between desired and fitted Position") == 0 )
00894              || (line.compare("# Control Points: Distance between desired and fitted Position (in Pixels)") == 0 )
00895              || (line.compare("# Control Points: Distance between desired and fitted Position (in \"Pixels\")") == 0 )) {
00896                 
00897                 // switch to reading the control point distance
00898                 if (scriptImgCounter != imgs.size()) {
00899                     DEBUG_ERROR("Read only " << scriptImgCounter << " images from PTOptimizer file");
00900                 }
00901                 DEBUG_DEBUG("Changing state to read control point distances");
00902                 state = 1;
00903                 break;
00904             }
00905             if (line[0] != 'o') continue;
00906             // select variables of the image
00907             VariableMap & var = imgVars[script2ImgMap[scriptImgCounter]];
00908             DEBUG_DEBUG("reading image variables for image:" << scriptImgCounter);
00909             // read position variables
00910             int link;
00911             PTScriptParsing::readVar(map_get(var, "r"), link, line);
00912             DEBUG_ASSERT(link == -1);
00913             PTScriptParsing::readVar(map_get(var, "p"), link, line);
00914             DEBUG_ASSERT(link == -1);
00915             PTScriptParsing::readVar(map_get(var, "y"), link, line);
00916             DEBUG_ASSERT(link == -1);
00917 
00918             DEBUG_DEBUG("yaw: " << map_get(var, "y").getValue()
00919                         << " pitch " << map_get(var, "p").getValue()
00920                         << " roll " << map_get(var, "r").getValue());
00921             // read lens variables
00922 
00923             PTScriptParsing::readVar(map_get(var, "TrX"), link, line);
00924             DEBUG_ASSERT(link == -1);
00925             PTScriptParsing::readVar(map_get(var, "TrY"), link, line);
00926             DEBUG_ASSERT(link == -1);
00927             PTScriptParsing::readVar(map_get(var, "TrZ"), link, line);
00928             DEBUG_ASSERT(link == -1);
00929             PTScriptParsing::readVar(map_get(var, "Tpy"), link, line);
00930             DEBUG_ASSERT(link == -1);
00931             PTScriptParsing::readVar(map_get(var, "Tpp"), link, line);
00932             DEBUG_ASSERT(link == -1);
00933 
00934             DEBUG_DEBUG("X: " << map_get(var, "TrX").getValue()
00935                         << " Y " << map_get(var, "TrY").getValue()
00936                         << " Z " << map_get(var, "TrZ").getValue());
00937             // read lens variables
00938 
00939 
00940             for (const char **c = Lens::variableNames; *c != 0; ++c) {
00941                 Variable & curVar = map_get(var, *c);
00942                 if (!PTScriptParsing::readVar(curVar, link, line)) {
00943                     DEBUG_ERROR("Could not read "<< *c << " at script line " << lineNr);
00944                 }
00945                 // linking in output forbidden
00946                 DEBUG_ASSERT(link == -1);
00947             }
00948             scriptImgCounter++;
00949             break;
00950         }
00951         case 1:
00952         {
00953             // read ctrl point distances:
00954             // # Control Point No 0:  0.428994
00955             if (line[0] == 'C') {
00956 //                DEBUG_DEBUG(CPs.size() << " points, read: " << pnr);
00957                 state = 2;
00958                 break;
00959             }
00960             if (line.find("# Control Point No") != 0) continue;
00961             DEBUG_DEBUG("reading cp dist line: " << line);
00962             std::string::size_type p;
00963             if ((p=line.find(':')) == std::string::npos) assert(0);
00964             p++;
00965             DEBUG_DEBUG("parsing point " << scriptCPCounter << " (idx:" << p << "): " << line.substr(p));
00966             double err = -1;
00967 
00968             hugin_utils::stringToDouble(line.substr(p), err);
00969             CPs[script2CPMap[scriptCPCounter]].error = err;
00970             DEBUG_DEBUG("read CP distance " << err);
00971             scriptCPCounter++;
00972             break;
00973         }
00974         default:
00975             // ignore line..
00976             break;
00977         }
00978     }
00979 #ifdef __unix__
00980     // reset locale
00981     setlocale(LC_NUMERIC,old_locale);
00982     free(old_locale);
00983 #endif
00984 
00985 }
00986 
00987 void Panorama::changeFinished(bool keepDirty)
00988 {
00989     if (state.images.size() == 0) {
00990         // force an empty update if all images have been
00991         // removed
00992         DEBUG_DEBUG("forcing images update, with no images");
00993         m_forceImagesUpdate = true;
00994     }
00995     // remove change notification for nonexisting images from set.
00996     UIntSet::iterator uB = changedImages.lower_bound(state.images.size());
00997     changedImages.erase(uB,changedImages.end());
00998 
00999     std::stringstream t;
01000     copy(changedImages.begin(), changedImages.end(),
01001          std::ostream_iterator<unsigned int>(t, " "));
01002     DEBUG_TRACE("changed image(s) " << t.str() << " begin");
01003     //force update of crops
01004     if(changedImages.size()>0)
01005     {
01006         for(UIntSet::iterator it=changedImages.begin();it!=changedImages.end();++it)
01007         {
01008             //if the projection was changed, we need to update the crop mode
01009             updateCropMode(*it);
01010             //now center the crop if user requested it
01011             if(state.images[*it]->getAutoCenterCrop())
01012             {
01013                 centerCrop(*it);
01014             };
01015         };
01016     };
01017     //update masks
01018     updateMasks();
01019     updateOptimizeVector();
01020     std::list<PanoramaObserver *>::iterator it;
01021     for(it = observers.begin(); it != observers.end(); ++it) {
01022         DEBUG_TRACE("notifying listener");
01023         if (changedImages.size() > 0 || m_forceImagesUpdate) {
01024             (*it)->panoramaImagesChanged(*this, changedImages);
01025         }
01026         (*it)->panoramaChanged(*this);
01027     }
01028     // reset changed images
01029     changedImages.clear();
01030     m_forceImagesUpdate = false;
01031     if (!keepDirty) {
01032         dirty = true;
01033         AppBase::DocumentData::setDirty(dirty);
01034     }
01035     DEBUG_TRACE("end");
01036 }
01037 
01038 void Panorama::updateMasksForImage(unsigned int imgNr, MaskPolygonVector newMasks)
01039 {
01040     DEBUG_ASSERT(imgNr < state.images.size());
01041     state.images[imgNr]->setMasks(newMasks);
01042     imageChanged(imgNr);
01043     m_forceImagesUpdate = true;
01044 };
01045 
01046 void Panorama::transferMask(MaskPolygon mask,unsigned int imgNr, const UIntSet& targetImgs)
01047 {
01048     if(targetImgs.size()==0)
01049     {
01050         return;
01051     };
01052     MaskPolygon transformedMask=mask;
01053     // clip positive mask to image boundaries or clip region
01054     vigra::Rect2D clipRect=vigra::Rect2D(0,0,state.images[imgNr]->getWidth(),state.images[imgNr]->getHeight());
01055     if(mask.isPositive())
01056     {
01057         //clip to crop region only positive masks
01058         switch(state.images[imgNr]->getCropMode())
01059         {
01060             case BaseSrcPanoImage::CROP_RECTANGLE:
01061                 clipRect=clipRect & state.images[imgNr]->getCropRect();
01062                 if(clipRect.isEmpty())
01063                 {
01064                     return;
01065                 };
01066                 if(clipRect.width()>10 && clipRect.height()>10)
01067                 {
01068                     clipRect.addBorder(-2);
01069                 };
01070                 break;
01071             case BaseSrcPanoImage::CROP_CIRCLE:
01072                 {
01073                     vigra::Rect2D cropRect=state.images[imgNr]->getCropRect();
01074                     hugin_utils::FDiff2D center((cropRect.left()+cropRect.right())/2.0,(cropRect.top()+cropRect.bottom())/2.0);
01075                     double radius=((cropRect.width()<cropRect.height())?cropRect.width():cropRect.height())/2.0;
01076                     if(radius>10)
01077                     {
01078                         radius-=2;
01079                     };
01080                     if(!transformedMask.clipPolygon(center,radius))
01081                     {
01082                         return;
01083                     };
01084                 };
01085                 break;
01086             default:
01087                 if(clipRect.width()>10 && clipRect.height()>10)
01088                 {
01089                     clipRect.addBorder(-2);
01090                 };
01091                 break;
01092         };
01093     };
01094     int origWindingNumber=transformedMask.getTotalWindingNumber();
01095     if(transformedMask.clipPolygon(clipRect))
01096     {
01097         //increase resolution of positive mask to get better transformation
01098         //of vertices, especially for fisheye images
01099         transformedMask.subSample(20);
01100         //transform polygon to panorama space
01101         HuginBase::PTools::Transform trans;
01102         trans.createInvTransform(getImage(imgNr),getOptions());
01103         transformedMask.transformPolygon(trans);
01104         for(UIntSet::const_iterator it=targetImgs.begin();it!=targetImgs.end();++it)
01105         {
01106             if(imgNr==(*it))
01107             {
01108                 continue;
01109             };
01110             MaskPolygon targetMask;
01111             if(state.images[imgNr]->YawisLinkedWith(*(state.images[*it])))
01112             {
01113                 //if yaw is linked, we simply copy the mask
01114                 targetMask=mask;
01115             }
01116             else
01117             {
01118                 targetMask=transformedMask;
01119                 PTools::Transform targetTrans;
01120                 targetTrans.createTransform(getImage(*it),getOptions());
01121                 targetMask.transformPolygon(targetTrans);
01122                 //check if transformation has produced invalid polygon
01123                 if(targetMask.getMaskPolygon().size()<3)
01124                 {
01125                     continue;
01126                 };
01127                 //check if mask was inverted - outside became inside and vice versa
01128                 //if so, invert mask
01129                 int newWindingNumber=targetMask.getTotalWindingNumber();
01130                 targetMask.setInverted(origWindingNumber * newWindingNumber < 0);
01131             };
01132             //now clip polygon to image rectangle, add mask only when polygon is inside image
01133             if(targetMask.clipPolygon(vigra::Rect2D(-maskOffset,-maskOffset,
01134                                       state.images[*it]->getWidth()+maskOffset,state.images[*it]->getHeight()+maskOffset)))
01135             {
01136                 targetMask.setMaskType(MaskPolygon::Mask_negative);
01137                 targetMask.setImgNr(*it);
01138                 state.images[*it]->addActiveMask(targetMask);
01139             };
01140         };
01141     };        
01142 };
01143 
01144 void Panorama::updateMasks(bool convertPosMaskToNeg)
01145 {
01146     // update masks
01147     UIntSet imgWithPosMasks;
01148     for(unsigned int i=0;i<state.images.size();i++)
01149     {
01150         state.images[i]->clearActiveMasks();
01151         if(state.images[i]->hasPositiveMasks())
01152         {
01153             imgWithPosMasks.insert(i);
01154         };
01155     };
01156     CalculateImageOverlap overlap(this);
01157     overlap.limitToImages(imgWithPosMasks);
01158     overlap.calculate(10);
01159     ConstStandardImageVariableGroups variable_groups(*this);
01160     ConstImageVariableGroup & lenses = variable_groups.getLenses();
01161     for(unsigned int i=0;i<state.images.size();i++)
01162     {
01163         if(state.images[i]->hasMasks())
01164         {
01165             MaskPolygonVector masks=state.images[i]->getMasks();
01166             for(unsigned int j=0;j<masks.size();j++)
01167             {
01168                 if(convertPosMaskToNeg)
01169                 {
01170                     //this is used for masking in the cp finder, we are consider
01171                     //all masks as negative masks, because at this moment
01172                     //the final position of the images is not known
01173                     switch(masks[j].getMaskType())
01174                     {
01175                         case MaskPolygon::Mask_negative:
01176                         case MaskPolygon::Mask_positive:
01177                             masks[j].setImgNr(i);
01178                             masks[j].setMaskType(MaskPolygon::Mask_negative);
01179                             state.images[i]->addActiveMask(masks[j]);
01180                             break;
01181                         case MaskPolygon::Mask_Stack_negative:
01182                         case MaskPolygon::Mask_Stack_positive:
01183                             {
01184                                 //copy mask to all images of the same stack
01185                                 UIntSet imgStack;
01186                                 for(unsigned int k=0;k<getNrOfImages();k++)
01187                                 {
01188                                     if(i!=k)
01189                                     {
01190                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
01191                                         {
01192                                             imgStack.insert(k);
01193                                         };
01194                                     };
01195                                 };
01196                                 masks[j].setImgNr(i);
01197                                 masks[j].setMaskType(MaskPolygon::Mask_negative);
01198                                 state.images[i]->addActiveMask(masks[j]);
01199                                 transferMask(masks[j],i,imgStack);
01200                             };
01201                             break;
01202                         case MaskPolygon::Mask_negative_lens:
01203                             {
01204                                 unsigned int lensNr=lenses.getPartNumber(i);
01205                                 //copy masks to all image of the same lens
01206                                 for(unsigned int k=0;k<getNrOfImages();k++)
01207                                 {
01208                                     if(lenses.getPartNumber(k)==lensNr)
01209                                     {
01210                                         masks[j].setImgNr(k);
01211                                         masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
01212                                         state.images[k]->addActiveMask(masks[j]);
01213                                     };
01214                                 };
01215                             };
01216                             break;
01217                     };
01218                 }
01219                 else
01220                 {
01221                     switch(masks[j].getMaskType())
01222                     {
01223                         case MaskPolygon::Mask_negative:
01224                             //negative mask, simply copy mask to active mask
01225                             masks[j].setImgNr(i);
01226                             state.images[i]->addActiveMask(masks[j]);
01227                             break;
01228                         case MaskPolygon::Mask_positive:
01229                             //propagate positive mask only if image is active
01230                             if(state.images[i]->getActive())
01231                             {
01232                                 UIntSet overlapImgs=overlap.getOverlapForImage(i);
01233                                 transferMask(masks[j],i,overlapImgs);
01234                             };
01235                             break;
01236                         case MaskPolygon::Mask_Stack_negative:
01237                             {
01238                                 //search all images of the stack
01239                                 UIntSet imgStack;
01240                                 for(unsigned int k=0;k<getNrOfImages();k++)
01241                                 {
01242                                     if(i!=k)
01243                                     {
01244                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
01245                                         {
01246                                             imgStack.insert(k);
01247                                         };
01248                                     };
01249                                 };
01250                                 //copy mask also to the image which contains the mask
01251                                 masks[j].setImgNr(i);
01252                                 masks[j].setMaskType(MaskPolygon::Mask_negative);
01253                                 state.images[i]->addActiveMask(masks[j]);
01254                                 transferMask(masks[j],i,imgStack);
01255                             };
01256                             break;
01257                         case MaskPolygon::Mask_Stack_positive:
01258                             {
01259                                 //remove all images from the stack from the set
01260                                 UIntSet imgStack;
01261                                 fill_set(imgStack,0,getNrOfImages()-1);
01262                                 imgStack.erase(i);
01263                                 for(unsigned int k=0;k<getNrOfImages();k++)
01264                                 {
01265                                     if(i!=k)
01266                                     {
01267                                         if(state.images[i]->StackisLinkedWith(*(state.images[k])))
01268                                         {
01269                                             imgStack.erase(k);
01270                                         };
01271                                     };
01272                                 };
01273                                 //only leave overlapping images in set
01274                                 UIntSet imgOverlap=overlap.getOverlapForImage(i);
01275                                 UIntSet imgs;
01276                                 std::set_intersection(imgStack.begin(),imgStack.end(),imgOverlap.begin(),imgOverlap.end(),inserter(imgs,imgs.begin()));
01277                                 //now transfer mask
01278                                 transferMask(masks[j],i,imgs);
01279                             };
01280                             break;
01281                         case MaskPolygon::Mask_negative_lens:
01282                             {
01283                                 unsigned int lensNr=lenses.getPartNumber(i);
01284                                 //copy masks to all image of the same lens
01285                                 for(unsigned int k=0;k<getNrOfImages();k++)
01286                                 {
01287                                     if(lenses.getPartNumber(k)==lensNr)
01288                                     {
01289                                         masks[j].setImgNr(k);
01290                                         masks[j].setMaskType(MaskPolygon::Mask_negative_lens);
01291                                         state.images[k]->addActiveMask(masks[j]);
01292                                     };
01293                                 };
01294                             };
01295                             break;
01296                     };
01297                 };
01298             };
01299         };
01300     };
01301 };
01302 
01303 void Panorama::updateCropMode(unsigned int imgNr)
01304 {
01305     vigra::Rect2D r=state.images[imgNr]->getCropRect();
01306     if(r.isEmpty() || r==vigra::Rect2D(state.images[imgNr]->getSize()))
01307     {
01308         state.images[imgNr]->setCropMode(SrcPanoImage::NO_CROP);
01309     }
01310     else
01311     {
01312         if (state.images[imgNr]->isCircularCrop())
01313         {
01314             state.images[imgNr]->setCropMode(SrcPanoImage::CROP_CIRCLE);
01315         }
01316         else
01317         {
01318             state.images[imgNr]->setCropMode(SrcPanoImage::CROP_RECTANGLE);
01319         };
01320     };
01321 };
01322 
01323 vigra::Rect2D Panorama::centerCropImage(unsigned int imgNr)
01324 {
01325     vigra::Rect2D cropRect;
01326     if(state.images[imgNr]->getCropMode()==SrcPanoImage::NO_CROP)
01327     {
01328         return cropRect;
01329     };
01330     int dx = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().x);
01331     int dy = hugin_utils::roundi(state.images[imgNr]->getRadialDistortionCenterShift().y);
01332     vigra::Point2D center = vigra::Point2D(state.images[imgNr]->getSize().width()/2 + dx, state.images[imgNr]->getSize().height()/2 + dy);
01333 
01334     vigra::Diff2D d(state.images[imgNr]->getCropRect().width() / 2, state.images[imgNr]->getCropRect().height() / 2);
01335     cropRect.setUpperLeft( center - d);
01336     cropRect.setLowerRight( center + d);
01337     return cropRect;
01338 };
01339 
01340 void Panorama::centerCrop(unsigned int imgNr)
01341 {
01342     vigra::Rect2D cropRect;
01343     if(  state.images[imgNr]->getCropMode()!=SrcPanoImage::NO_CROP &&
01344          state.images[imgNr]->getAutoCenterCrop() && 
01345        !(state.images[imgNr]->getCropRect().isEmpty())
01346        )
01347     {
01348         cropRect=centerCropImage(imgNr);
01349         if(!cropRect.isEmpty())
01350         {
01351             state.images[imgNr]->setCropRect(cropRect);
01352             imageChanged(imgNr);
01353         };
01354     };
01355     for (std::size_t i = 0; i < getNrOfImages(); i++)
01356     {
01357         if(i==imgNr)
01358         {
01359             continue;
01360         };
01361         if (state.images[imgNr]->RadialDistortionCenterShiftisLinkedWith(*state.images[i]))
01362         {
01363             if(  state.images[i]->getCropMode()!=SrcPanoImage::NO_CROP &&
01364                  state.images[i]->getAutoCenterCrop() && 
01365                !(state.images[i]->getCropRect().isEmpty())
01366                )
01367             {
01368                 cropRect=centerCropImage(i);
01369                 if(!cropRect.isEmpty())
01370                 {
01371                     state.images[i]->setCropRect(cropRect);
01372                     imageChanged(i);
01373                 };
01374             };
01375         };
01376     };
01377 };
01378 
01379 void UpdateOptVectorSet(std::set<std::string>& imgVar, const std::string& var, const bool opt)
01380 {
01381     if(opt)
01382     {
01383         imgVar.insert(var);
01384     }
01385     else
01386     {
01387         imgVar.erase(var);
01388     };
01389 };
01390 
01391 std::set<size_t> Panorama::getRefImages()
01392 {
01393     unsigned int refImg = getOptions().optimizeReferenceImage;
01394     std::set<size_t> refImgs;
01395     refImgs.insert(refImg);
01396     const HuginBase::SrcPanoImage & refImage = getImage(refImg);
01397     for (size_t imgNr = 0; imgNr < getNrOfImages(); imgNr++)
01398     {
01399         if(imgNr!=refImg)
01400         {
01401             const HuginBase::SrcPanoImage & compImage = getImage(imgNr);
01402             if (refImage.YawisLinkedWith(compImage))
01403             {
01404                 refImgs.insert(imgNr);
01405             };
01406         };
01407     };
01408     return refImgs;
01409 };
01410 
01411 void Panorama::checkRefOptStatus(bool& linkRefImgsYaw, bool& linkRefImgsPitch, bool& linkRefImgsRoll)
01412 {
01413     // count number of vertical/horizontal control points
01414     int nHCP = 0;
01415     int nVCP = 0;
01416     const CPVector & cps = getCtrlPoints();
01417     for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
01418     {
01419         // control points
01420         if (it->mode == ControlPoint::X)
01421         {
01422             nVCP++;
01423         }
01424         else
01425         {
01426             if (it->mode == ControlPoint::Y)
01427             {
01428                 nHCP++;
01429             }
01430         };
01431     };
01432 
01433     // try to select sensible position optimisation parameters,
01434     // dependent on output projection
01435     linkRefImgsYaw=false;
01436     linkRefImgsPitch=false;
01437     linkRefImgsRoll=false;
01438     switch (getOptions().getProjection())
01439     {
01440         case PanoramaOptions::RECTILINEAR:
01441             linkRefImgsRoll = nVCP + nHCP >= 1;
01442             linkRefImgsYaw = nVCP + nHCP >= 3 && nVCP >= 1 && nHCP >= 1;
01443             linkRefImgsPitch = nVCP + nHCP >= 2;
01444             break;
01445         case PanoramaOptions::CYLINDRICAL:
01446         case PanoramaOptions::EQUIRECTANGULAR:
01447             linkRefImgsPitch =  nHCP + nVCP > 1;
01448             linkRefImgsRoll = nHCP + nVCP >= 1;
01449             break;
01450         default:
01451             break;
01452     };
01453 };
01454 
01455 void Panorama::updateOptimizeVector()
01456 {
01457     if(state.images.size()==0)
01458     {
01459         return;
01460     };
01461     if(state.optSwitch!=0)
01462     {
01463         std::set<size_t> refImgs=getRefImages();
01464         bool linkRefImgsYaw=false;
01465         bool linkRefImgsPitch=false;
01466         bool linkRefImgsRoll=false;
01467         checkRefOptStatus(linkRefImgsYaw, linkRefImgsPitch, linkRefImgsRoll);
01468 
01469         for(size_t i=0;i<getNrOfImages();i++)
01470         {
01471             if(state.optSwitch & OPT_PAIR || state.optSwitch & OPT_POSITION || state.optSwitch & OPT_ALL)
01472             {
01473                 if(set_contains(refImgs,i))
01474                 {
01475                     UpdateOptVectorSet(state.optvec[i],"y",linkRefImgsYaw);
01476                     UpdateOptVectorSet(state.optvec[i],"p",linkRefImgsPitch);
01477                     UpdateOptVectorSet(state.optvec[i],"r",linkRefImgsRoll);
01478                     //don't optimize translation parameters of anchor
01479                     UpdateOptVectorSet(state.optvec[i],"TrX",false);
01480                     UpdateOptVectorSet(state.optvec[i],"TrY",false);
01481                     UpdateOptVectorSet(state.optvec[i],"TrZ",false);
01482                 }
01483                 else
01484                 {
01485                     UpdateOptVectorSet(state.optvec[i],"y",true);
01486                     UpdateOptVectorSet(state.optvec[i],"p",true);
01487                     UpdateOptVectorSet(state.optvec[i],"r",true);
01488                     UpdateOptVectorSet(state.optvec[i],"TrX",(state.optSwitch & OPT_TRANSLATION)>0);
01489                     UpdateOptVectorSet(state.optvec[i],"TrY",(state.optSwitch & OPT_TRANSLATION)>0);
01490                     UpdateOptVectorSet(state.optvec[i],"TrZ",(state.optSwitch & OPT_TRANSLATION)>0);
01491                 };
01492             }
01493             else
01494             {
01495                 UpdateOptVectorSet(state.optvec[i],"y",false);
01496                 UpdateOptVectorSet(state.optvec[i],"p",false);
01497                 UpdateOptVectorSet(state.optvec[i],"r",false);
01498                 UpdateOptVectorSet(state.optvec[i],"Trx",false);
01499                 UpdateOptVectorSet(state.optvec[i],"Try",false);
01500                 UpdateOptVectorSet(state.optvec[i],"Trz",false);
01501             };
01502             UpdateOptVectorSet(state.optvec[i],"v",state.optSwitch & OPT_VIEW || state.optSwitch & OPT_ALL);
01503             UpdateOptVectorSet(state.optvec[i],"a",(state.optSwitch & OPT_ALL)>0);
01504             UpdateOptVectorSet(state.optvec[i],"b",state.optSwitch & OPT_BARREL || state.optSwitch & OPT_ALL);
01505             UpdateOptVectorSet(state.optvec[i],"c",(state.optSwitch & OPT_ALL)>0);
01506             UpdateOptVectorSet(state.optvec[i],"d",(state.optSwitch & OPT_ALL)>0);
01507             UpdateOptVectorSet(state.optvec[i],"e",(state.optSwitch & OPT_ALL)>0);
01508             //shear and translation plane not include in master switches
01509             UpdateOptVectorSet(state.optvec[i],"g",false);
01510             UpdateOptVectorSet(state.optvec[i],"t",false);
01511             UpdateOptVectorSet(state.optvec[i],"Tpy", false);
01512             UpdateOptVectorSet(state.optvec[i],"Tpp", false);
01513         };
01514     };
01515     if(state.optPhotoSwitch!=0)
01516     {
01517         for(size_t i=0;i<getNrOfImages();i++)
01518         {
01519             UpdateOptVectorSet(state.optvec[i],"Eev",state.optPhotoSwitch & OPT_EXPOSURE && i!=state.options.colorReferenceImage);
01520             UpdateOptVectorSet(state.optvec[i],"Er", state.optPhotoSwitch & OPT_WHITEBALANCE && i!=state.options.colorReferenceImage);
01521             UpdateOptVectorSet(state.optvec[i],"Eb", state.optPhotoSwitch & OPT_WHITEBALANCE && i!=state.options.colorReferenceImage);
01522             UpdateOptVectorSet(state.optvec[i],"Vb", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
01523             UpdateOptVectorSet(state.optvec[i],"Vc", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
01524             UpdateOptVectorSet(state.optvec[i],"Vd", (state.optPhotoSwitch & OPT_VIGNETTING)>0);
01525             UpdateOptVectorSet(state.optvec[i],"Vx", (state.optPhotoSwitch & OPT_VIGNETTING_CENTER)>0);
01526             UpdateOptVectorSet(state.optvec[i],"Vy", (state.optPhotoSwitch & OPT_VIGNETTING_CENTER)>0);
01527             UpdateOptVectorSet(state.optvec[i],"Ra", (state.optPhotoSwitch & OPT_RESPONSE)>0);
01528             UpdateOptVectorSet(state.optvec[i],"Rb", (state.optPhotoSwitch & OPT_RESPONSE)>0);
01529             UpdateOptVectorSet(state.optvec[i],"Rc", (state.optPhotoSwitch & OPT_RESPONSE)>0);
01530             UpdateOptVectorSet(state.optvec[i],"Rd", (state.optPhotoSwitch & OPT_RESPONSE)>0);
01531             UpdateOptVectorSet(state.optvec[i],"Re", (state.optPhotoSwitch & OPT_RESPONSE)>0);
01532         };
01533     };
01534 };
01535 
01536 void Panorama::swapImages(unsigned int img1, unsigned int img2)
01537 {
01538     DEBUG_TRACE("swapping images " << img1 << ", " << img2);
01539     DEBUG_ASSERT(img1 < state.images.size());
01540     DEBUG_ASSERT(img2 < state.images.size());
01541 
01542     // first, swap image pointers in the list.
01543     SrcPanoImage * pimg1 = state.images[img1];
01544     state.images[img1] = state.images[img2];
01545     state.images[img2] = pimg1;
01546     
01547     // update control points
01548     for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
01549         int n1 = (*it).image1Nr;
01550         int n2 = (*it).image2Nr;
01551         if ((*it).image1Nr == img1) {
01552             n1 = img2;
01553         } else if ((*it).image1Nr == img2) {
01554             n1 = img1;
01555         }
01556         if ((*it).image2Nr == img1) {
01557             n2 = img2;
01558         } else if ((*it).image2Nr == img2) {
01559             n2 = img1;
01560         }
01561         (*it).image1Nr = n1;
01562         (*it).image2Nr = n2;
01563     }
01564 
01565     // update panorama options
01566     if (state.options.colorReferenceImage == img1) {
01567         state.options.colorReferenceImage = img2;
01568     } else if (state.options.colorReferenceImage == img2) {
01569         state.options.colorReferenceImage = img1;
01570     }
01571     if (state.options.optimizeReferenceImage == img1) {
01572         state.options.optimizeReferenceImage = img2;
01573     } else if (state.options.optimizeReferenceImage == img2) {
01574         state.options.optimizeReferenceImage = img1;
01575     }
01576     imageChanged(img1);
01577     imageChanged(img2);
01578 }
01579 
01580 void Panorama::moveImage(size_t img1, size_t img2)
01581 {
01582     //generate a vector with the translated image numbers
01583     std::vector<size_t> imgList(getNrOfImages(),-1);
01584     for(size_t i=0; i<getNrOfImages(); i++)
01585     {
01586         imgList[i]=i;
01587     };
01588     imgList.erase(imgList.begin()+img1);
01589     if(img2<imgList.size())
01590     {
01591         imgList.insert(imgList.begin()+img2, img1);
01592     }
01593     else
01594     {
01595         imgList.push_back(img1);
01596     };
01597     //generate map for translation of old -> new image numbers
01598     std::map<size_t,size_t> imgMap;
01599     for(size_t i=0; i<imgList.size(); i++)
01600     {
01601         imgMap[imgList[i]]=i;
01602     };
01603     // now generate the new images list
01604     std::vector<SrcPanoImage *> new_images(getNrOfImages());
01605     for(size_t i=0; i<imgList.size(); i++)
01606     {
01607         new_images[i]=state.images[imgList[i]];
01608         if(i!=imgList[i])
01609         {
01610             imageChanged(imgList[i]);
01611         };
01612     };
01613     state.images=new_images;
01614 
01615     // update optimize vector
01616     OptimizeVector newOptVec;
01617     for(size_t i=0; i<state.optvec.size(); i++)
01618     {
01619         newOptVec.push_back(state.optvec[imgList[i]]);
01620     };
01621     state.optvec=newOptVec;
01622 
01623     // update control points
01624     for (CPVector::iterator it=state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
01625     {
01626         (*it).image1Nr = imgMap[(*it).image1Nr];
01627         (*it).image2Nr = imgMap[(*it).image2Nr];
01628     }
01629 
01630     // update panorama options
01631     state.options.colorReferenceImage=imgMap[state.options.colorReferenceImage];
01632     state.options.optimizeReferenceImage=imgMap[state.options.optimizeReferenceImage];
01633 };
01634 
01635 bool Panorama::setMementoToCopyOf(const PanoramaDataMemento* memento)
01636 {
01637     if(memento==NULL)
01638         return false;
01639     
01640     const PanoramaMemento* mymemento;
01641         
01642     try {
01643         
01644         mymemento = dynamic_cast<const PanoramaMemento*>(memento);
01645         
01646     } catch (std::bad_cast&) {
01647 //        std::cerr << "Incompatible memento type." << std::endl;
01648         DEBUG_DEBUG("Incompatible memento type.");
01649         return false;
01650     }
01651     
01652     setMemento(PanoramaMemento(*mymemento));
01653     return true;
01654 }
01655 
01656 
01658 void Panorama::setMemento(const PanoramaMemento& memento)
01659 {
01660     DEBUG_TRACE("");
01661     
01662     // remove old content.
01663     reset();
01664     DEBUG_DEBUG("nr of images in memento:" << memento.images.size());
01665     
01666     state = memento;
01667     updateMasks();
01668     unsigned int nNewImages = state.images.size();
01669     DEBUG_DEBUG("nNewImages:" << nNewImages);
01670     
01671     // send changes for all images
01672     for (unsigned int i = 0; i < nNewImages; i++) {
01673         imageChanged(i);
01674     }
01675 }
01676 
01677 PanoramaDataMemento* Panorama::getNewMemento() const
01678 {
01679     return new PanoramaMemento(getMemento());
01680 }
01681 
01682 void Panorama::setOptions(const PanoramaOptions & opt)
01683 {
01684     if (state.options.optimizeReferenceImage != opt.optimizeReferenceImage) {
01685         imageChanged(opt.optimizeReferenceImage);
01686         imageChanged(state.options.optimizeReferenceImage);
01687     }
01688 
01689     if (state.options.colorReferenceImage != opt.colorReferenceImage) {
01690         imageChanged(opt.colorReferenceImage);
01691         imageChanged(state.options.colorReferenceImage);
01692     }
01693 
01694     state.options = opt;
01695 }
01696 
01697 void Panorama::addObserver(PanoramaObserver * o)
01698 {
01699     observers.push_back(o);
01700 }
01701 
01702 bool Panorama::removeObserver(PanoramaObserver * o)
01703 {
01704     size_t oldCount=observers.size();
01705     observers.remove(o);
01706     return observers.size()!=oldCount;
01707 }
01708 
01709 void Panorama::clearObservers()
01710 {
01711     observers.clear();
01712 }
01713 
01714 const bool Panorama::hasPendingChanges() const
01715 {
01716     return !changedImages.empty();
01717 }
01718 
01719 void Panorama::imageChanged(unsigned int imgNr)
01720 {
01721 //    DEBUG_TRACE("adding image " << imgNr);
01722     changedImages.insert(imgNr);
01723     assert(changedImages.find(imgNr) != changedImages.end());
01724 }
01725 
01726 void Panorama::activateImage(unsigned int imgNr, bool active)
01727 {
01728     assert(imgNr < state.images.size());
01729     if (state.images[imgNr]->getActive() != active)
01730     {
01731         state.images[imgNr]->setActive(active);
01732         imageChanged(imgNr);
01733     }
01734 }
01735 
01736 UIntSet Panorama::getActiveImages() const
01737 {
01738         UIntSet activeImgs;
01739 
01740     for (unsigned int i = 0; i < state.images.size(); i++) {
01741         if (state.images[i]->getActive())
01742         {
01743             activeImgs.insert(i);
01744         }
01745     }
01746         return activeImgs;
01747 }
01748 
01749 const std::string Panorama::getICCProfileDesc() const
01750 {
01751     return state.iccProfileDesc;
01752 };
01753 
01754 void Panorama::setICCProfileDesc(const std::string& newDesc)
01755 {
01756     state.iccProfileDesc = newDesc;
01757 };
01758 
01759 //==== internal function for variable management
01760 
01761 SrcPanoImage Panorama::getSrcImage(unsigned imgNr) const
01762 {
01763     DEBUG_ASSERT(imgNr < state.images.size());
01764     return *state.images[imgNr];
01765 }
01766 
01767 void Panorama::setSrcImage(unsigned int imgNr, const SrcPanoImage & img)
01768 {
01769     DEBUG_ASSERT(imgNr < state.images.size());
01770     
01771     /* Copy the variables. We don't assign directly so we can do the changes to
01772      * any linked variables.
01773      */
01774     SrcPanoImage *dest = state.images[imgNr];
01775     #define image_variable( name, type, default_value ) \
01776     dest->set##name (img.get##name());
01777     #include "image_variables.h"
01778     #undef image_variable
01779     
01780     // mark the potentially changed images.
01781 #define image_variable( name, type, default_value ) \
01782     for (std::size_t i = 0; i < getNrOfImages(); i++)\
01783     {\
01784         if(state.images[imgNr]->name##isLinkedWith(*state.images[i]))\
01785         {\
01786             imageChanged(i);\
01787         }\
01788     }
01789 #include "image_variables.h"
01790 #undef image_variable
01791 }
01792 
01793 
01794 Panorama Panorama::duplicate() const
01795 {
01796     Panorama pano(*this);
01797     pano.observers.clear();
01798     return pano;
01799 }
01800 
01801 Panorama Panorama::getSubset(const UIntSet & imgs) const
01802 {
01803     Panorama subset;
01804     // copy data except for listners
01805     
01806     // bits that don't change in the subset.
01807     subset.imgFilePrefix = imgFilePrefix;
01808     subset.dirty = dirty;
01809     subset.state.options = state.options;
01810     subset.state.optSwitch=0;
01811     subset.state.optPhotoSwitch=0;
01812     subset.state.needsOptimization = state.needsOptimization;
01813     subset.changedImages = changedImages;
01814     subset.m_forceImagesUpdate = m_forceImagesUpdate;
01815     subset.m_ptoptimizerVarNames = m_ptoptimizerVarNames;
01816     
01817     // create image number map.
01818     std::map<unsigned int, unsigned int> imageNrMap;
01819 
01820     // copy image information
01821     unsigned int ic = 0;
01822     for (UIntSet::const_iterator imgNrIt = imgs.begin(); imgNrIt != imgs.end();
01823          ++imgNrIt)
01824     {
01825         subset.state.images.push_back(new SrcPanoImage(*state.images[*imgNrIt]));
01826         subset.state.optvec.push_back(state.optvec[*imgNrIt]);
01827         imageNrMap[*imgNrIt] = ic;
01828         ic++;
01829     }
01830     
01831     // recreate links between image variables.
01832     ic = 0;
01833     for (UIntSet::const_iterator i = imgs.begin(); i != imgs.end(); ++i)
01834     {
01835         unsigned int jc = ic + 1;
01836         UIntSet::const_iterator j = i;
01837         for (++j; j != imgs.end(); ++j)
01838         {
01843 #define image_variable( name, type, default_value )\
01844             if (state.images[*i]->name##isLinkedWith(*state.images[*j]))\
01845             {\
01846                 subset.state.images[ic]->link##name(subset.state.images[jc]);\
01847             }
01848 #include "image_variables.h"
01849 #undef image_variable
01850             jc++;
01851         }
01852         ic++;
01853     }
01854 
01855     // select and translate control points.
01856     subset.state.ctrlPoints.clear();
01857     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it) {
01858         if (set_contains(imgs, it->image1Nr) && set_contains(imgs, it->image2Nr)) {
01859             ControlPoint pnt = *it;
01860             pnt.image1Nr = imageNrMap[pnt.image1Nr];
01861             pnt.image2Nr = imageNrMap[pnt.image2Nr];
01862             subset.state.ctrlPoints.push_back(pnt);
01863         }
01864     }
01865 
01866     //update optimizeReferenceImage and colorReferenceImage number
01867     unsigned int newRefImg=0;
01868     std::map<unsigned int, unsigned int>::iterator it=imageNrMap.find(state.options.optimizeReferenceImage);
01869     if(it!=imageNrMap.end())
01870     {
01871         newRefImg=it->second;
01872     };
01873     it=imageNrMap.find(state.options.colorReferenceImage);
01874     subset.state.options.optimizeReferenceImage=newRefImg;
01875     newRefImg=0;
01876     if(it!=imageNrMap.end())
01877     {
01878         newRefImg=it->second;
01879     }
01880     subset.state.options.colorReferenceImage=newRefImg;
01881     return subset;
01882 }
01883 
01884 int FindStackNumberForImage(const std::vector<UIntSet>& imageGroups, const unsigned int imgNr)
01885 {
01886     for (size_t i = 0; i < imageGroups.size(); ++i)
01887     {
01888         if (set_contains(imageGroups[i], imgNr))
01889         {
01890             return i;
01891         };
01892     };
01893     return -1;
01894 };
01895 
01896 PanoramaData* Panorama::getUnlinkedSubset(UIntSetVector& imageGroups) const
01897 {
01898     const CPVector cps = getCtrlPoints();
01899     CPVector newCP;
01900 
01901     // remove all connected images, keep only a single image for each connected stack
01902     imageGroups.clear();
01903     std::vector<bool> visitedImages(getNrOfImages(), false);
01904     for (size_t i = 0; i < getNrOfImages(); ++i)
01905     {
01906         if (visitedImages[i])
01907         {
01908             continue;
01909         };
01910         const SrcPanoImage& img1 = getImage(i);
01911         UIntSet imgs;
01912         imgs.insert(i);
01913         visitedImages[i] = true;
01914         if (img1.YawisLinked())
01915         {
01916             for (size_t j = i + 1; j < getNrOfImages(); ++j)
01917             {
01918                 if (img1.YawisLinkedWith(getImage(j)))
01919                 {
01920                     imgs.insert(j);
01921                     visitedImages[j] = true;
01922                 }
01923             }
01924         };
01925         imageGroups.push_back(imgs);
01926     };
01927     UIntSet singleStackImgs;
01928     for (size_t i = 0; i < imageGroups.size(); ++i)
01929     {
01930         singleStackImgs.insert(*imageGroups[i].begin());
01931     };
01932     // new generate subpano
01933     PanoramaData* subPano = getNewSubset(singleStackImgs);
01934     // translate now reference image
01935     int newRefImage = FindStackNumberForImage(imageGroups, getOptions().optimizeReferenceImage);
01936     if (newRefImage != -1)
01937     {
01938         PanoramaOptions opts = subPano->getOptions();
01939         opts.optimizeReferenceImage = newRefImage;
01940         subPano->setOptions(opts);
01941     };
01942     // remove all vertical and horizontal cp, also remap all cps to new subpano
01943     // all cps from removed images will be mapped to first image of corresponding stack
01944     for (CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
01945     {
01946         if (it->mode == ControlPoint::X_Y)
01947         {
01948             ControlPoint cp(*it);
01949             int newImg1 = FindStackNumberForImage(imageGroups, cp.image1Nr);
01950             int newImg2 = FindStackNumberForImage(imageGroups, cp.image2Nr);
01951             if (newImg1 != -1 && newImg2 != -1 && newImg1 != newImg2)
01952             {
01953                 cp.image1Nr = newImg1;
01954                 cp.image2Nr = newImg2;
01955                 newCP.push_back(cp);
01956             };
01957         };
01958     };
01959     subPano->setCtrlPoints(newCP);
01960     return subPano;
01961 }
01962 
01963 void Panorama::mergePanorama(const Panorama &newPano)
01964 {
01965     if(newPano.getNrOfImages()>0)
01966     {
01967         std::vector<unsigned int> new_image_nr(newPano.getNrOfImages());
01968         HuginBase::OptimizeVector optVec=getOptimizeVector();
01969         HuginBase::OptimizeVector optVecNew=newPano.getOptimizeVector();
01970         //add only new images
01971         for(unsigned int i=0;i<newPano.getNrOfImages();i++)
01972         {
01973             std::string filename=newPano.getImage(i).getFilename();
01974             bool found=false;
01975             for(unsigned int j=0;j<getNrOfImages();j++)
01976             {
01977                 if(getImage(j).getFilename()==filename)
01978                 {
01979                     //image is already in panorama, we remember the image nr
01980                     found=true;
01981                     new_image_nr[i]=j;
01982                     // now check if we have to update the masks
01983                     HuginBase::MaskPolygonVector masksOld=getImage(j).getMasks();
01984                     HuginBase::MaskPolygonVector masksNew=newPano.getImage(i).getMasks();
01985                     if(masksNew.size()>0)
01986                     {
01987                         for(unsigned int k=0;k<masksNew.size();k++)
01988                         {
01989                             bool usedMasks=false;
01990                             unsigned int l=0;
01991                             while((!usedMasks) && l<masksOld.size())
01992                             {
01993                                 usedMasks=(masksNew[k]==masksOld[l]);
01994                                 l++;
01995                             };
01996                             if(!usedMasks)
01997                                 masksOld.push_back(masksNew[k]);
01998                         };
01999                         updateMasksForImage(j,masksOld);
02000                     };
02001                     break;
02002                 };
02003             };
02004             if(!found)
02005             {
02006                 //new image found, add it
02007                 new_image_nr[i]=addImage(newPano.getImage(i));
02008                 //copy also optimise vector
02009                 optVec.push_back(optVecNew[i]);
02010             };
02011         };
02012         setOptimizeVector(optVec);
02013         // recreate links between image variables.
02014         for (unsigned int i=0; i<newPano.getNrOfImages(); i++)
02015         {
02016             for(unsigned int j=i+1;j<newPano.getNrOfImages();j++)
02017             {
02018                 const HuginBase::SrcPanoImage &img=newPano.getImage(i);
02019 #define image_variable( name, type, default_value )\
02020                 if(img.name##isLinkedWith(newPano.getImage(j)))\
02021                 {\
02022                     linkImageVariable##name(new_image_nr[i],new_image_nr[j]);\
02023                 };
02024 #include "panodata/image_variables.h"
02025 #undef image_variable
02026             }
02027         }
02028         //now translate cp
02029         CPVector cps=newPano.getCtrlPoints();
02030         const int nextLineCPOffset = getNextCPTypeLineNumber() - 3;
02031         for(unsigned int i=0;i<cps.size();i++)
02032         {
02033             // special treatment of line control points
02034             if (cps[i].mode > 2)
02035             {
02036                 addCtrlPoint(HuginBase::ControlPoint(new_image_nr[cps[i].image1Nr], cps[i].x1, cps[i].y1,
02037                     new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode + nextLineCPOffset));
02038             }
02039             else
02040             {
02041                 // normal, horizontal and vertical cp keep their mode
02042                 addCtrlPoint(HuginBase::ControlPoint(new_image_nr[cps[i].image1Nr], cps[i].x1, cps[i].y1,
02043                     new_image_nr[cps[i].image2Nr], cps[i].x2, cps[i].y2, cps[i].mode));
02044             };
02045         };
02046         removeDuplicateCtrlPoints();
02047     };
02048 };
02049 
02050 int Panorama::getNextCPTypeLineNumber() const
02051 {
02052     int t=0;
02053     for (CPVector::const_iterator it = state.ctrlPoints.begin(); it != state.ctrlPoints.end(); ++it)
02054     {
02055         t = std::max(t, it->mode);
02056     }
02057     if (t <= 2) {
02058         t=2;
02059     }
02060     return t+1;
02061 }
02062 
02063 
02064 Panorama::ReadWriteError Panorama::readData(std::istream& dataInput, std::string documentType)
02065 {
02066     // [TODO] check the document type, return INCOMPATIBLE_TYPE
02067     
02068     if(!dataInput.good() || dataInput.eof())
02069     {
02070         DEBUG_WARN("Failed to read from dataInput.");
02071         return INVALID_DATA;
02072     }
02073     
02074     PanoramaMemento newPano;
02075     int ptoVersion;
02076     if (newPano.loadPTScript(dataInput, ptoVersion, getFilePrefix())) {
02077         
02078         this->setMemento(newPano);
02079         return SUCCESSFUL;
02080         
02081     } else {
02082         DEBUG_FATAL("Could not parse the data input successfully.");
02083         return PARCER_ERROR;
02084     }
02085 }
02086 
02088 Panorama::ReadWriteError Panorama::writeData(std::ostream& dataOutput, std::string documentType)
02089 {
02090     UIntSet all;
02091     
02092     if (getNrOfImages() > 0)
02093         fill_set(all, 0, getNrOfImages()-1);
02094     
02095     printPanoramaScript(dataOutput, getOptimizeVector(), getOptions(), all, false, getFilePrefix());
02096     
02097     return SUCCESSFUL;
02098 }
02099 
02100 void Panorama::updateWhiteBalance(double redFactor, double blueFactor)
02101 {
02102     UIntSet modified_images;
02103     for(unsigned int i=0;i<getNrOfImages();i++)
02104     {
02105         if(!set_contains(modified_images,i))
02106         {
02107             state.images[i]->setWhiteBalanceRed(redFactor * state.images[i]->getWhiteBalanceRed());
02108             state.images[i]->setWhiteBalanceBlue(blueFactor * state.images[i]->getWhiteBalanceBlue());
02109             modified_images.insert(i);
02110             imageChanged(i);
02111             //check linked images and remember for later
02112             if(state.images[i]->WhiteBalanceRedisLinked())
02113             {
02114                 if(i+1<getNrOfImages())
02115                 {
02116                     for(unsigned int j=i+1;j<getNrOfImages();j++)
02117                     {
02118                         if(state.images[i]->WhiteBalanceRedisLinkedWith(*(state.images[j])))
02119                         {
02120                             modified_images.insert(j);
02121                             imageChanged(j);
02122                         };
02123                     };
02124                 };
02125             };
02126         };
02127     };
02128 };
02129 
02130 const double Panorama::getMaxExposureDifference() const
02131 {
02132     if (state.images.empty())
02133     {
02134         return 0;
02135     }
02136     double minEv = 1000;
02137     double maxEv = -1000;
02138     for (size_t i = 0; i < state.images.size(); i++)
02139     {
02140         const double ev = state.images[i]->getExposureValue();
02141         minEv = std::min(minEv, ev);
02142         maxEv = std::max(maxEv, ev);
02143     };
02144     return maxEv - minEv;
02145 };
02146 
02147 const bool Panorama::hasPossibleStacks() const
02148 {
02149     if (state.images.empty())
02150     {
02151         return false;
02152     }
02153     // this algorithm is based on panostart by Bruno Postle
02154     // bracketed pano has at least a dynamic range of 1.2 ev values (corresponds to bracket with +-2/3)
02155     if (getMaxExposureDifference()<1.2)
02156     {
02157         return false;
02158     }
02159     //now get all exposure layers
02160     UIntSet allImg;
02161     fill_set(allImg, 0, state.images.size() - 1);
02162     UIntSetVector evValues = getExposureLayers(*this, allImg, 0.3);
02163     //if there is only one unique exposure value then there are no stacks
02164     if (evValues.size()<2)
02165     {
02166         return false;
02167     }
02168     //if number of unique exposure values is equal the number of images then there are no stacks
02169     if (evValues.size() == state.images.size())
02170     {
02171         return false;
02172     }
02173     //if number of images is not a multiple of number of unique exposure values
02174     //then the stacks are incomplete, skipping
02175     if (state.images.size() % evValues.size() != 0)
02176     {
02177         return false;
02178     }
02179     //check if exposure value is repeated with step size of bracket size
02180     if (set_contains(evValues[0], evValues.size()))
02181     {
02182         return true;
02183     }
02184     else
02185     {
02186         return false;
02187     };
02188 };
02189 
02191 void Panorama::linkPossibleStacks(bool linkPosition)
02192 {
02193     // we need at least 2 images
02194     if (state.images.size()<=1)
02195     {
02196         return;
02197     };
02198     // unlink all existing stacks
02199     for (size_t imgNr = 0; imgNr < state.images.size(); imgNr++)
02200     {
02201         if (state.images[imgNr]->YawisLinked())
02202         {
02203             unlinkImageVariableYaw(imgNr);
02204             unlinkImageVariablePitch(imgNr);
02205             unlinkImageVariableRoll(imgNr);
02206             unlinkImageVariableX(imgNr);
02207             unlinkImageVariableY(imgNr);
02208             unlinkImageVariableZ(imgNr);
02209             unlinkImageVariableTranslationPlaneYaw(imgNr);
02210             unlinkImageVariableTranslationPlanePitch(imgNr);
02211         };
02212         if (state.images[imgNr]->StackisLinked())
02213         {
02214             unlinkImageVariableStack(imgNr);
02215         };
02216     };
02217     // now link all possible stacks
02218     UIntSet allImg;
02219     fill_set(allImg, 0, state.images.size() - 1);
02220     UIntSetVector evValues = getExposureLayers(*this, allImg, 0.3);
02221     if (evValues.empty())
02222     {
02223         return;
02224     };
02225     unsigned int imgNr = 0;
02226     for (size_t i = 1; i<state.images.size(); i++)
02227     {
02228         if (set_contains(evValues[0], i))
02229         {
02230             imgNr = i;
02231         }
02232         else
02233         {
02234             linkImageVariableStack(imgNr, i);
02235             if (linkPosition)
02236             {
02237                 linkImageVariableYaw(imgNr, i);
02238                 linkImageVariablePitch(imgNr, i);
02239                 linkImageVariableRoll(imgNr, i);
02240                 linkImageVariableX(imgNr, i);
02241                 linkImageVariableY(imgNr, i);
02242                 linkImageVariableZ(imgNr, i);
02243                 linkImageVariableTranslationPlaneYaw(imgNr, i);
02244                 linkImageVariableTranslationPlanePitch(imgNr, i);
02245             };
02246         };
02247     };
02248 };
02249 
02250 PanoramaMemento::PanoramaMemento(const PanoramaMemento & data)
02251 {
02252     // Use the assignment operator to get the work done: see the next function.
02253     *this = data;
02254 }
02255 
02256 PanoramaMemento & PanoramaMemento::operator=(const PanoramaMemento & data)
02257 {
02258     // Copy the PanoramaMemento.
02259     
02260     // Don't do anything in the case of self assignment. This is important as we
02261     // are about to delete the image information.
02262     if (&data == this)
02263     {
02264         return *this;
02265     }
02266     
02267     // Remove any images we currently had.
02268     deleteAllImages();
02269     // copy image variables
02270     for (std::vector<SrcPanoImage *>::const_iterator it = data.images.begin();
02271          it != data.images.end(); ++it)
02272     {
02273         images.push_back(new SrcPanoImage(*(*it)));
02274     }
02275     // Copies of SrcPanoImage's variables aren't linked, so we have to create
02276     // new links in the same pattern.
02282     std::size_t num_imgs = images.size();
02283     for (std::size_t i = 0; i < num_imgs; i++)
02284     {
02285         // copy this image's links.
02286         // links to lower numbered images will have already been spotted, since
02287         // they are bi-directional.
02288         for (std::size_t j = i + 1; j < num_imgs; j++)
02289         {
02290 #define image_variable( name, type, default_value )\
02291             if (data.images[i]->name##isLinkedWith(*data.images[j]))\
02292             {\
02293                 images[i]->link##name(images[j]);\
02294             }
02295 #include "image_variables.h"
02296 #undef image_variable
02297         }
02298     }
02299     
02300     ctrlPoints = data.ctrlPoints;
02301     iccProfileDesc = data.iccProfileDesc;
02302     
02303     options = data.options;
02304     
02305     optSwitch = data.optSwitch;
02306     optPhotoSwitch = data.optPhotoSwitch;
02307     optvec = data.optvec;
02308 
02309     needsOptimization = data.needsOptimization;
02310     
02311     return *this;
02312 }
02313 
02314 PanoramaMemento::~PanoramaMemento()
02315 {
02316     deleteAllImages();
02317 }
02318 
02319 void PanoramaMemento::deleteAllImages()
02320 {
02321     // delete all the images pointed to by the images vector.
02322     for (std::vector<SrcPanoImage *>::iterator it = images.begin();
02323          it != images.end(); ++it)
02324     {
02325         delete *it;
02326     }
02327     // now clear the pointers themselves.
02328     images.clear();
02329 }
02330 
02331 bool PanoramaMemento::loadPTScript(std::istream &i, int & ptoVersion, const std::string &prefix)
02332 {
02333     DEBUG_TRACE("");
02334     // set numeric locale to C, for correct number output
02335     char * p = setlocale(LC_NUMERIC,NULL);
02336     char * old_locale = strdup(p);
02337     setlocale(LC_NUMERIC,"C");
02338     PTParseState state;
02339     std::string line;
02340 
02341     // vector with the different information lines about images
02342     std::vector<PTScriptParsing::ImgInfo> oImgInfo;
02343     std::vector<PTScriptParsing::ImgInfo> iImgInfo;
02344     // strange comment information.
02345     std::vector<PTScriptParsing::ImgInfo> cImgInfo;
02346     // hugin additional information
02347     std::vector<PTScriptParsing::ImgInfo> huginImgInfo;
02348     // vector with readed masks
02349     MaskPolygonVector ImgMasks;
02350     CPVector loadedCp;
02351 
02352     // indicate lines that should be skipped for whatever reason
02353     bool skipNextLine = false;
02354 
02355     bool PTGUIScriptFile = false;
02356     int PTGUIScriptVersion = 0;
02357     // PTGui lens line detected
02358     int ctrlPointsImgNrOffset = 0;
02359     bool PTGUILensLine = false;
02360 
02361     bool PTGUILensLoaded = false;
02362     PTScriptParsing::ImgInfo PTGUILens;
02363 
02364     // set new options to some sensible default.
02365     options.reset();
02366     options.tiff_saveROI = false;
02367 
02368     ptoVersion = 1;
02369 
02370     bool firstOptVecParse = true;
02371     unsigned int lineNr = 0;    
02372     while (i.good()) {
02373         std::getline(i, line);
02374         lineNr++;
02375         DEBUG_DEBUG(lineNr << ": " << line);
02376         if (skipNextLine) {
02377             skipNextLine = false;
02378             continue;
02379         }
02380         //skip emtpy lines
02381         if(line.empty())
02382             continue;
02383         // check for a known line
02384         switch(line[0]) {
02385         case 'p':
02386         {
02387             DEBUG_DEBUG("p line: " << line);
02388             int i;
02389             if (PTScriptParsing::getIntParam(i, line, "f"))
02390                 options.setProjection( (PanoramaOptions::ProjectionFormat) i );
02391             unsigned int w;
02392             if (PTScriptParsing::getIntParam(w, line, "w"))
02393                 options.setWidth(w);
02394             double v;
02395             if (PTScriptParsing::getDoubleParam(v, line, "v"))
02396                 options.setHFOV(v, false);
02397             int height;
02398             if (PTScriptParsing::getIntParam(height, line, "h"))
02399                 options.setHeight(height);
02400 
02401             double newE;
02402             if (PTScriptParsing::getDoubleParam(newE, line, "E"))
02403                 options.outputExposureValue = newE;
02404             int ar=0;
02405             if (PTScriptParsing::getIntParam(ar, line, "R"))
02406                 options.outputMode = (PanoramaOptions::OutputMode) ar;
02407 
02408             std::string format;
02409             if (PTScriptParsing::getPTParam(format, line, "T"))
02410                 options.outputPixelType = format;
02411 
02412             if (PTScriptParsing::getPTParam(format, line, "S")) {
02413                 int left, right, top, bottom;
02414                 int n = sscanf(format.c_str(), "%d,%d,%d,%d", &left, &right, &top, &bottom);
02415                 if (n == 4) {
02416                     options.setROI(vigra::Rect2D(left, top, right, bottom));
02417                 } else {
02418                     DEBUG_WARN("Could not parse crop string: " << format);
02419                 }
02420             }
02421 
02422             // parse projection parameters
02423             if (PTScriptParsing::getPTParam(format, line, "P")) {
02424                 char * tstr = strdup(format.c_str());
02425                 std::vector<double> projParam;
02426                 char * b = strtok(tstr, " \"");
02427                 if (b != NULL) {
02428                     while (b != NULL) {
02429                     double tempDbl;
02430                     if (sscanf(b, "%lf", &tempDbl) == 1) {
02431                         projParam.push_back(tempDbl);
02432                         b = strtok(NULL, " \"");
02433                     }
02434                     }
02435                 }
02436                 free(tstr);
02437                 // only set projection parameters, if the have the right size.
02438                 if (projParam.size() == options.getProjectionParameters().size()) {
02439                     options.setProjectionParameters(projParam);
02440                 }
02441             }
02442 
02443             // this is fragile.. hope nobody adds additional whitespace
02444             // and other arguments than q...
02445             // n"JPEG q80"
02446             if (PTScriptParsing::getPTParam(format, line, "n")) {
02447                 int t = format.find(' ');
02448                 options.outputFormat = options.getFormatFromName(format.substr(0,t));
02449 
02450                 // parse output format options.
02451                 switch (options.outputFormat)
02452                 {
02453                     case PanoramaOptions::JPEG:
02454                     case PanoramaOptions::JPEG_m:
02455                     {
02456                         // "parse" jpg quality
02457                         int q;
02458                         if (PTScriptParsing::getIntParam(q, format, "q")) {
02459                         options.quality = (int) q;
02460                         }
02461                     }
02462                     break;
02463                     case PanoramaOptions::TIFF_m:
02464                     {
02465                         int coordImgs = 0;
02466                         if (PTScriptParsing::getIntParam(coordImgs, format, "p"))
02467                         if (coordImgs)
02468                             options.saveCoordImgs = true;
02469                     }
02470                     case PanoramaOptions::TIFF:
02471                     case PanoramaOptions::TIFF_mask:
02472                     case PanoramaOptions::TIFF_multilayer:
02473                     case PanoramaOptions::TIFF_multilayer_mask:
02474                     {
02475                         // parse tiff compression mode
02476                         std::string comp;
02477                         if (PTScriptParsing::getPTParam(comp, format, "c:")) {
02478                             if (comp == "NONE" || comp == "LZW" ||
02479                                 comp == "PACKBITS" || comp == "DEFLATE")
02480                             {
02481                                 options.tiffCompression = comp;
02482                             } else {
02483                                 DEBUG_WARN("No valid tiff compression found");
02484                             }
02485                         }
02486                         // read tiff roi
02487                         if (PTScriptParsing::getPTParam(comp, format, "r:")) {
02488                             if (comp == "CROP") {
02489                                 options.tiff_saveROI = true;
02490                             } else {
02491                                 options.tiff_saveROI = false;
02492                             }
02493                         }
02494                     }
02495                 break;
02496                 default:
02497                 break;
02498                 }
02499             }
02500 
02501             int cRefImg = 0;
02502             if (PTScriptParsing::getIntParam(cRefImg, line, "k")) {
02503                 options.colorCorrection = PanoramaOptions::BRIGHTNESS_COLOR;
02504             } else if (PTScriptParsing::getIntParam(cRefImg, line,"b")) {
02505                 options.colorCorrection = PanoramaOptions::BRIGHTNESS;
02506             } else if (PTScriptParsing::getIntParam(cRefImg, line,"d")) {
02507                 options.colorCorrection = PanoramaOptions::COLOR;
02508             } else {
02509                 options.colorCorrection = PanoramaOptions::NONE;
02510             }
02511             options.colorReferenceImage=cRefImg;
02512             break;
02513 
02514         }
02515         case 'm':
02516         {
02517             DEBUG_DEBUG("m line: " << line);
02518             // parse misc options
02519             int i;
02520             if (PTScriptParsing::getIntParam(i, line, "i"))
02521                 options.interpolator = (vigra_ext::Interpolator) i;
02522             (void)PTScriptParsing::getDoubleParam(options.gamma, line, "g");
02523 
02524             if (PTScriptParsing::getIntParam(i, line, "f")) {
02525                 switch(i) {
02526                 case 0:
02527                     options.remapAcceleration = PanoramaOptions::MAX_SPEEDUP;
02528                     break;
02529                 case 1:
02530                     options.remapAcceleration = PanoramaOptions::MEDIUM_SPEEDUP;
02531                     break;
02532                 default:
02533                     options.remapAcceleration = PanoramaOptions::NO_SPEEDUP;
02534                     break;
02535                 }
02536             } else {
02537                 options.remapAcceleration = PanoramaOptions::NO_SPEEDUP;
02538             }
02539 
02540             break;
02541         }
02542         case 'v':
02543         {
02544             DEBUG_DEBUG("v line: " << line);
02545             if (!PTGUIScriptFile) {
02546                 if (firstOptVecParse) {
02547                     int nImg = std::max(iImgInfo.size(), oImgInfo.size());
02548                     DEBUG_DEBUG("nImg: " << nImg);
02549                     optvec = OptimizeVector(nImg);
02550                     firstOptVecParse = false;
02551                 }
02552                 std::stringstream optstream;
02553                 optstream << line.substr(1);
02554                 std::string var;
02555                 while (!(optstream >> std::ws).eof()) {
02556                     optstream >> var;
02557                     if (var.length() == 1) {
02558                         // special case for PTGUI
02559                         var += "0";
02560                     }
02561                     // find first numerical character
02562                     std::string::size_type np = var.find_first_of("0123456789");
02563                     if (np == std::string::npos) {
02564                         // invalid, continue
02565                         continue;
02566                     }
02567                     std::string name=var.substr(0,np);
02568                     unsigned int nr;
02569                     if (!hugin_utils::stringToUInt(var.substr(np), nr))
02570                     {
02571                         // invalid, continue
02572                         continue;
02573                     };
02574                     DEBUG_ASSERT(nr < optvec.size());
02575                     if(nr < optvec.size())
02576                     {
02577                         optvec[nr].insert(name);
02578                         DEBUG_DEBUG("parsing opt: >" << var << "< : var:" << name << " image:" << nr);
02579                     };
02580                 }
02581             }
02582             break;
02583         }
02584         case 'c':
02585         {
02586             DEBUG_DEBUG("c line: " << line);
02587             int t;
02588             // read control points
02589             ControlPoint point;
02590             // TODO - should verify that line syntax is correct
02591             PTScriptParsing::getIntParam(point.image1Nr, line, "n");
02592             point.image1Nr += ctrlPointsImgNrOffset;
02593             PTScriptParsing::getIntParam(point.image2Nr, line, "N");
02594             point.image2Nr += ctrlPointsImgNrOffset;
02595             PTScriptParsing::getDoubleParam(point.x1, line, "x");
02596             PTScriptParsing::getDoubleParam(point.x2, line, "X");
02597             PTScriptParsing::getDoubleParam(point.y1, line, "y");
02598             PTScriptParsing::getDoubleParam(point.y2, line, "Y");
02599             if (!PTScriptParsing::getIntParam(t, line, "t")){
02600                 t = 0;
02601             }
02602 
02603             point.mode = t;
02604             loadedCp.push_back(point);
02605             state = P_CP;
02606             break;
02607         }
02608 
02609         // handle the complicated part.. the image & lens settings.
02610         // treat i and o lines the same.. however, o lines have priority
02611         // over i lines.(i lines often do not contain link information!)
02612         case 'i':
02613         {
02614             if (PTGUILensLine) {
02615                 PTGUILensLine = false;
02616                 PTGUILensLoaded = true;
02617                 PTGUILens.parse(line);
02618             } else {
02619                 iImgInfo.push_back(PTScriptParsing::ImgInfo(line));
02620             }
02621             break;
02622         }
02623         case 'o':
02624         {
02625             if (PTGUILensLine) {
02626                 PTGUILensLine = false;
02627                 PTGUILensLoaded = true;
02628                 PTGUILens.parse(line);
02629             } else {
02630                 oImgInfo.push_back(PTScriptParsing::ImgInfo(line));
02631             }
02632             break;
02633         }
02634 
02635         case 'k':
02636         {
02637             unsigned int param;
02638             if (PTScriptParsing::getIntParam(param, line, "i"))
02639             {
02640                 MaskPolygon newPolygon;
02641                 newPolygon.setImgNr(param);
02642                 if (PTScriptParsing::getIntParam(param, line, "t"))
02643                     newPolygon.setMaskType((HuginBase::MaskPolygon::MaskType)param);
02644                 std::string format;
02645                 if (PTScriptParsing::getPTParam(format, line, "p"))
02646                 {
02647                     if(newPolygon.parsePolygonString(format))
02648                         ImgMasks.push_back(newPolygon);
02649                 };
02650             };
02651             break;
02652         }
02653 
02654         case '#':
02655         {
02656             // parse special comments...
02657             if (line.substr(0,20) == std::string("# ptGui project file")) {
02658                 PTGUIScriptFile = true;
02659             }
02660             if (line.substr(0,12) == "#-dummyimage") {
02661                 PTGUILensLine = true;
02662             }
02663             if (PTGUIScriptFile) {
02664                 // parse special PTGUI stuff.
02665                 if (sscanf(line.c_str(), "#-fileversion %d", &PTGUIScriptVersion) > 0) {
02666                     DEBUG_DEBUG("Detected PTGUI script version: " << PTGUIScriptVersion);
02667                     switch (PTGUIScriptVersion) {
02668                         case 0:
02669                             break;
02670                         case 1:
02671                             break;
02672                         case 2:
02673                             break;
02674                         case 3:
02675                             break;
02676                         case 4:
02677                             break;
02678                         case 5:
02679                             break;
02680                         case 6:
02681                             break;
02682                         case 7:
02683                             break;
02684                         default:
02685                             ctrlPointsImgNrOffset = -1;
02686                             // latest known version is 8
02687                             break;
02688                     }
02689                 }
02690             }
02691 
02692             if (line.substr(0,8) == "#-hugin ") {
02693                 // read hugin image line
02694                 PTScriptParsing::ImgInfo info;
02695                 info.autoCenterCrop = (line.find("autoCenterCrop=1") != std::string::npos);
02696                 size_t pos = line.find("cropFactor=");
02697                 if (pos > 0 && pos < line.length()) {
02698                     double cropFactor=1;
02699                     const char * s = line.c_str() + pos;
02700                     sscanf(s,"cropFactor=%lf", & cropFactor);
02701                     if(cropFactor<0.01 || cropFactor > 100)
02702                         cropFactor=1;
02703                     info.cropFactor = cropFactor;
02704                 }
02705                 pos = line.find("disabled");
02706                 if (pos > 0 && pos < line.length()) {
02707                     info.enabled = false;
02708                 }
02709                 huginImgInfo.push_back(info);
02710             }
02711 
02712             // PTGui and PTAssember project files:
02713             // #-imgfile 960 1280 "D:\data\bruno\074-098\087.jpg"
02714             if (line.substr(0,10) == "#-imgfile ") {
02715 
02716                 // arghhh. I like string processing without regexps.
02717                 int b = line.find_first_not_of(" ",9);
02718                 int e = line.find_first_of(" ",b);
02719                 DEBUG_DEBUG(" width:" << line.substr(b, e - b) << ":")
02720                 int nextWidth;
02721                 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextWidth))
02722                 {
02723                     continue;
02724                 };
02725                 DEBUG_DEBUG("next width " << nextWidth);
02726                 b = line.find_first_not_of(" ",e);
02727                 e = line.find_first_of(" ",b);
02728                 DEBUG_DEBUG(" height:" << line.substr(b, e - b) << ":")
02729                 int nextHeight;
02730                 if (!hugin_utils::stringToInt(line.substr(b, e - b), nextHeight))
02731                 {
02732                     continue;
02733                 };
02734                 DEBUG_DEBUG("next height " << nextHeight);
02735 
02736                 std::string nextFilename;
02737                 try {
02738                     b = line.find_first_not_of(" \"",e);
02739                     e = line.find_first_of("\"",b);
02740                     nextFilename = line.substr(b,e-b);
02741                 } catch (std::out_of_range& e) {
02742                     DEBUG_ERROR("ERROR PARSING INPUT FILE" << e.what( ));
02743                     return false;
02744                 }
02745                 DEBUG_DEBUG("next filename " << nextFilename);
02746 
02747                 PTScriptParsing::ImgInfo info;
02748                 info.width  = nextWidth;
02749                 info.height = nextHeight;
02750                 info.filename = nextFilename;
02751                 cImgInfo.push_back(info);
02752             }
02753 
02754 
02755             // parse hugin properties
02756             if (line.substr(0,7) == "#hugin_") {
02757                 std::istringstream is(line);
02758                 std::string var,value;
02759                 is >> var >> value;
02760                 if (!is.fail()) {
02761                     if (var == "#hugin_ptoversion") {
02762                         ptoVersion = atoi(value.c_str());
02763                     }
02764 
02765                     if (var == "#hugin_optimizeReferenceImage") {
02766                         options.optimizeReferenceImage = atoi(value.c_str());
02767                     } else if (var == "#hugin_remapper") {
02768                         if (value == "nona") {
02769                             options.remapper = PanoramaOptions::NONA;
02770                         } else if (value == "PTmender") {
02771                             options.remapper = PanoramaOptions::PTMENDER;
02772                         }
02773                     } else if (var == "#hugin_blender") {
02774                         if (value == "none") {
02775                             options.blendMode = PanoramaOptions::NO_BLEND;
02776                         } else if (value == "PTblender") {
02777                             options.blendMode = PanoramaOptions::PTBLENDER_BLEND;
02778                         } else if (value == "enblend") {
02779                             options.blendMode = PanoramaOptions::ENBLEND_BLEND;
02780                         } else if (value == "PTmasker") {
02781                             options.blendMode = PanoramaOptions::PTMASKER_BLEND;
02782                         } else if (value == "smartblend") {
02783                             options.blendMode = PanoramaOptions::SMARTBLEND_BLEND;
02784                         } else if (value == "internal") {
02785                             options.blendMode = PanoramaOptions::INTERNAL_BLEND;
02786                         }
02787 
02788                     } else if (var == "#hugin_enblendOptions") {
02789                         options.enblendOptions = value;
02790                         while (!is.eof()) {
02791                             is >> value;
02792                             if (value.length() > 0) {
02793                                 options.enblendOptions += " ";
02794                                 options.enblendOptions += value;
02795                             }
02796                         }
02797                     } else if (var == "#hugin_enfuseOptions") {
02798                         options.enfuseOptions = value;
02799                         while (!is.eof()) {
02800                             is >> value;
02801                             if (value.length() > 0) {
02802                                 options.enfuseOptions += " ";
02803                                 options.enfuseOptions += value;
02804                             }
02805                         }
02806                     } else if (var == "#hugin_hdrmergeOptions") {
02807                         options.hdrmergeOptions = value;
02808                         while (!is.eof()) {
02809                             is >> value;
02810                             if (value.length() > 0) {
02811                                 options.hdrmergeOptions += " ";
02812                                 options.hdrmergeOptions += value;
02813                             }
02814                         }
02815 
02816                     } else if (var == "#hugin_outputLDRBlended") {
02817                         options.outputLDRBlended = (value == "true");
02818                     } else if (var == "#hugin_outputLDRLayers") {
02819                         options.outputLDRLayers = (value == "true");
02820                     } else if (var == "#hugin_outputLDRExposureRemapped") {
02821                         options.outputLDRExposureRemapped = (value == "true");
02822                     } else if (var == "#hugin_outputLDRExposureLayers") {
02823                         options.outputLDRExposureLayers = (value == "true");
02824                     } else if (var == "#hugin_outputLDRExposureBlended") {
02825                         options.outputLDRExposureBlended = (value == "true");
02826                     } else if (var == "#hugin_outputLDRExposureLayersFused") {
02827                         options.outputLDRExposureLayersFused = (value == "true");
02828                     } else if (var == "#hugin_outputLDRStacks") {
02829                         options.outputLDRStacks = (value == "true");
02830                     } else if (var == "#hugin_outputHDRBlended") {
02831                         options.outputHDRBlended = (value == "true");
02832                     } else if (var == "#hugin_outputHDRLayers") {
02833                         options.outputHDRLayers = (value == "true");
02834                     } else if (var == "#hugin_outputHDRStacks") {
02835                         options.outputHDRStacks = (value == "true");
02836 
02837                     } else if (var == "#hugin_outputStacksMinOverlap") {
02838                         double val=atof(value.c_str());
02839                         if(val>0 && val <= 1)
02840                         {
02841                             options.outputStacksMinOverlap = val;
02842                         };
02843                         if (val < 0)
02844                         {
02845                             options.outputStacksMinOverlap = -1;
02846                         };
02847                     } else if (var == "#hugin_outputLayersExposureDiff") {
02848                         double val=atof(value.c_str());
02849                         if(val>0.01)
02850                         {
02851                             options.outputLayersExposureDiff = val;
02852                         }
02853 
02854                     } else if (var == "#hugin_outputLayersCompression") {
02855                         options.outputLayersCompression = value;
02856                     } else if (var == "#hugin_outputImageType") {
02857                         options.outputImageType = value;
02858                     } else if (var == "#hugin_outputImageTypeCompression") {
02859                         options.outputImageTypeCompression = value;
02860                     } else if (var == "#hugin_outputJPEGQuality") {
02861                         options.quality = atoi(value.c_str());
02862                     } else if (var == "#hugin_outputImageTypeHDR") {
02863                         options.outputImageTypeHDR = value;
02864                     } else if (var == "#hugin_outputImageTypeHDRCompression") {
02865                         options.outputImageTypeHDRCompression = value;
02866                     } else if (var == "#hugin_optimizerMasterSwitch") {
02867                         optSwitch = atoi(value.c_str());
02868                     } else if (var == "#hugin_optimizerPhotoMasterSwitch") {
02869                         optPhotoSwitch = atoi(value.c_str());
02870                     };
02871 
02872                 }
02873             }
02874             break;
02875         }
02876 
02877         } // case
02878     }
02879 
02880     // assemble images from the information read before..
02881 
02884 #if 0
02885     // handle PTGUI special case
02886     if (PTGUILensLoaded) {
02887         // create lens with dummy info
02888         Lens l;
02889         for (const char **v = Lens::variableNames; *v != 0; v++) {
02890             map_get(l.variables, *v).setValue(PTGUILens.vars[*v]);
02891         }
02892         l.setImageSize(vigra::Size2D(PTGUILens.width, PTGUILens.height));
02893         l.setCropFactor(1);
02894         l.setProjection((Lens::LensProjectionFormat) PTGUILens.f);
02895         lenses.push_back(l);
02896     }
02897 #endif
02898 
02899 /*
02900     // ugly hack to load PTGui script files
02901     if (ptGUIDummyImage) {
02902         DEBUG_DEBUG("loading default PTGUI line: " << line);
02903             Lens l;
02904             // skip ptgui's dummy image
02905             // load parameters into default lens...
02906             for (LensVarMap::iterator it = l.variables.begin();
02907              it != l.variables.end();
02908              ++it)
02909             {
02910                 DEBUG_DEBUG("reading default lens variable " << it->first);
02911                 int link;
02912                 bool ok = readVar(it->second, link, line);
02913                 DEBUG_ASSERT(ok);
02914                 DEBUG_ASSERT(link == -1);
02915             }
02916             lenses.push_back(l);
02917 
02918             ptGUIDummyImage = false;
02919             break;
02920         }
02921 */
02922 
02923     // merge image info from the 3 different lines...
02924     // i lines are the main reference.
02925 
02926     int nImgs = iImgInfo.size();
02927     int nOLines = oImgInfo.size();
02928     int nCLines = cImgInfo.size();
02929 
02930     if (nImgs < nOLines) {
02931         // no, or less i lines found. scrap i lines.
02932         DEBUG_DEBUG("throwing away " << nImgs << " i lines");
02933         iImgInfo = oImgInfo;
02934         nImgs = nOLines;
02935     }
02936     if (nOLines < nImgs) {
02937         oImgInfo = iImgInfo;
02938     }
02939 
02940     // merge o lines and i lines into i lines.
02941     for (int i=0; i < nImgs; i++) {
02942 
02943         // move parameters from o lines -> i (only if it isn't given in the
02944         // i lines. or it is linked on the o lines)
02945 
02946         // ordinary variables
02947         for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v; v++) {
02948 
02949             if ((iImgInfo[i].links[*v] == -2 && oImgInfo[i].links[*v] != -2) || (iImgInfo[i].links[*v] == -1 && oImgInfo[i].links[*v] >=0)) {
02950                 DEBUG_DEBUG(*v << ": o -> i");
02951                 iImgInfo[i].vars[*v] = oImgInfo[i].vars[*v];
02952                 iImgInfo[i].links[*v] = oImgInfo[i].links[*v];
02953             }
02954         }
02955 
02956         if (iImgInfo[i].filename == "" && oImgInfo[i].filename != "") {
02957             DEBUG_DEBUG("filename: o -> i");
02958             iImgInfo[i].filename = oImgInfo[i].filename;
02959         }
02960 
02961         if (iImgInfo[i].crop.isEmpty() && !oImgInfo[i].crop.isEmpty()) {
02962             DEBUG_DEBUG("crop: o -> i");
02963             iImgInfo[i].crop = oImgInfo[i].crop;
02964         }
02965 
02966         if (iImgInfo[i].width <= 0 && oImgInfo[i].width > 0) {
02967             DEBUG_DEBUG("width: o -> i");
02968             iImgInfo[i].width = oImgInfo[i].width;
02969         }
02970 
02971         if (iImgInfo[i].height <= 0 && oImgInfo[i].height > 0) {
02972             DEBUG_DEBUG("height: o -> i");
02973             iImgInfo[i].height = oImgInfo[i].height;
02974         }
02975 
02976         if (iImgInfo[i].f < 0 && oImgInfo[i].f > 0) {
02977             DEBUG_DEBUG("f: o -> i");
02978             iImgInfo[i].f = oImgInfo[i].f;
02979         }
02980 
02981         if (nCLines == nImgs) {
02982             // img file & size in clines
02983             if (cImgInfo[i].filename != "" && cImgInfo[i].width > 0) {
02984                 DEBUG_DEBUG("filename, width, height: c -> i");
02985                 iImgInfo[i].filename = cImgInfo[i].filename;
02986                 iImgInfo[i].width = cImgInfo[i].width;
02987                 iImgInfo[i].height = cImgInfo[i].height;
02988             }
02989         }
02990         if (huginImgInfo.size() == (size_t)nImgs) {
02991             iImgInfo[i].cropFactor = huginImgInfo[i].cropFactor;
02992             iImgInfo[i].autoCenterCrop = huginImgInfo[i].autoCenterCrop;
02993             iImgInfo[i].enabled= huginImgInfo[i].enabled;
02994         }
02995     }
02996 
02997     // create images.
02998     for (int i=0; i < nImgs; i++) {
02999 
03000         DEBUG_DEBUG("i line: " << i);
03001         // read the variables
03002         VariableMap vars;
03003         int link = -2;
03004         fillVariableMap(vars);
03005 
03006         for (const char ** v = PTScriptParsing::ImgInfo::varnames; *v != 0; v++) {
03007             std::string name(*v);
03008             double val = iImgInfo[i].vars[*v];
03009             map_get(vars,name).setValue(val);
03010             if (iImgInfo[i].links[*v] >= 0) {
03011                 link = iImgInfo[i].links[*v];
03012             }
03013         }
03014         
03015         std::string file = iImgInfo[i].filename;
03016         // add prefix if only a relative path.
03017 #ifdef _WIN32
03018         bool absPath = ( (file[1]==':' && file[2]=='\\') || (file[1]==':' && file[2]=='/') || (file[0] == '\\' && file[1] == '\\'));
03019 #else
03020         bool absPath = file[0] == '/';
03021 #endif
03022         if (!absPath) {
03023             file.insert(0, prefix);
03024         }
03025         DEBUG_DEBUG("filename: " << file);
03026         
03027         // Make a new SrcPanoImage in this list for expressing this one.
03028         SrcPanoImage * new_img_p = new SrcPanoImage();
03029         images.push_back(new_img_p);
03030         // and make a reference to it so we dont keep using images.back(),
03031         SrcPanoImage & new_img = *new_img_p;
03032         new_img.setFilename(file);
03033         new_img.setSize(vigra::Size2D(iImgInfo[i].width, iImgInfo[i].height));
03034         new_img.checkImageSizeKnown();
03035         
03036         // Panotools Script variables for the current SrcPanoImage variable.
03037         // We just need the names.
03038         VariableMap vars_for_name;
03039         
03040         // A dummy SrcPanoImage to use to fill vars_for_name.
03041         SrcPanoImage name_src;
03042         
03043 /* Set image variables in new_img using the PanoTools Script names, with
03044  * linking where specified in the file.
03045  * 
03046  * We create a list of PanoTools script variable names for each variable in
03047  * SrcPanoImg (vars_for_name). It may have multiple variables or none at all.
03048  * If a link is found between any of the variables inside it we can link them up
03049  * in new_img.
03050  * 
03051  * The Panorama Tools script format makes it possible to link parts of the same
03052  * SrcPanoImage variable differently (e.g. you can link the horizontal component
03053  * of shearing to a different target to the vertical component, not link the
03054  * vertical component at all). SrcPanoImage cannot handle this, so we end up
03055  * linking all components of the same variable to anything specified in the PTO
03056  * script for one of the components: most often we specify the same link
03057  * multiple times.
03058  */
03062 #define RESET_LOCALE setlocale(LC_NUMERIC,old_locale); free(old_locale);
03063 #define image_variable( name, type, default_value )\
03064         PTOVariableConverterFor##name::addToVariableMap(name_src.get##name##IV(), vars_for_name);\
03065         for (VariableMap::iterator vit = vars_for_name.begin();\
03066              vit != vars_for_name.end(); vit++)\
03067         {\
03068             if (link >= 0 && iImgInfo[i].links[vit->first] >= 0)\
03069             {\
03070                 if (   !(PTGUILensLoaded && link == 0)\
03071                     && (int) images.size() < link && (!PTGUILensLoaded))\
03072                 {\
03073                     DEBUG_ERROR("variables must be linked to an image with a lower number" << endl\
03074                                 << "number links: " << link << " images: " << images.size() << endl\
03075                                 << "error on line " << lineNr << ":" << endl\
03076                                 << line);\
03077                     RESET_LOCALE\
03078                     return false;\
03079                 }\
03080                 DEBUG_DEBUG("anchored to image " << iImgInfo[i].links[vit->first]);\
03081                 new_img.link##name(images[iImgInfo[i].links[vit->first]]);\
03082             } else {\
03083                 double val = map_get(vars, vit->first).getValue();\
03084                 new_img.setVar(vit->first, val);\
03085             }\
03086         }\
03087         vars_for_name.clear();
03088 #include "image_variables.h"
03089 #undef image_variable
03090         new_img.setProjection((SrcPanoImage::Projection) iImgInfo[i].f);
03091 
03092         // check, if stacks are correctly linked
03093 #define check_stack_link(name) \
03094         if(!new_img.YawisLinked() && new_img.name##isLinked())\
03095         {\
03096             new_img.unlink##name();\
03097         };\
03098         if(new_img.YawisLinked() && !new_img.name##isLinked())\
03099         {\
03100             for(size_t j=0; j<i; j++)\
03101             {\
03102                 if(new_img.YawisLinkedWith(*images[j]))\
03103                 {\
03104                     new_img.link##name(images[j]);\
03105                     break;\
03106                 };\
03107             };\
03108         }
03109         check_stack_link(Pitch);
03110         check_stack_link(Roll);
03111         check_stack_link(X);
03112         check_stack_link(Y);
03113         check_stack_link(Z);
03114         check_stack_link(TranslationPlaneYaw);
03115         check_stack_link(TranslationPlanePitch);
03116 #undef check_stack_link
03117 
03118 #if 0
03119         new_img.setFeatherWidth((unsigned int) iImgInfo[i].blend_radius);
03120 #endif
03121         
03122         // is this right?
03123         new_img.setCropFactor(iImgInfo[i].cropFactor);
03124         new_img.setVigCorrMode(iImgInfo[i].vigcorrMode);
03125         new_img.setFlatfieldFilename(iImgInfo[i].flatfieldname);
03126         new_img.setResponseType((SrcPanoImage::ResponseType)iImgInfo[i].responseType);
03127         new_img.setAutoCenterCrop(iImgInfo[i].autoCenterCrop);
03128         new_img.setActive(iImgInfo[i].enabled);
03129         
03130         if (!iImgInfo[i].crop.isEmpty()) {
03131             if (new_img.isCircularCrop())
03132             {
03133                 new_img.setCropMode(SrcPanoImage::CROP_CIRCLE);
03134             } else {
03135                 new_img.setCropMode(SrcPanoImage::CROP_RECTANGLE);
03136             }
03137             new_img.setCropRect(iImgInfo[i].crop);
03138         }
03139 
03140         //now fill the mask
03141         for(unsigned int j=0;j<ImgMasks.size();j++)
03142             if(ImgMasks[j].getImgNr()==i)
03143                 //now clip mask to image size + offset
03144                 if(ImgMasks[j].clipPolygon(vigra::Rect2D(-0.9*maskOffset,-0.9*maskOffset,
03145                     new_img.getWidth()+0.9*maskOffset,new_img.getHeight()+0.9*maskOffset)))
03146                 {
03147                     new_img.addMask(ImgMasks[j]);
03148                 };
03149     }
03150     
03151     // if we haven't found a v line in the project file
03152     if (optvec.size() != images.size()) {
03153         optvec = OptimizeVector(images.size());
03154     }
03155 
03156     if (!loadedCp.empty())
03157     {
03158         // check if control points are linked with existing images
03159         const size_t nrImg = images.size();
03160         for (CPVector::const_iterator it = loadedCp.begin(); it != loadedCp.end(); ++it)
03161         {
03162             HuginBase::ControlPoint cp = *it;
03163             if (cp.image1Nr < nrImg && cp.image2Nr < nrImg)
03164             {
03165                 ctrlPoints.push_back(cp);
03166             };
03167         };
03168         if (loadedCp.size() != ctrlPoints.size())
03169         {
03170             std::cout << "WARNING: Project file contains control points that are connected with" << std::endl
03171                 << "  non existing images. Ignoring these control points." << std::endl;
03172         };
03173     };
03174 
03175     // reset locale
03176     setlocale(LC_NUMERIC,old_locale);
03177     free(old_locale);
03178 
03179     return true;
03180 }
03181 
03182 } // namespace

Generated on 5 Feb 2016 for Hugintrunk by  doxygen 1.4.7