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

Generated on 22 Nov 2017 for Hugintrunk by  doxygen 1.4.7