CPVector AutoCtrlPointCreator::readUpdatedControlPoints(const std::string & file,
                                                    PT::Panorama & pano)
{
    ifstream stream(file.c_str());
    if (! stream.is_open()) {
        DEBUG_ERROR("Could not open autopano output: " << file);
        return CPVector();
    }

    Panorama tmpp;
    PanoramaMemento newPano;
    int ptoVersion = 0;
    newPano.loadPTScript(stream, ptoVersion, "");
    tmpp.setMemento(newPano);

    // create mapping between the panorama images.
    map<unsigned int, unsigned int> imgMapping;
    for (unsigned int ni = 0; ni < tmpp.getNrOfImages(); ni++) {
        std::string nname = stripPath(tmpp.getImage(ni).getFilename());
        for (unsigned int oi=0; oi < pano.getNrOfImages(); oi++) {
            std::string oname = stripPath(pano.getImage(oi).getFilename());
            if (nname == oname) {
                // insert image
                imgMapping[ni] = oi;
                break;
            }
        }
        if (! set_contains(imgMapping, ni)) {
            DEBUG_ERROR("Could not find image " << ni << ", name: " << tmpp.getImage(ni).getFilename() << " in autopano output");
            return CPVector();
        }
    }


    // get control points
    CPVector ctrlPoints = tmpp.getCtrlPoints();
    // make sure they are in correct order
    for (CPVector::iterator it= ctrlPoints.begin(); it != ctrlPoints.end(); ++it) {
        (*it).image1Nr = imgMapping[(*it).image1Nr];
        (*it).image2Nr = imgMapping[(*it).image2Nr];
    }

    return ctrlPoints;
}
int main(int argc, char* argv[])
{

    // Exit with usage unless filename given as argument
    if (argc < 2)
    {
            usage();
    }

    unsigned int i = 1, mask = 0;
    double threshold = 0.5;
    vector<string> images_to_mask;
    string pto_file = (""),output_pto = ("");
    string mask_format = ("PNG");
    string model_file = ("celeste.model");
    int course_fine = 0;
    int resize_dimension=800;

    // Deal with arguments
    // parse arguments
    int c;
    const char * optstring = "i:o:d:s:t:m:f:r:h";

    while ((c = getopt (argc, argv, optstring)) != -1)
    {
        switch(c)
        {
            case 'h': 
                usage();
                break;
            case 'i':
                pto_file=optarg;
                break;
            case 'o':
                output_pto=optarg;
                break;
            case 't':
                threshold = atof(optarg);
                if(threshold<=0 || threshold>1)
                {
                    cerr << "Invalid parameter: threshold (-t) should be between 0 and 1" << std::endl;
                    return 1;
                };
                break;
            case 'm': 
                mask = atoi(optarg);
                if(mask<0 || mask>1)
                {
                    cerr << "Invalid parameter: mask parameter (-m) can only be 0 or 1" << std::endl;
                    return 1;
                };
                break;
            case 'f': 
                mask_format = optarg; 
                break;
            case 'd':
                model_file = optarg;
                break;
            case 'r': 
                course_fine = atoi(optarg);
                break;
            case 's': 
                resize_dimension = atoi(optarg);
                if(resize_dimension<100)
                {
                    cerr << "Invalid parameter: maximum dimension (-s) should be bigger than 100" << std::endl;
                    return 1;
                };
                break;
            case ':':
                cerr <<"Missing parameter for parameter " << argv[optind] << endl;
                return 1;
                break;
            case '?': /* invalid parameter */
                return 1;
                break;
            default: /* unknown */
                usage();
        };
    };

    while(optind<argc)
    {
        images_to_mask.push_back(argv[optind]);
        optind++;
    };
    
    if(images_to_mask.size()==0 && pto_file.empty())
    {
        cout << "No project file or image files given."<< endl;
        return 1;
    };

    
	// Check model file
	if (!fileexists(model_file)){
	
#if _WINDOWS
        char buffer[MAX_PATH];//always use MAX_PATH for filepaths
        GetModuleFileName(NULL,buffer,sizeof(buffer));
        string working_path=(buffer);
        string install_path_model="";
        //remove filename
        std::string::size_type pos=working_path.rfind("\\");
        if(pos!=std::string::npos)
        {
            working_path.erase(pos);
            //remove last dir: should be bin
            pos=working_path.rfind("\\");
            if(pos!=std::string::npos)
            {
                working_path.erase(pos);
                //append path delimiter and path
                working_path.append("\\share\\hugin\\data\\");
                install_path_model=working_path;
            }
        }
#elif defined MAC_SELF_CONTAINED_BUNDLE
		char path[PATH_MAX + 1];
		uint32_t size = sizeof(path);
		string install_path_model("");
		if (_NSGetExecutablePath(path, &size) == 0)
		{
			install_path_model=dirname(path);
			install_path_model.append("/../Resources/xrc/");
		}
#else
        string install_path_model = (INSTALL_DATA_DIR);
#endif

		install_path_model.append(model_file);
		
		if (!fileexists(install_path_model)){
		
    			cout << endl << "Couldn't open SVM model file " << model_file << endl;
			cout << "Also tried " << install_path_model << endl << endl; 
    			exit(1);

		}else{
		
			model_file = install_path_model;
		
		}
  	}

	// Set output .pto filename if not given
	if (output_pto == ("") && pto_file != ("")){
		output_pto = pto_file.substr(0,pto_file.length()-4).append("_celeste.pto");
	}

	// Convert mask format to upper case
	transform(mask_format.begin(), mask_format.end(), mask_format.begin(),(int(*)(int)) toupper);
	if (mask_format == ("JPG")){
		mask_format = ("JPEG");
	}
	if (mask_format != ("PNG") &&mask_format != ("BMP") && mask_format != ("GIF") && mask_format !=	("JPEG") && mask_format != ("TIFF")){
		mask_format = ("TIFF");
	}

	// Print some stuff out
	cout << endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << endl;
	cout << "using Support Vector Machines." << endl;
	cout << endl << "Version " << DISPLAY_VERSION << endl << endl;
	cout << "Arguments:" << endl;
	cout << "Input Hugin file:\t" << pto_file << endl;
	cout << "Output Hugin file:\t" << output_pto << endl;
	cout << "SVM model file:\t\t" << model_file << endl;
	cout << "Max dimension:\t\t" << resize_dimension << endl;
	cout << "SVM threshold:\t\t" << threshold << endl;
	cout << "Create PTO masks:\t";
	if (mask){
		cout << "Yes" << endl;
	}else{
		cout << "No" << endl;
	} 
	cout << "Mask format:\t\t" << mask_format << endl;
	cout << "Filter radius:\t\t";

	// Mask resolution
    int radius;
	if (course_fine)
    {
        radius = 10;
        cout << "Small" << endl << endl;
	}
    else
    {
        radius=20;
        cout << "Large" << endl << endl;
	} 
	
	// Convert mask format to lower case
        cout << "Converting mask format to lower case..." << endl;
	transform(mask_format.begin(), mask_format.end(), mask_format.begin(),(int(*)(int)) tolower);

	// Vectors to store SVM responses and PTO file info etc
	vector<string> images,pto_file_top,pto_file_cps,pto_file_end;
	vector<double> svm_responses;

    cout << "Loading SVMmodel..." << endl;
    struct celeste::svm_model* model;
    if(!celeste::loadSVMmodel(model,model_file))
    {
        cerr << "Could not load SVMmodel" << endl;
        return 1;
    };

    // Mask images
	if (images_to_mask.size())
    {
        cout << "Masking images..." << endl << endl;
        for (unsigned int l = 0; l < images_to_mask.size(); l++)
        {
            std::string imagefile=images_to_mask[l];
            try
            {
                cout << "Opening image file:\t" << imagefile << endl;
                // Read image given and convert to UInt16
                vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
                if(in.width()==0 || in.height()==0)
                {
                    continue;
                };

                // Create mask file name
                string mask_name = generateMaskName(imagefile,mask_format);

                cout << "Generating mask:\t" << mask_name << endl;				
                // Create mask
                vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
                exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
            }
            catch (vigra::StdException & e)
            {
                // catch any errors that might have occured and print their reason
                cout << "Unable to open file:\t" << imagefile << endl << endl;
                cout << e.what() << endl << endl;
    		};
        };
	};

    // Process PTO file
    if (pto_file != (""))
    {
  		cout << "Parsing Hugin project file " << pto_file << endl << endl;

        Panorama pano;
        ifstream prjfile(pto_file.c_str());
        if (!prjfile.good())
        {
            cerr << "could not open script : " << pto_file << endl;
            celeste::destroySVMmodel(model);
            return 1;
        }
        pano.setFilePrefix(hugin_utils::getPathPrefix(pto_file));
        DocumentData::ReadWriteError err = pano.readData(prjfile);
        if (err != DocumentData::SUCCESSFUL)
        {
            cerr << "error while parsing panos tool script: " << pto_file << endl;
            cerr << "DocumentData::ReadWriteError code: " << err << endl;
            celeste::destroySVMmodel(model);
            return 1;
        }
	cout << "Done parsing Hugin step 1" << endl;

        for(unsigned int i=0;i<pano.getNrOfImages();i++)
        {
            CPointVector cps=pano.getCtrlPointsVectorForImage(i);
            if(cps.size()>0 || mask)
            {
                try
                {
                    string imagefile=pano.getImage(i).getFilename();
                    vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
                    if(in.width()==0 || in.height()==0)
                    {
                        continue;
                    };
                    if(cps.size()>0)
                    {
                        UIntSet cloudCP=celeste::getCelesteControlPoints(model,in,cps,radius,threshold,resize_dimension);
                        if(cloudCP.size()>0)
                        {
                            for(UIntSet::reverse_iterator it=cloudCP.rbegin();it!=cloudCP.rend();++it)
                            {
                                pano.removeCtrlPoint(*it);
                            };
                        };
                    };
                    if(mask)
                    {
                        string mask_name = generateMaskName(imagefile,mask_format);
                        // Create mask
                        vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
                        exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
                    };
                }
                catch (vigra::StdException & e)
                {
                    // catch any errors that might have occured and print their reason
                    cout << "Unable to open file:\t" << pano.getImage(i).getFilename() << endl << endl;
                    cout << e.what() << endl << endl;
    		    };
            };
        };
        cout << "about to write new pto file" << endl;

		// write new pto file
        ofstream of(output_pto.c_str());
        UIntSet imgs;
	cout << "fill_set..." << endl;
        fill_set(imgs,0, pano.getNrOfImages()-1);
	cout << "printing..." << endl; 
        pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(pto_file));
    
        cout << endl << "Written file " << output_pto << endl << endl;
	}
    celeste::destroySVMmodel(model);
	return(0);
	
}	
Пример #3
0
int main(int argc, char* argv[])
{
    // parse arguments
    const char* optstring = "o:i:l:h";

    static struct option longOptions[] =
    {
        {"output", required_argument, NULL, 'o' },
        {"image", required_argument, NULL, 'i' },
        {"lines", required_argument, NULL, 'l' },
        {"help", no_argument, NULL, 'h' },
        0
    };

    UIntSet cmdlineImages;
    int c;
    int optionIndex = 0;
    int nrLines = 5;
    string output;
    while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
    {
        switch (c)
        {
            case 'o':
                output = optarg;
                break;
            case 'h':
                usage(argv[0]);
                return 0;
            case 'i':
                {
                    int imgNr=atoi(optarg);
                    if((imgNr==0) && (strcmp(optarg,"0")!=0))
                    {
                        cerr << "Could not parse image number.";
                        return 1;
                    };
                    cmdlineImages.insert(imgNr);
                };
                break;
            case 'l':
                nrLines=atoi(optarg);
                if(nrLines<1)
                {
                    cerr << "Could not parse number of lines.";
                    return 1;
                };
                break;
            case ':':
                cerr <<"Option " << longOptions[optionIndex].name << " requires a number" << endl;
                return 1;
                break;
            case '?':
                break;
            default:
                abort ();
        }
    }

    if (argc - optind != 1)
    {
        cout << "Warning: " << argv[0] << " can only work on one project file at one time" << endl << endl;
        usage(argv[0]);
        return 1;
    };

    string input=argv[optind];
    // read panorama
    Panorama pano;
    ifstream prjfile(input.c_str());
    if (!prjfile.good())
    {
        cerr << "could not open script : " << input << endl;
        return 1;
    }
    pano.setFilePrefix(hugin_utils::getPathPrefix(input));
    DocumentData::ReadWriteError err = pano.readData(prjfile);
    if (err != DocumentData::SUCCESSFUL)
    {
        cerr << "error while parsing panos tool script: " << input << endl;
        cerr << "DocumentData::ReadWriteError code: " << err << endl;
        return 1;
    }

    if(pano.getNrOfImages()==0)
    {
        cerr << "error: project file does not contains any image" << endl;
        cerr << "aborting processing" << endl;
        return 1;
    };

    std::vector<size_t> imagesToProcess;
    if(cmdlineImages.size()==0)
    {
        //no image given, process all
        for(size_t i=0;i<pano.getNrOfImages();i++)
        {
            imagesToProcess.push_back(i);
        };
    }
    else
    {
        //check, if given image numbers are valid
        for(UIntSet::const_iterator it=cmdlineImages.begin();it!=cmdlineImages.end();it++)
        {
            if((*it)>=0 && (*it)<pano.getNrOfImages())
            {
                imagesToProcess.push_back(*it);
            };
        };
    };

    if(imagesToProcess.size()==0)
    {
        cerr << "No image to process found" << endl << "Stopping processing" << endl;
        return 1;
    };

    PT_setProgressFcn(ptProgress);
    PT_setInfoDlgFcn(ptinfoDlg);

    cout << argv[0] << " is searching for vertical lines" << endl;
#if _WINDOWS
    //multi threading of image loading results sometime in a race condition
    //try to prevent this by initialisation of codecManager before
    //running multi threading part
    std::string s=vigra::impexListExtensions();
#endif
#ifdef HAS_PPL
    size_t nrCPS=pano.getNrOfCtrlPoints();
    Concurrency::parallel_for<size_t>(0,imagesToProcess.size(),[&pano,imagesToProcess,nrLines](size_t i)
#else
    for(size_t i=0;i<imagesToProcess.size();i++)
#endif
    {
        unsigned int imgNr=imagesToProcess[i];
        cout << "Working on image " << pano.getImage(imgNr).getFilename() << endl;
        // now load and process all images
        vigra::ImageImportInfo info(pano.getImage(imgNr).getFilename().c_str());
        HuginBase::CPVector foundLines;
        if(info.isGrayscale())
        {
            foundLines=LoadGrayImageAndFindLines(info, pano, imgNr, nrLines);
        }
        else
        {
            if(info.isColor())
            {
                //colour images
                foundLines=LoadImageAndFindLines(info, pano, imgNr, nrLines);
            }
            else
            {
                std::cerr << "Image " << pano.getImage(imgNr).getFilename().c_str() << " has " 
                    << info.numBands() << " channels." << std::endl
                    << "Linefind works only with grayscale or color images." << std::endl
                    << "Skipping image." << std::endl;
            };
        };
#ifndef HAS_PPL
        cout << "Found " << foundLines.size() << " vertical lines" << endl;
#endif
        if(foundLines.size()>0)
        {
            for(CPVector::const_iterator cpIt=foundLines.begin(); cpIt!=foundLines.end(); cpIt++)
            {
                pano.addCtrlPoint(*cpIt);
            };
        };
    }
#ifdef HAS_PPL
    );
void OptimizePhotometricPanel::runOptimizer(const UIntSet & imgs)
{
    DEBUG_TRACE("");
    int mode = m_mode_cb->GetSelection();

    // check if vignetting and response are linked, display a warning if they are not
    // The variables to check:
    const HuginBase::ImageVariableGroup::ImageVariableEnum vars[] = {
            HuginBase::ImageVariableGroup::IVE_EMoRParams,
            HuginBase::ImageVariableGroup::IVE_ResponseType,
            HuginBase::ImageVariableGroup::IVE_VigCorrMode,
            HuginBase::ImageVariableGroup::IVE_RadialVigCorrCoeff,
            HuginBase::ImageVariableGroup::IVE_RadialVigCorrCenterShift
        };
    // keep a list of commands needed to fix it:
    std::vector<PT::PanoCommand *> commands;
    HuginBase::ConstImageVariableGroup & lenses = variable_groups->getLenses();
    for (size_t i = 0; i < lenses.getNumberOfParts(); i++)
    {
        std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> links_needed;
        links_needed.clear();
        for (int v = 0; v < 5; v++)
        {
            if (!lenses.getVarLinkedInPart(vars[v], i))
            {
                links_needed.insert(vars[v]);
            }
        };
        if (!links_needed.empty())
        {
            commands.push_back(new PT::LinkLensVarsCmd(*m_pano, i, links_needed));
        }
    }
    // if the list of commands is empty, all is good and we don't need a warning.
    if (!commands.empty()) {
        int ok = wxMessageBox(_("The same vignetting and response parameters should\nbe applied for all images of a lens.\nCurrently each image can have different parameters.\nLink parameters?"), _("Link parameters"), wxYES_NO | wxICON_INFORMATION);
        if (ok == wxYES) {
            // perform all the commands we stocked up earilier.
            for (std::vector<PT::PanoCommand *>::iterator it = commands.begin();
                    it != commands.end(); it++)
            {
                GlobalCmdHist::getInstance().addCommand(*it);
            }
        } else {
            // free all the commands, the user doesn't want them used.
            for (std::vector<PT::PanoCommand *>::iterator it = commands.begin();
                    it != commands.end(); it++)
            {
                delete *it;
            }
        }
    }

    Panorama optPano = m_pano->getSubset(imgs);
    PanoramaOptions opts = optPano.getOptions();

    OptimizeVector optvars;
    if(mode==OPT_CUSTOM)
    {
        optvars = getOptimizeVector();
        if (optPano.getNrOfImages() != m_pano->getNrOfImages())
        {
            OptimizeVector o = optvars;
            optvars.clear();
            for (UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); ++it)
            {
                optvars.push_back(o[*it]);
            }
        }
        unsigned int countVar=0;
        for(unsigned int i=0;i<optvars.size();i++)
        {
            countVar+=optvars[i].size();
        };
        if(countVar==0)
        {
            wxMessageBox(_("You selected no parameters to optimize.\nTherefore optimization will be canceled."), _("Exposure optimization"), wxOK | wxICON_INFORMATION);
            return;
        };
    };


    std::vector<vigra_ext::PointPairRGB> m_points;
    // extract points only if not done previously

    long nPoints = 200;
    wxConfigBase::Get()->Read(wxT("/OptimizePhotometric/nRandomPointsPerImage"), & nPoints);
    // get parameters for estimation.
    nPoints = wxGetNumberFromUser(_("The vignetting and exposure correction is determined by analysing color values in the overlapping areas.\nTo speed up the computation, only a random subset of points is used."),
                                    _("Number of points per image"),
                                    _("Photometric optimization"), nPoints, 0, 32000,
                                    this);
    if (nPoints < 0) {
        return;
    }
    wxConfigBase::Get()->Write(wxT("/OptimizePhotometric/nRandomPointsPerImage"),nPoints);

    ProgressReporterDialog progress(5.0, _("Photometric alignment"), _("Loading images"));

    progress.Show();

    nPoints = nPoints * optPano.getNrOfImages();
    // get the small images
    std::vector<vigra::FRGBImage *> srcImgs;
    for (size_t i=0; i < optPano.getNrOfImages(); i++) {
        ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename());
        vigra::FRGBImage * img = new FRGBImage;
        if (!e) {
            wxMessageBox(_("Error: could not load all images"), _("Error"));
            return;
        }
        if (e->image8 && e->image8->width() > 0) {
            reduceToNextLevel(*(e->image8), *img);
            transformImage(vigra::srcImageRange(*img), vigra::destImage(*img),
                            vigra::functor::Arg1()/vigra::functor::Param(255.0));
        } else if (e->image16 && e->image16->width() > 0) {
            reduceToNextLevel(*(e->image16), *img);
            transformImage(vigra::srcImageRange(*img), vigra::destImage(*img),
                            vigra::functor::Arg1()/vigra::functor::Param(65535.0));
        } else {
            reduceToNextLevel(*(e->imageFloat), *img);
        }
        srcImgs.push_back(img);
    }
    bool randomPoints = true;
    extractPoints(optPano, srcImgs, nPoints, randomPoints, progress, m_points);

    if (m_points.size() == 0) {
        wxMessageBox(_("Error: no overlapping points found, Photometric optimization aborted"), _("Error"));
        return;
    }

    double error = 0;
    try {
        //wxBusyCursor busyc;
        if (mode != OPT_CUSTOM) {
            // run automatic optimisation
            // ensure that we have a valid anchor.
            PanoramaOptions opts = optPano.getOptions();
            if (opts.colorReferenceImage >= optPano.getNrOfImages()) {
                opts.colorReferenceImage = 0;
                optPano.setOptions(opts);
            }
            smartOptimizePhotometric(optPano, PhotometricOptimizeMode(mode),
                                    m_points, progress, error);
        } else {
            // optimize selected parameters
            optimizePhotometric(optPano, optvars,
                                m_points, progress, error);
        }
    } catch (std::exception & error) {
        wxMessageBox(_("Internal error during photometric optimization:\n") + wxString(error.what(), wxConvLocal), _("Internal error"));
    }

    progress.Close();

    // display information about the estimation process:
    int ret = wxMessageBox(wxString::Format(_("Photometric optimization results:\nAverage difference (RMSE) between overlapping pixels: %.2f gray values (0..255)\n\nApply results?"), error*255),
                           _("Photometric optimization finished"), wxYES_NO | wxICON_INFORMATION,this);

    if (ret == wxYES) {
        DEBUG_DEBUG("Applying vignetting corr");
        // TODO: merge into a single update command
        const VariableMapVector & vars = optPano.getVariables();
        GlobalCmdHist::getInstance().addCommand(
                new PT::UpdateImagesVariablesCmd(*m_pano, imgs, vars)
                                               );
        //now update panorama exposure value
        PanoramaOptions opts = m_pano->getOptions();
        opts.outputExposureValue = calcMeanExposure(*m_pano);
        GlobalCmdHist::getInstance().addCommand(
                new PT::SetPanoOptionsCmd(*m_pano, opts)
                                               );
    }
}
CPVector AutoPanoSiftStack::automatch(CPDetectorSetting &setting, Panorama & pano, const UIntSet & imgs,
                                     int nFeatures, int & ret_value, wxWindow *parent)
{
    CPVector cps;
    if (imgs.size() == 0) {
        return cps;
    };
    std::vector<stack_img> stack_images;
    HuginBase::StandardImageVariableGroups* variable_groups = new HuginBase::StandardImageVariableGroups(pano);
    for(UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); it++)
    {
        unsigned int stack_nr=variable_groups->getStacks().getPartNumber(*it);
        //check, if this stack is already in list
        bool found=false;
        unsigned int index=0;
        for(index=0;index<stack_images.size();index++)
        {
            found=(stack_images[index].layer_nr==stack_nr);
            if(found)
                break;
        };
        if(!found)
        {
            //new stack
            stack_images.resize(stack_images.size()+1);
            index=stack_images.size()-1;
            //add new stack
            stack_images[index].layer_nr=stack_nr;
        };
        //add new image
        unsigned int new_image_index=stack_images[index].images.size();
        stack_images[index].images.resize(new_image_index+1);
        stack_images[index].images[new_image_index].img_nr=*it;
        stack_images[index].images[new_image_index].ev=pano.getImage(*it).getExposure();
    };
    delete variable_groups;
    //get image with median exposure for search with cp generator
    UIntSet images_layer;
    for(unsigned int i=0;i<stack_images.size();i++)
    {
        std::sort(stack_images[i].images.begin(),stack_images[i].images.end(),sort_img_ev);
        unsigned int index=0;
        if(stack_images[i].images[0].ev!=stack_images[i].images[stack_images[i].images.size()-1].ev)
        {
            index=stack_images[i].images.size() / 2;
        };
        images_layer.insert(stack_images[i].images[index].img_nr);
    };
    //generate cp for median exposure
    ret_value=0;
    if(images_layer.size()>1)
    {
        AutoPanoSift matcher;
        cps=matcher.automatch(setting, pano, images_layer, nFeatures, ret_value, parent);
        if(ret_value!=0)
            return cps;
    };
    //now work on all stacks
    if(!setting.GetProgStack().IsEmpty())
    {
        CPDetectorSetting stack_setting;
        stack_setting.SetType(CPDetector_AutoPanoSift);
        stack_setting.SetProg(setting.GetProgStack());
        stack_setting.SetArgs(setting.GetArgsStack());

        for(unsigned int i=0;i<stack_images.size();i++)
        {
            UIntSet images_stack;
            images_stack.clear();
            for(unsigned int j=0;j<stack_images[i].images.size();j++)
                images_stack.insert(stack_images[i].images[j].img_nr);
            if(images_stack.size()>1)
            {
                AutoPanoSift matcher;
                CPVector new_cps=matcher.automatch(stack_setting, pano, images_stack, nFeatures, ret_value, parent);
                if(new_cps.size()>0)
                    AddControlPointsWithCheck(cps,new_cps);
                if(ret_value!=0)
                    return cps;
            };
        };
    }
    return cps;
};
CPVector AutoPanoKolor::automatch(CPDetectorSetting &setting, Panorama & pano, const UIntSet & imgs,
                              int nFeatures, int & ret_value, wxWindow *parent)
{
    CPVector cps;
    wxString autopanoExe = setting.GetProg();

    // write default autopano.kolor.com flags
    wxString autopanoArgs = setting.GetArgs();

    string imgFiles;
    for(UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); it++)
    {
        imgFiles.append(" ").append(quoteFilename(pano.getImage(*it).getFilename()));
    }

    wxString ptofilepath = wxFileName::CreateTempFileName(wxT("ap_res"));
    wxFileName ptofn(ptofilepath);
    wxString ptofile = ptofn.GetFullName();
    autopanoArgs.Replace(wxT("%o"), ptofile);
    wxString tmp;
    tmp.Printf(wxT("%d"), nFeatures);
    autopanoArgs.Replace(wxT("%p"), tmp);
    SrcPanoImage firstImg = pano.getSrcImage(*imgs.begin());
    tmp.Printf(wxT("%f"), firstImg.getHFOV());
    autopanoArgs.Replace(wxT("%v"), tmp);

    tmp.Printf(wxT("%d"), (int) firstImg.getProjection());
    autopanoArgs.Replace(wxT("%f"), tmp);

    autopanoArgs.Replace(wxT("%i"), wxString (imgFiles.c_str(), HUGIN_CONV_FILENAME));

    wxString tempdir = ptofn.GetPath();
	autopanoArgs.Replace(wxT("%d"), ptofn.GetPath());
    wxString cmd;
    cmd.Printf(wxT("%s %s"), wxQuoteFilename(autopanoExe).c_str(), autopanoArgs.c_str());
