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

Generated on 3 Dec 2016 for Hugintrunk by  doxygen 1.4.7