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

Generated on 4 Aug 2015 for Hugintrunk by  doxygen 1.4.7