#ifdef __WXMSW__
    if (cmd.size() > 32766) {
        CPMessage(_("Command line for control point detector too long.\nThis is a Windows limitation\nPlease select less images, or place the images in a folder with\na shorter pathname"),
                     _("Too many images selected"), parent);
        return cps;
    }
#endif
    DEBUG_DEBUG("Executing: " << cmd.c_str());

    wxArrayString arguments = wxCmdLineParser::ConvertStringToArgs(cmd);
    if (arguments.GetCount() > 127) {
        DEBUG_ERROR("Too many arguments for call to wxExecute()");
        DEBUG_ERROR("Try using the %s parameter in preferences");
        CPMessage(wxString::Format(_("Too many arguments (images). Try using the %%s parameter in preferences.\n\n Could not execute command: %s"), autopanoExe.c_str()), _("wxExecute Error"), parent);
        return cps;
    }

    ret_value = 0;
    // use MyExternalCmdExecDialog
    ret_value = CPExecute(autopanoExe, autopanoArgs, _("finding control points"), parent);

    if (ret_value == HUGIN_EXIT_CODE_CANCELLED) {
        return cps;
    } else if (ret_value == -1) {
        CPMessage( wxString::Format(_("Could not execute command: %s"),cmd.c_str()), _("wxExecute Error"),  parent);
        return cps;
    } else if (ret_value > 0) {
        CPMessage(wxString::Format(_("Command: %s\nfailed with error code: %d"),cmd.c_str(),ret_value),
                     _("wxExecute Error"), parent);
        return cps;
    }

    ptofile = ptofn.GetFullPath();
    ptofile.append(wxT("0.oto"));
    if (! wxFileExists(ptofile.c_str()) ) {
        CPMessage(wxString::Format(_("Could not open %s for reading\nThis is an indicator that the control point detector call failed,\nor incorrect command line parameters have been used.\n\nExecuted command: %s"),ptofile.c_str(),cmd.c_str()),
                     _("Control point detector failure"), parent );
        return cps;
    }
    // read and update control points
    cps = readUpdatedControlPoints((const char *)ptofile.mb_str(HUGIN_CONV_FILENAME), pano);

    if (!wxRemoveFile(ptofile)) {
        DEBUG_DEBUG("could not remove temporary file: " << ptofile.c_str());
    }
    return cps;
}
CPVector AutoPanoSift::automatch(CPDetectorSetting &setting, Panorama & pano, const UIntSet & imgs,
                                     int nFeatures, int & ret_value, wxWindow *parent)
{
    CPVector cps;
    if (imgs.size() == 0) {
        return cps;
    }
    // create suitable command line..
    wxString autopanoExe = GetProgPath(setting.GetProg());
    if(setting.IsTwoStepDetector())
    {
        std::vector<wxString> keyFiles(pano.getNrOfImages());
        cps=automatch(setting, pano, imgs, nFeatures, keyFiles, ret_value, parent);
        Cleanup(setting, pano, imgs, keyFiles, parent);
        return cps;
    };
    wxString autopanoArgs = setting.GetArgs();
    
    // TODO: create a secure temporary filename here
    wxString ptofile = wxFileName::CreateTempFileName(wxT("ap_res"));
    autopanoArgs.Replace(wxT("%o"), ptofile);
    wxString tmp;
    tmp.Printf(wxT("%d"), nFeatures);
    autopanoArgs.Replace(wxT("%p"), tmp);

    SrcPanoImage firstImg = pano.getSrcImage(*imgs.begin());
    tmp.Printf(wxT("%f"), firstImg.getHFOV());
    autopanoArgs.Replace(wxT("%v"), tmp);

    tmp.Printf(wxT("%d"), (int) firstImg.getProjection());
    autopanoArgs.Replace(wxT("%f"), tmp);

    long idx = autopanoArgs.Find(wxT("%namefile")) ;
    DEBUG_DEBUG("find %namefile in '"<< autopanoArgs.mb_str(wxConvLocal) << "' returned: " << idx);
    bool use_namefile = idx >=0;
    idx = autopanoArgs.Find(wxT("%i"));
    DEBUG_DEBUG("find %i in '"<< autopanoArgs.mb_str(wxConvLocal) << "' returned: " << idx);
    bool use_params = idx >=0;
    idx = autopanoArgs.Find(wxT("%s"));
    bool use_inputscript = idx >=0;

    if (! (use_namefile || use_params || use_inputscript)) {
        CPMessage(_("Please use %namefile, %i or %s to specify the input files for the control point detector"),
                     _("Error in control point detector command"), parent);
        return cps;
    }

    wxFile namefile;
    wxString namefile_name;
    if (use_namefile) {
        // create temporary file with image names.
        namefile_name = wxFileName::CreateTempFileName(wxT("ap_imgnames"), &namefile);
        DEBUG_DEBUG("before replace %namefile: " << autopanoArgs.mb_str(wxConvLocal));
        autopanoArgs.Replace(wxT("%namefile"), namefile_name);
        DEBUG_DEBUG("after replace %namefile: " << autopanoArgs.mb_str(wxConvLocal));
        for(UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); it++)
        {
            namefile.Write(wxString(pano.getImage(*it).getFilename().c_str(), HUGIN_CONV_FILENAME));
            namefile.Write(wxT("\r\n"));
        }
        // close namefile
        if (namefile_name != wxString(wxT(""))) {
            namefile.Close();
        }
    } else {
        string imgFiles;
        for(UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); it++)
        {
            imgFiles.append(" ").append(quoteFilename(pano.getImage(*it).getFilename()));
        }
        autopanoArgs.Replace(wxT("%i"), wxString (imgFiles.c_str(), HUGIN_CONV_FILENAME));
    }

    wxString ptoinfile_name;
    if (use_inputscript) {
        wxFile ptoinfile;
        ptoinfile_name = wxFileName::CreateTempFileName(wxT("ap_inproj"));
        autopanoArgs.Replace(wxT("%s"), ptoinfile_name);

        ofstream ptoinstream(ptoinfile_name.mb_str(wxConvFile));
        //delete all existing control points in temp project
        //otherwise the existing control points will be loaded again
        Panorama tempPano=pano.duplicate();
        CPVector emptyCPV;
        tempPano.setCtrlPoints(emptyCPV);
        tempPano.printPanoramaScript(ptoinstream, tempPano.getOptimizeVector(), tempPano.getOptions(), imgs, false);
    }

