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

Generated on Wed Sep 17 01:25:38 2014 for Hugintrunk by  doxygen 1.3.9.1