#ifdef __WXMSW__
    if (autopanoArgs.size() > 32000) {
        CPMessage(_("Command line for control point detector too long.\nThis is a Windows limitation\nPlease select less images, or place the images in a folder with\na shorter pathname"),
                     _("Too many images selected"), parent );
        return cps;
    }
#endif

    wxString cmd = autopanoExe + wxT(" ") + autopanoArgs;
    DEBUG_DEBUG("Executing: " << autopanoExe.mb_str(wxConvLocal) << " " << autopanoArgs.mb_str(wxConvLocal));

    wxArrayString arguments = wxCmdLineParser::ConvertStringToArgs(autopanoArgs);
    if (arguments.GetCount() > 127) {
        DEBUG_ERROR("Too many arguments for call to wxExecute()");
        DEBUG_ERROR("Try using the %%s parameter in preferences");
        CPMessage(wxString::Format(_("Too many arguments (images). Try using the %%s parameter in preferences.\n\n Could not execute command: %s"), autopanoExe.c_str()), _("wxExecute Error"), parent);
        return cps;
    }

    ret_value = 0;
    // use MyExternalCmdExecDialog
    ret_value = CPExecute(autopanoExe, autopanoArgs, _("finding control points"), parent);

    if (ret_value == HUGIN_EXIT_CODE_CANCELLED) {
        return cps;
    } else if (ret_value == -1) {
        CPMessage( wxString::Format(_("Could not execute command: %s"),cmd.c_str()), _("wxExecute Error"), parent);
        return cps;
    } else if (ret_value > 0) {
        CPMessage(wxString::Format(_("Command: %s\nfailed with error code: %d"),cmd.c_str(),ret_value),
                     _("wxExecute Error"), parent);
        return cps;
    }

    if (! wxFileExists(ptofile.c_str())) {
        CPMessage(wxString::Format(_("Could not open %s for reading\nThis is an indicator that the control point detector call failed,\nor incorrect command line parameters have been used.\n\nExecuted command: %s"),ptofile.c_str(),cmd.c_str()),
                     _("Control point detector failure"), parent );
        return cps;
    }

    // read and update control points
    if(use_inputscript)
    {
        cps = readUpdatedControlPoints((const char*)ptofile.mb_str(HUGIN_CONV_FILENAME), pano, imgs);
    }
    else
    {
        cps = readUpdatedControlPoints((const char *)ptofile.mb_str(HUGIN_CONV_FILENAME), pano);
    };

    if (namefile_name != wxString(wxT(""))) {
        namefile.Close();
        wxRemoveFile(namefile_name);
    }

    if (ptoinfile_name != wxString(wxT(""))) {
        wxRemoveFile(ptoinfile_name);
    }

    if (!wxRemoveFile(ptofile)) {
        DEBUG_DEBUG("could not remove temporary file: " << ptofile.c_str());
    }

    return cps;
}
Пример #8
0
int main(int argc, char* argv[])
{
    // parse arguments
    const char* optstring = "o:p:f:c:s:lh";

    static struct option longOptions[] =
    {
        {"output", required_argument, NULL, 'o' },
        {"projection", required_argument, NULL, 'p' },
        {"fov", required_argument, NULL, 'f' },
        {"crop", required_argument, NULL, 'c' },
        {"stacklength", required_argument, NULL, 's' },
        {"linkstacks", no_argument, NULL, 'l' },
        {"distortion", no_argument, NULL, 300 },
        {"vignetting", no_argument, NULL, 301 },
        {"help", no_argument, NULL, 'h' },

        0
    };

    int c;
    int optionIndex = 0;
    string output;
    int projection=-1;
    float fov=-1;
    int stackLength=1;
    bool linkStacks=false;
    vigra::Rect2D cropRect(0,0,0,0);
    bool loadDistortion=false;
    bool loadVignetting=false;
    while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
    {
        switch (c)
        {
            case 'o':
                output = optarg;
                break;
            case 'h':
                usage(argv[0]);
                return 0;
            case 'p':
                {
                    projection=atoi(optarg);
                    if((projection==0) && (strcmp(optarg,"0")!=0))
                    {
                        cerr << "Could not parse image number.";
                        return 1;
                    };
                    if(projection<0)
                    {
                        cerr << "Invalid projection number." << endl;
                        return 1;
                    };
                };
                break;
            case 'f':
                fov=atof(optarg);
                if(fov<1 || fov>360)
                {
                    cerr << "Invalid field of view";
                    return 1;
                };
                break;
            case 'c':
                {
                    int left, right, top, bottom;
                    int n=sscanf(optarg, "%d,%d,%d,%d", &left, &right, &top, &bottom);
                    if (n==4)
                    {
                        if(right>left && bottom>top)
                        {
                            cropRect.setUpperLeft(vigra::Point2D(left,top));
                            cropRect.setLowerRight(vigra::Point2D(right,bottom));
                        }
                        else
                        {
                            cerr << "Invalid crop area" << endl;
                            return 1;
                        };
                    }
                    else
                    {
                        cerr << "Could not parse crop values" << endl;
                        return 1;
                    };
                };
                break;
            case 's':
                stackLength=atoi(optarg);
                if(stackLength<1)
                {
                    cerr << "Could not parse stack length." << endl;
                    return 1;
                };
                break;
            case 'l':
                linkStacks=true;
                break;
            case 300:
                loadDistortion=true;
                break;
            case 301:
                loadVignetting=true;
                break;
            case ':':
                cerr <<"Option " << longOptions[optionIndex].name << " requires a number" << endl;
                return 1;
                break;
            case '?':
                break;
            default:
                abort ();
        }
    }

    if (argc - optind < 1)
    {
        usage(argv[0]);
        return 1;
    };

    cout << "Generating pto file..." << endl;
    cout.flush();

    std::vector<string> filelist;
    while(optind<argc)
    {
        string input;
#ifdef _WINDOWS
        //do globbing
        input=GetAbsoluteFilename(argv[optind]);
        char drive[_MAX_DRIVE];
        char dir[_MAX_DIR];
        char fname[_MAX_FNAME];
        char ext[_MAX_EXT];
        char newFile[_MAX_PATH];

        _splitpath(input.c_str(), drive, dir, NULL, NULL);

        struct _finddata_t finddata;
        intptr_t findhandle = _findfirst(input.c_str(), &finddata);
        if (findhandle != -1)
        {
            do
            {
                //ignore folder, can be happen when using *.*
                if((finddata.attrib & _A_SUBDIR)==0)
                {
                    _splitpath(finddata.name, NULL, NULL, fname, ext);
                    _makepath(newFile, drive, dir, fname, ext);
                    //check if valid image file
                    if(vigra::isImage(newFile))
                    {
                        filelist.push_back(std::string(newFile));
                    };
                };
            }
            while (_findnext(findhandle, &finddata) == 0);
            _findclose(findhandle);
        }
#else
        input=argv[optind];
        if(hugin_utils::FileExists(input))
        {
            if(vigra::isImage(input.c_str()))
            {
                filelist.push_back(GetAbsoluteFilename(input));
            };
        };
#endif
        optind++;
    };

    if(filelist.size()==0)
    {
        cerr << "No valid image files given." << endl;
        return 1;
    };

    //sort filenames
    sort(filelist.begin(),filelist.end(),doj::alphanum_less());

    if(projection<0)
    {
        InitLensDB();
    };

    Panorama pano;
    for(size_t i=0; i<filelist.size();i++)
    {
        SrcPanoImage srcImage;
        cout << "Reading " << filelist[i] << "..." << endl;
        srcImage.setFilename(filelist[i]);
        try
        {
            vigra::ImageImportInfo info(filelist[i].c_str());
            if(info.width()==0 || info.height()==0)
            {
                cerr << "ERROR: Could not decode image " << filelist[i] << endl
                     << "Skipping this image." << endl << endl;
                continue;
            }
            srcImage.setSize(info.size());
            std::string pixelType=info.getPixelType();
            if((pixelType=="UINT8") || (pixelType=="UINT16") || (pixelType=="INT16"))
            {
                srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_EMOR);
            }
            else
            {
                srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_LINEAR);
            };
        }
        catch(std::exception & e)
        {
            cerr << "ERROR: caught exception: " << e.what() << endl;
            cerr << "Could not read image information for file " << filelist[i] << endl;
            cerr << "Skipping this image." << endl << endl;
            continue;
        };

        srcImage.readEXIF();
        srcImage.applyEXIFValues();
        if(projection>=0)
        {
            srcImage.setProjection((HuginBase::BaseSrcPanoImage::Projection)projection);
        }
        else
        {
            srcImage.readProjectionFromDB();
        };
        if(fov>0)
        {
            srcImage.setHFOV(fov);
            if(srcImage.getCropFactor()==0)
            {
                srcImage.setCropFactor(1.0);
            };
        }
        else
        {
            //set plausible default value if they could not read from exif
            if(srcImage.getExifFocalLength()==0 || srcImage.getCropFactor()==0)
            {
                cout << "\tNo value for field of view found in EXIF data. " << endl
                     << "\tAssuming a HFOV of 50 degrees. " << endl;
                srcImage.setHFOV(50);
                srcImage.setCropFactor(1.0);
            };
        };
        if(cropRect.width()>0 && cropRect.height()>0)
        {
            if(srcImage.isCircularCrop())
            {
                srcImage.setCropMode(SrcPanoImage::CROP_CIRCLE);
            }
            else
            {
                srcImage.setCropMode(SrcPanoImage::CROP_RECTANGLE);
            };
            srcImage.setAutoCenterCrop(false);
            srcImage.setCropRect(cropRect);
        };
        if(loadDistortion)
        {
            if(srcImage.readDistortionFromDB())
            {
                cout << "\tRead distortion data from lensfun database." << endl;
            }
            else
            {
                cout << "\tNo valid distortion data found in lensfun database." << endl;
            };
        };
        if(loadVignetting)
        {
            if(srcImage.readVignettingFromDB())
            {
                cout << "\tRead vignetting data from lensfun database." << endl;
            }
            else
            {
                cout << "\tNo valid vignetting data found in lensfun database." << endl;
            };
        };

        pano.addImage(srcImage);
    };

    if(pano.getNrOfImages()==0)
    {
        cerr << "Adding images to project files failed." << endl;
        HuginBase::LensDB::LensDB::Clean();
        return 1;
    };

    //link lenses
    if(pano.getNrOfImages()>1)
    {
        double redBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifRedBalance();
        double blueBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifBlueBalance();
        if(fabs(redBalanceAnchor)<1e-2)
        {
            redBalanceAnchor=1;
        };
        if(fabs(blueBalanceAnchor)<1e-2)
        {
            blueBalanceAnchor=1;
        };
        StandardImageVariableGroups variable_groups(pano);
        ImageVariableGroup& lenses = variable_groups.getLenses();

        for(size_t i=1;i<pano.getNrOfImages();i++)
        {
            int image=-1;
            const SrcPanoImage & srcImg=pano.getImage(i);
            for(size_t j=0;j<i;j++)
            {
                const SrcPanoImage & compareImg=pano.getImage(j);
                if(srcImg.getHFOV()==compareImg.getHFOV() &&
                    srcImg.getProjection()==compareImg.getProjection() &&
                    srcImg.getExifModel()==compareImg.getExifModel() &&
                    srcImg.getExifMake()==compareImg.getExifMake() &&
                    srcImg.getSize()==compareImg.getSize())
                {
                    image=j;
                    break;
                };
            };
            if(image!=-1)
            {
                SrcPanoImage img=pano.getSrcImage(i);
                double ev=img.getExposureValue();
                lenses.switchParts(i,lenses.getPartNumber(image));
                lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_ExposureValue, i);
                img.setExposureValue(ev);
                lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceRed, i);
                lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceBlue, i);
                img.setWhiteBalanceRed(img.getExifRedBalance()/redBalanceAnchor);
                img.setWhiteBalanceBlue(img.getExifBlueBalance()/blueBalanceAnchor);
                pano.setSrcImage(i, img);
            };
        };
        cout << endl << "Assigned " << lenses.getNumberOfParts() << " lenses." << endl;
        if(lenses.getNumberOfParts()>1 && stackLength>1)
        {
            cout << "Project contains more than one lens, but you requested to assign" << endl
                 << "stacks. This is not supported. Therefore stacks will not be" << endl
                 << "assigned." << endl << endl;
            stackLength=1;
        };
    };

    //link stacks
    if(pano.getNrOfImages()>1 && stackLength>1)
    {
        stackLength=std::min<int>(stackLength,pano.getNrOfImages());
        int stackCount=pano.getNrOfImages() / stackLength;
        if(pano.getNrOfImages() % stackLength > 0)
        {
            stackCount++;
        };
        if(stackCount<pano.getNrOfImages())
        {
            for(size_t stackNr=0;stackNr<stackCount;stackNr++)
            {
                size_t firstImgStack=stackNr*stackLength;
                for(size_t i=0;i<stackLength;i++)
                {
                    if(firstImgStack+i<pano.getNrOfImages())
                    {
                        pano.linkImageVariableStack(firstImgStack,firstImgStack+i);
                        if(linkStacks)
                        {
                            pano.linkImageVariableYaw(firstImgStack,firstImgStack+i);
                            pano.linkImageVariablePitch(firstImgStack,firstImgStack+i);
                            pano.linkImageVariableRoll(firstImgStack,firstImgStack+i);
                        };
                    };
                };
            };
            cout << "Assigned " << stackCount << " stacks." << endl;
        };
    };

    //set output exposure value
    PanoramaOptions opt = pano.getOptions();
    opt.outputExposureValue = CalculateMeanExposure::calcMeanExposure(pano);
    pano.setOptions(opt);
    // set optimizer switches
    pano.setOptimizerSwitch(HuginBase::OPT_PAIR);
    pano.setPhotometricOptimizerSwitch(HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);

    //output
    if(output=="")
    {
        output=hugin_utils::stripExtension(pano.getImage(0).getFilename());
        if(pano.getNrOfImages()>1)
        {
            output.append("-");
            output.append(hugin_utils::stripExtension(hugin_utils::stripPath(pano.getImage(pano.getNrOfImages()-1).getFilename())));
        };
        output=output.append(".pto");
    };
    output=GetAbsoluteFilename(output);
    //write output
    UIntSet imgs;
    fill_set(imgs,0, pano.getNrOfImages()-1);
    ofstream of(output.c_str());
    pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(output));

    cout << endl << "Written output to " << output << endl;
    HuginBase::LensDB::LensDB::Clean();
    return 0;
}
int main(int argc, char* argv[])
{
    // parse arguments
    const char* optstring = "o:t:h";
    enum
    {
        MINOVERLAP=1000
    };

    static struct option longOptions[] =
    {
        {"output", required_argument, NULL, 'o' },
        {"template", required_argument, NULL, 't'},
        {"help", no_argument, NULL, 'h' },
        0
    };

    int c;
    int optionIndex = 0;
    string output;
    string templateFile;
    while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
    {
        switch (c)
        {
            case 'o':
                output = optarg;
                break;
            case 't':
                templateFile = optarg;
                if(!hugin_utils::FileExists(templateFile))
                {
                    cerr << "Error: Template \"" << templateFile << "\" not found." << endl;
                    return 1;
                };
                break;
            case 'h':
                usage(argv[0]);
                return 0;
            case ':':
                cerr <<"Option " << longOptions[optionIndex].name << " requires a parameter" << endl;
                return 1;
                break;
            case '?':
                break;
            default:
                abort ();
        }
    }

    if (argc - optind == 0)
    {
        cout << "Error: No project file given." << endl;
        return 1;
    };
    if (argc - optind != 1)
    {
        cout << "Error: pto_template can only work on one project file at one time" << endl;
        return 1;
    };
    if (templateFile.length()==0)
    {
        cerr << "Error: No template given." << endl;
        return 1;
    };

    string input=argv[optind];
    // read panorama
    Panorama pano;
    ifstream prjfile(input.c_str());
    if (!prjfile.good())
    {
        cerr << "Error: could not open script : " << input << endl;
        return 1;
    }
    pano.setFilePrefix(hugin_utils::getPathPrefix(input));
    DocumentData::ReadWriteError err = pano.readData(prjfile);
    if (err != DocumentData::SUCCESSFUL)
    {
        cerr << "Error while parsing panos tool script: " << input << endl;
        cerr << "DocumentData::ReadWriteError code: " << err << endl;
        return 1;
    }

    if(pano.getNrOfImages()==0)
    {
        cerr << "Error: project file does not contains any image" << endl;
        cerr << "aborting processing" << endl;
        return 1;
    };

    Panorama newPano;
    ifstream templateStream(templateFile.c_str());
    if (!templateStream.good())
    {
        cerr << "Error: could not open template script : " << templateFile << endl;
        return 1;
    }
    newPano.setFilePrefix(hugin_utils::getPathPrefix(templateFile));
    err = newPano.readData(templateStream);
    if (err != DocumentData::SUCCESSFUL)
    {
        cerr << "Error while parsing template script: " << templateFile << endl;
        cerr << "DocumentData::ReadWriteError code: " << err << endl;
        return 1;
    }

    if (pano.getNrOfImages() != newPano.getNrOfImages())
    {
        cerr << "Error: template expects " << newPano.getNrOfImages() << " images," << endl
             << "       current project contains " << pano.getNrOfImages() << " images" << endl
             << "       Could not apply template" << endl;
        return false;
    }

    // check image sizes, and correct parameters if required.
    for (unsigned int i = 0; i < newPano.getNrOfImages(); i++)
    {
        // check if image size is correct
        const SrcPanoImage & oldSrcImg = pano.getImage(i);
        SrcPanoImage newSrcImg = newPano.getSrcImage(i);

        // just keep the file name
        newSrcImg.setFilename(oldSrcImg.getFilename());
        if (oldSrcImg.getSize() != newSrcImg.getSize())
        {
            // adjust size properly.
            newSrcImg.resize(oldSrcImg.getSize());
        }
        newPano.setSrcImage(i, newSrcImg);
    }
    // keep old control points.
    newPano.setCtrlPoints(pano.getCtrlPoints());

    //write output
    UIntSet imgs;
    fill_set(imgs, 0, newPano.getNrOfImages()-1);
    // Set output .pto filename if not given
    if (output=="")
    {
        output=input.substr(0,input.length()-4).append("_template.pto");
    }
    ofstream of(output.c_str());
    newPano.printPanoramaScript(of, newPano.getOptimizeVector(), newPano.getOptions(), imgs, false, hugin_utils::getPathPrefix(input));

    cout << endl << "Written output to " << output << endl;
    return 0;
}