void AssistantPanel::OnCreate( wxCommandEvent & e ) { // just run the stitcher // this is kind of a bad hack, since several settings are determined // based on the current state of PanoPanel, and not the Panorama object itself // calc optimal size using output projection double sizeFactor = HUGIN_ASS_PANO_DOWNSIZE_FACTOR; wxConfigBase::Get()->Read(wxT("/Assistant/panoDownsizeFactor"), &sizeFactor, HUGIN_ASS_PANO_DOWNSIZE_FACTOR); PanoramaOptions opts = m_pano->getOptions(); int w = m_pano->calcOptimalWidth(); // check if resize was plausible! if (w> 0) { opts.setWidth(floori(w*sizeFactor), true); // copy information into our panorama GlobalCmdHist::getInstance().addCommand( new PT::SetPanoOptionsCmd(*m_pano, opts) ); } wxCommandEvent dummy; MainFrame::Get()->OnDoStitch(dummy); }
int main(int argc, char *argv[]) { // parse arguments const char * optstring = "alho:npqsv:m"; int c; string output; bool doPairwise = false; bool doAutoOpt = false; bool doNormalOpt = false; bool doLevel = false; bool chooseProj = false; bool quiet = false; bool doPhotometric = false; double hfov = 0.0; while ((c = getopt (argc, argv, optstring)) != -1) { switch (c) { case 'o': output = optarg; break; case 'h': usage(argv[0]); return 0; case 'p': doPairwise = true; break; case 'a': doAutoOpt = true; break; case 'n': doNormalOpt = true; break; case 'l': doLevel = true; break; case 's': chooseProj = true; break; case 'q': quiet = true; break; case 'v': hfov = atof(optarg); break; case 'm': doPhotometric = true; break; default: abort (); } } if (argc - optind != 1) { usage(argv[0]); return 1; } const char * scriptFile = argv[optind]; Panorama pano; if (scriptFile[0] == '-') { DocumentData::ReadWriteError err = pano.readData(std::cin); if (err != DocumentData::SUCCESSFUL) { cerr << "error while reading script file from stdin." << endl; cerr << "DocumentData::ReadWriteError code: " << err << endl; return 1; } } else { ifstream prjfile(scriptFile); if (!prjfile.good()) { cerr << "could not open script : " << scriptFile << endl; return 1; } pano.setFilePrefix(hugin_utils::getPathPrefix(scriptFile)); DocumentData::ReadWriteError err = pano.readData(prjfile); if (err != DocumentData::SUCCESSFUL) { cerr << "error while parsing panos tool script: " << scriptFile << endl; cerr << "DocumentData::ReadWriteError code: " << err << endl; return 1; } } if (pano.getNrOfImages() == 0) { cerr << "Panorama should consist of at least one image" << endl; return 1; } // for bad HFOV (from autopano-SIFT) for (unsigned i=0; i < pano.getNrOfImages(); i++) { SrcPanoImage img = pano.getSrcImage(i); if (img.getProjection() == SrcPanoImage::RECTILINEAR && img.getHFOV() >= 180) { // something is wrong here, try to read from exif data double focalLength = 0; double cropFactor = 0; cerr << "HFOV of image " << img.getFilename() << " invalid, trying to read EXIF tags" << endl; bool ok = img.readEXIF(focalLength, cropFactor, true, false); if (! ok) { if (hfov) { img.setHFOV(hfov); } else { cerr << "EXIF reading failed, please specify HFOV with -v" << endl; return 1; } } pano.setSrcImage(i, img); } } if(pano.getNrOfCtrlPoints()==0 && (doPairwise || doAutoOpt || doNormalOpt)) { cerr << "Panorama have to have control points to optimise positions" << endl; return 1; }; if (doPairwise && ! doAutoOpt) { // do pairwise optimisation set<string> optvars; optvars.insert("r"); optvars.insert("p"); optvars.insert("y"); AutoOptimise::autoOptimise(pano); // do global optimisation if (!quiet) std::cerr << "*** Pairwise position optimisation" << endl; PTools::optimize(pano); } else if (doAutoOpt) { if (!quiet) std::cerr << "*** Adaptive geometric optimisation" << endl; SmartOptimise::smartOptimize(pano); } else if (doNormalOpt) { if (!quiet) std::cerr << "*** Optimising parameters specified in PTO file" << endl; PTools::optimize(pano); } else { if (!quiet) std::cerr << "*** Geometric parameters not optimized" << endl; } if (doLevel) { bool hasVerticalLines=false; CPVector allCP=pano.getCtrlPoints(); if(allCP.size()>0 && (doPairwise || doAutoOpt || doNormalOpt)) { for(size_t i=0;i<allCP.size() && !hasVerticalLines;i++) { hasVerticalLines=(allCP[i].mode==ControlPoint::X); }; }; // straighten only if there are no vertical control points if(hasVerticalLines) { cout << "Skipping automatic leveling because of existing vertical control points." << endl; } else { StraightenPanorama(pano).run(); CenterHorizontally(pano).run(); }; } if (chooseProj) { PanoramaOptions opts = pano.getOptions(); double hfov, vfov; CalculateFitPanorama fitPano = CalculateFitPanorama(pano); fitPano.run(); opts.setHFOV(fitPano.getResultHorizontalFOV()); opts.setHeight(roundi(fitPano.getResultHeight())); vfov = opts.getVFOV(); hfov = opts.getHFOV(); // avoid perspective projection if field of view > 100 deg double mf = 100; if (vfov < mf) { // cylindrical or rectilinear if (hfov < mf) { opts.setProjection(PanoramaOptions::RECTILINEAR); } else { opts.setProjection(PanoramaOptions::CYLINDRICAL); } } // downscale pano a little double sizeFactor = 0.7; pano.setOptions(opts); double w = CalculateOptimalScale::calcOptimalScale(pano); opts.setWidth(roundi(opts.getWidth()*w*sizeFactor), true); pano.setOptions(opts); } if(doPhotometric) { // photometric estimation PanoramaOptions opts = pano.getOptions(); int nPoints = 200; int pyrLevel=3; bool randomPoints = true; nPoints = nPoints * pano.getNrOfImages(); std::vector<vigra_ext::PointPairRGB> points; ProgressDisplay *progressDisplay; if(!quiet) progressDisplay=new StreamProgressDisplay(std::cout); else progressDisplay=new DummyProgressDisplay(); try { loadImgsAndExtractPoints(pano, nPoints, pyrLevel, randomPoints, *progressDisplay, points, !quiet); } catch (std::exception & e) { cerr << "caught exception: " << e.what() << endl; return 1; }; if(!quiet) cout << "\rSelected " << points.size() << " points" << endl; if (points.size() == 0) { cerr << "Error: no overlapping points found, exiting" << endl; return 1; } progressDisplay->startSubtask("Photometric Optimization", 0.0); // first, ensure that vignetting and response coefficients are linked 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 }; HuginBase::StandardImageVariableGroups variable_groups(pano); HuginBase::ImageVariableGroup & 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()) { std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it; for (it = links_needed.begin(); it != links_needed.end(); it++) { lenses.linkVariablePart(*it, i); } } } HuginBase::SmartPhotometricOptimizer::PhotometricOptimizeMode optmode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_LDR; if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) { optmode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_HDR; } SmartPhotometricOptimizer photoOpt(pano, progressDisplay, pano.getOptimizeVector(), points, optmode); photoOpt.run(); // calculate the mean exposure. opts.outputExposureValue = CalculateMeanExposure::calcMeanExposure(pano); pano.setOptions(opts); progressDisplay->finishSubtask(); delete progressDisplay; }; // write result OptimizeVector optvec = pano.getOptimizeVector(); UIntSet imgs; fill_set(imgs,0, pano.getNrOfImages()-1); if (output != "") { ofstream of(output.c_str()); pano.printPanoramaScript(of, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile)); } else { pano.printPanoramaScript(cout, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile)); } return 0; }
bool PanoDetector::matchMultiRow(PoolExecutor& aExecutor) { //step 1 std::vector<HuginBase::UIntSet> checkedImagePairs(_panoramaInfo->getNrOfImages()); std::vector<stack_img> stack_images; HuginBase::StandardImageVariableGroups* variable_groups = new HuginBase::StandardImageVariableGroups(*_panoramaInfo); for(unsigned int i=0; i<_panoramaInfo->getNrOfImages(); i++) { unsigned int stack_nr=variable_groups->getStacks().getPartNumber(i); //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=i; stack_images[index].images[new_image_index].ev=_panoramaInfo->getImage(i).getExposure(); }; delete variable_groups; //get image with median exposure for search with cp generator vector<size_t> images_layer; UIntSet images_layer_set; 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.push_back(stack_images[i].images[index].img_nr); images_layer_set.insert(stack_images[i].images[index].img_nr); if(stack_images[i].images.size()>1) { //build match list for stacks for(unsigned int j=0; j<stack_images[i].images.size()-1; j++) { size_t img1=stack_images[i].images[j].img_nr; size_t img2=stack_images[i].images[j+1].img_nr; _matchesData.push_back(MatchData()); MatchData& aM=_matchesData.back(); aM._i1=&(_filesData[img1]); aM._i2=&(_filesData[img2]); checkedImagePairs[img1].insert(img2); checkedImagePairs[img2].insert(img1); }; }; }; //build match data list for image pairs if(images_layer.size()>1) { std::sort(images_layer.begin(), images_layer.end()); for(unsigned int i=0; i<images_layer.size()-1; i++) { size_t img1=images_layer[i]; size_t img2=images_layer[i+1]; _matchesData.push_back(MatchData()); MatchData& aM = _matchesData.back(); aM._i1 = &(_filesData[img1]); aM._i2 = &(_filesData[img2]); checkedImagePairs[img1].insert(img2); checkedImagePairs[img2].insert(img1); }; }; TRACE_INFO(endl<< "--- Find matches ---" << endl); try { BOOST_FOREACH(MatchData& aMD, _matchesData) aExecutor.execute(new MatchDataRunnable(aMD, *this)); aExecutor.wait(); } catch(Synchronization_Exception& e) { TRACE_ERROR(e.what() << endl); return false; } // Add detected matches to _panoramaInfo BOOST_FOREACH(MatchData& aM, _matchesData) BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches) _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y, aM._i2->_number, aPM->_img2_x, aPM->_img2_y)); // step 2: connect all image groups _matchesData.clear(); Panorama mediumPano=_panoramaInfo->getSubset(images_layer_set); CPGraph graph; createCPGraph(mediumPano, graph); CPComponents comps; unsigned int n = findCPComponents(graph, comps); if(n>1) { vector<unsigned int> ImagesGroups; for(unsigned int i=0; i<n; i++) { ImagesGroups.push_back(images_layer[*(comps[i].begin())]); if(comps[i].size()>1) { ImagesGroups.push_back(images_layer[*(comps[i].rbegin())]); } }; for(unsigned int i=0; i<ImagesGroups.size()-1; i++) { for(unsigned int j=i+1; j<ImagesGroups.size(); j++) { size_t img1=ImagesGroups[i]; size_t img2=ImagesGroups[j]; //skip already checked image pairs if(set_contains(checkedImagePairs[img1],img2)) { continue; }; _matchesData.push_back(MatchData()); MatchData& aM = _matchesData.back(); aM._i1 = &(_filesData[img1]); aM._i2 = &(_filesData[img2]); checkedImagePairs[img1].insert(img2); checkedImagePairs[img2].insert(img1); }; }; TRACE_INFO(endl<< "--- Find matches in images groups ---" << endl); try { BOOST_FOREACH(MatchData& aMD, _matchesData) aExecutor.execute(new MatchDataRunnable(aMD, *this)); aExecutor.wait(); } catch(Synchronization_Exception& e) { TRACE_ERROR(e.what() << endl); return false; } BOOST_FOREACH(MatchData& aM, _matchesData) BOOST_FOREACH(lfeat::PointMatchPtr& aPM, aM._matches) _panoramaInfo->addCtrlPoint(ControlPoint(aM._i1->_number, aPM->_img1_x, aPM->_img1_y, aM._i2->_number, aPM->_img2_x, aPM->_img2_y)); }; // step 3: now connect all overlapping images _matchesData.clear(); PT::Panorama optPano=_panoramaInfo->getSubset(images_layer_set); createCPGraph(optPano, graph); if(findCPComponents(graph, comps)==1) { if(images_layer.size()>2) { //reset translation parameters VariableMapVector varMapVec=optPano.getVariables(); for(size_t i=0; i<varMapVec.size(); i++) { map_get(varMapVec[i], "TrX").setValue(0); map_get(varMapVec[i], "TrY").setValue(0); map_get(varMapVec[i], "TrZ").setValue(0); }; optPano.updateVariables(varMapVec); //next steps happens only when all images are connected; //now optimize panorama PanoramaOptions opts = optPano.getOptions(); opts.setProjection(PanoramaOptions::EQUIRECTANGULAR); opts.optimizeReferenceImage=0; // calculate proper scaling, 1:1 resolution. // Otherwise optimizer distances are meaningless. opts.setWidth(30000, false); opts.setHeight(15000); optPano.setOptions(opts); int w = optPano.calcOptimalWidth(); opts.setWidth(w); opts.setHeight(w/2); optPano.setOptions(opts); //generate optimize vector, optimize only yaw and pitch OptimizeVector optvars; const SrcPanoImage& anchorImage = optPano.getImage(opts.optimizeReferenceImage); for (unsigned i=0; i < optPano.getNrOfImages(); i++) { std::set<std::string> imgopt; if(i==opts.optimizeReferenceImage) { //optimize only anchors pitch, not yaw imgopt.insert("p"); } else { imgopt.insert("p"); imgopt.insert("y"); }; optvars.push_back(imgopt); } optPano.setOptimizeVector(optvars); // remove vertical and horizontal control points CPVector cps = optPano.getCtrlPoints(); CPVector newCP; for (CPVector::const_iterator it = cps.begin(); it != cps.end(); it++) { if (it->mode == ControlPoint::X_Y) { newCP.push_back(*it); } } optPano.setCtrlPoints(newCP); if (getVerbose() < 2) { PT_setProgressFcn(ptProgress); PT_setInfoDlgFcn(ptinfoDlg); }; //in a first step do a pairwise optimisation HuginBase::AutoOptimise::autoOptimise(optPano, false); //now the final optimisation HuginBase::PTools::optimize(optPano); if (getVerbose() < 2) { PT_setProgressFcn(NULL); PT_setInfoDlgFcn(NULL); }; //now match overlapping images if(!matchPrealigned(aExecutor, &optPano, checkedImagePairs, images_layer, false)) { return false; }; }; } else { if(!match(aExecutor, checkedImagePairs)) { return false; }; }; return true; };
SmallRemappedImageCache::MRemappedImage * SmallRemappedImageCache::getRemapped(const PanoramaData& pano, const PanoramaOptions & popts, unsigned int imgNr, vigra::Rect2D outputROI, AppBase::MultiProgressDisplay& progress) { // always map to HDR mode. curve and exposure is applied in preview window, for speed PanoramaOptions opts = popts; opts.outputMode = PanoramaOptions::OUTPUT_HDR; opts.outputExposureValue = 0.0; // return old image, if already in cache and if it has changed since the last rendering if (set_contains(m_images, imgNr)) { // return cached image if the parameters of the image have not changed SrcPanoImage oldParam = m_imagesParam[imgNr]; if (oldParam == pano.getSrcImage(imgNr) && m_panoOpts[imgNr].getHFOV() == opts.getHFOV() && m_panoOpts[imgNr].getWidth() == opts.getWidth() && m_panoOpts[imgNr].getHeight() == opts.getHeight() && m_panoOpts[imgNr].getProjection() == opts.getProjection() && m_panoOpts[imgNr].getProjectionParameters() == opts.getProjectionParameters() ) { DEBUG_DEBUG("using cached remapped image " << imgNr); return m_images[imgNr]; } } ImageCache::getInstance().softFlush(); typedef BasicImageView<RGBValue<unsigned char> > BRGBImageView; // typedef NumericTraits<PixelType>::RealPromote RPixelType; // remap image DEBUG_DEBUG("remapping image " << imgNr); // load image const SrcPanoImage & img = pano.getImage(imgNr); ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); if ( (e->image8->width() == 0) && (e->image16->width() == 0) && (e->imageFloat->width() == 0) ) { throw std::runtime_error("could not retrieve small source image for preview generation"); } Size2D srcImgSize; if (e->image8->width() > 0) srcImgSize = e->image8->size(); else if (e->image16->width() > 0) srcImgSize = e->image16->size(); else srcImgSize = e->imageFloat->size(); MRemappedImage *remapped = new MRemappedImage; SrcPanoImage srcPanoImg = pano.getSrcImage(imgNr); // adjust distortion parameters for small preview image srcPanoImg.resize(srcImgSize); FImage srcFlat; // use complete image, by supplying an empty mask image BImage srcMask; if (img.getVigCorrMode() & SrcPanoImage::VIGCORR_FLATFIELD) { ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(img.getFlatfieldFilename().c_str()); if (!e) { throw std::runtime_error("could not retrieve flatfield image for preview generation"); } if (e->image8->width()) { srcFlat.resize(e->image8->size()); copyImage(srcImageRange(*(e->image8), RGBToGrayAccessor<RGBValue<UInt8> >()), destImage(srcFlat)); } else if (e->image16->width()) { srcFlat.resize(e->image16->size()); copyImage(srcImageRange(*(e->image16), RGBToGrayAccessor<RGBValue<vigra::UInt16> >()), destImage(srcFlat)); } else { srcFlat.resize(e->imageFloat->size()); copyImage(srcImageRange(*(e->imageFloat), RGBToGrayAccessor<RGBValue<float> >()), destImage(srcFlat)); } } progress.pushTask(AppBase::ProgressTask("remapping", "", 0)); // compute the bounding output rectangle here! vigra::Rect2D outROI = estimateOutputROI(pano, opts, imgNr); DEBUG_DEBUG("srcPanoImg size: " << srcPanoImg.getSize() << " pano roi:" << outROI); if (e->imageFloat->width()) { // remap image remapImage(*(e->imageFloat), srcMask, srcFlat, srcPanoImg, opts, outROI, *remapped, progress); } else if (e->image16->width()) { // remap image remapImage(*(e->image16), srcMask, srcFlat, srcPanoImg, opts, outROI, *remapped, progress); } else { remapImage(*(e->image8), srcMask, srcFlat, srcPanoImg, opts, outROI, *remapped, progress); } progress.popTask(); m_images[imgNr] = remapped; m_imagesParam[imgNr] = pano.getSrcImage(imgNr); m_panoOpts[imgNr] = opts; return remapped; }
FDiff2D CalculateFOV::calcFOV(const PanoramaData& panorama) { if (panorama.getNrOfImages() == 0) { // no change return FDiff2D(panorama.getOptions().getHFOV(), panorama.getOptions().getVFOV()); } vigra::Size2D panoSize(360*2,180*2); // remap into minature pano. PanoramaOptions opts; opts.setHFOV(360); opts.setProjection(PanoramaOptions::EQUIRECTANGULAR); opts.setWidth(panoSize.x); opts.setHeight(panoSize.y); // remap image // DGSW - make sure the type is correct vigra::BImage panoAlpha(panoSize.x, panoSize.y,static_cast< unsigned char >(0)); // vigra::BImage panoAlpha(panoSize.x, panoSize.y,0); Nona::RemappedPanoImage<vigra::BImage, vigra::BImage> remapped; UIntSet activeImgs = panorama.getActiveImages(); for (UIntSet::iterator it = activeImgs.begin(); it != activeImgs.end(); ++it) { // for (unsigned int imgNr=0; imgNr < getNrOfImages(); imgNr++) { // DGSW FIXME - Unreferenced // const PanoImage & img = getImage(*it); remapped.setPanoImage(panorama.getSrcImage(*it), opts, vigra::Rect2D(0,0,panoSize.x,panoSize.y)); //remapped.setPanoImage(*this, *it, vigra::Size2D(img.getWidth(), img.getHeight()), opts); // calculate alpha channel remapped.calcAlpha(); // copy into global alpha channel. vigra::copyImageIf(vigra_ext::applyRect(remapped.boundingBox(), vigra_ext::srcMaskRange(remapped)), vigra_ext::applyRect(remapped.boundingBox(), vigra_ext::srcMask(remapped)), vigra_ext::applyRect(remapped.boundingBox(), destImage(panoAlpha))); // vigra::ImageExportInfo imge2("c:/hugin_calcfov_alpha.png"); // exportImage(vigra::srcImageRange(panoAlpha), imge2); } // get field of view FDiff2D ul,lr; bool found = false; ul.x = DBL_MAX; ul.y = DBL_MAX; lr.x = -DBL_MAX; lr.y = -DBL_MAX; for (int v=0; v< panoSize.y; v++) { for (int h=0; h < panoSize.x; h++) { if (panoAlpha(h,v)) { // pixel is valid if ( ul.x > h ) { found=true; ul.x = h; } if ( ul.y > v ) { found=true; ul.y = v; } if ( lr.x < h) { found=true; lr.x = h; } if ( lr.y < v) { found=true; lr.y = v; } } } } if (!found) { // if nothing found, return current fov return FDiff2D(panorama.getOptions().getHFOV(), panorama.getOptions().getVFOV()); } ul=ul/2.0; lr=lr/2.0; ul.x = ul.x - 180; ul.y = ul.y - 90; lr.x = lr.x - 180; lr.y = lr.y - 90; FDiff2D fov (2*std::max(fabs(ul.x), fabs(lr.x)), 2*std::max(fabs(ul.y), fabs(lr.y))); if(fov.x<40) { fov.x+=1; }; return fov; }
CPVector AutoPanoSiftMultiRow::automatch(CPDetectorSetting &setting, Panorama & pano, const UIntSet & imgs, int nFeatures, int & ret_value, wxWindow *parent) { CPVector cps; if (imgs.size() < 2) { return cps; }; std::vector<wxString> keyFiles(pano.getNrOfImages()); //generate cp for every consecutive image pair unsigned int counter=0; for(UIntSet::const_iterator it = imgs.begin(); it != imgs.end(); ) { if(counter==imgs.size()-1) break; counter++; UIntSet ImagePair; ImagePair.clear(); ImagePair.insert(*it); it++; ImagePair.insert(*it); AutoPanoSift matcher; CPVector new_cps; new_cps.clear(); if(setting.IsTwoStepDetector()) new_cps=matcher.automatch(setting, pano, ImagePair, nFeatures, keyFiles, ret_value, parent); else new_cps=matcher.automatch(setting, pano, ImagePair, nFeatures, ret_value, parent); if(new_cps.size()>0) AddControlPointsWithCheck(cps,new_cps); if(ret_value!=0) { Cleanup(setting, pano, imgs, keyFiles, parent); return cps; }; }; // now connect all image groups // generate temporary panorama to add all found cps UIntSet allImgs; fill_set(allImgs, 0, pano.getNrOfImages()-1); Panorama optPano=pano.getSubset(allImgs); for (CPVector::const_iterator it=cps.begin();it!=cps.end();++it) optPano.addCtrlPoint(*it); CPGraph graph; createCPGraph(optPano, graph); CPComponents comps; int n = findCPComponents(graph, comps); if(n>1) { UIntSet ImagesGroups; for(unsigned int i=0;i<n;i++) { ImagesGroups.insert(*(comps[i].begin())); if(comps[i].size()>1) ImagesGroups.insert(*(comps[i].rbegin())); }; AutoPanoSift matcher; CPVector new_cps; if(setting.IsTwoStepDetector()) new_cps=matcher.automatch(setting, optPano, ImagesGroups, nFeatures, keyFiles, ret_value, parent); else new_cps=matcher.automatch(setting, optPano, ImagesGroups, nFeatures, ret_value, parent); if(new_cps.size()>0) AddControlPointsWithCheck(cps,new_cps,&optPano); if(ret_value!=0) { Cleanup(setting, pano, imgs, keyFiles, parent); return cps; }; createCPGraph(optPano,graph); n=findCPComponents(graph, comps); }; if(n==1 && setting.GetOption()) { //next steps happens only when all images are connected; //now optimize panorama PanoramaOptions opts = pano.getOptions(); opts.setProjection(PanoramaOptions::EQUIRECTANGULAR); // calculate proper scaling, 1:1 resolution. // Otherwise optimizer distances are meaningless. opts.setWidth(30000, false); opts.setHeight(15000); optPano.setOptions(opts); int w = optPano.calcOptimalWidth(); opts.setWidth(w); opts.setHeight(w/2); optPano.setOptions(opts); //generate optimize vector, optimize only yaw and pitch OptimizeVector optvars; const SrcPanoImage & anchorImage = optPano.getImage(opts.optimizeReferenceImage); for (unsigned i=0; i < optPano.getNrOfImages(); i++) { std::set<std::string> imgopt; if(i==opts.optimizeReferenceImage) { //optimize only anchors pitch, not yaw imgopt.insert("p"); } else { // do not optimize anchor image's stack for position. if(!optPano.getImage(i).YawisLinkedWith(anchorImage)) { imgopt.insert("p"); imgopt.insert("y"); }; }; optvars.push_back(imgopt); } optPano.setOptimizeVector(optvars); // remove vertical and horizontal control points CPVector backupOldCPS = optPano.getCtrlPoints(); CPVector backupNewCPS; for (CPVector::const_iterator it = backupOldCPS.begin(); it != backupOldCPS.end(); it++) { if (it->mode == ControlPoint::X_Y) { backupNewCPS.push_back(*it); } } optPano.setCtrlPoints(backupNewCPS); // do a first pairwise optimisation step HuginBase::AutoOptimise::autoOptimise(optPano,false); HuginBase::PTools::optimize(optPano); optPano.setCtrlPoints(backupOldCPS); //and find cp on overlapping images //work only on image pairs, which are not yet connected AutoPanoSiftPreAlign matcher; CPDetectorSetting newSetting; newSetting.SetProg(setting.GetProg()); newSetting.SetArgs(setting.GetArgs()); if(setting.IsTwoStepDetector()) { newSetting.SetProgMatcher(setting.GetProgMatcher()); newSetting.SetArgsMatcher(setting.GetArgsMatcher()); }; newSetting.SetOption(true); CPVector new_cps; if(setting.IsTwoStepDetector()) new_cps=matcher.automatch(newSetting, optPano, imgs, nFeatures, keyFiles, ret_value, parent); else new_cps=matcher.automatch(newSetting, optPano, imgs, nFeatures, ret_value, parent); if(new_cps.size()>0) AddControlPointsWithCheck(cps,new_cps); }; Cleanup(setting, pano, imgs, keyFiles, parent); return cps; };
void CenterHorizontally::centerHorizontically(PanoramaData& panorama) { vigra::Size2D panoSize(360,180); // remap into minature pano. PanoramaOptions opts; opts.setHFOV(360); opts.setProjection(PanoramaOptions::EQUIRECTANGULAR); opts.setWidth(360); opts.setHeight(180); // remap image vigra::BImage panoAlpha(panoSize); Nona::RemappedPanoImage<vigra::BImage, vigra::BImage> remapped; // use selected images. const UIntSet allActiveImgs(panorama.getActiveImages()); if (allActiveImgs.empty()) { // do nothing if there are no images return; } //only check unlinked images UIntSet activeImgs; for (UIntSet::const_iterator it = allActiveImgs.begin(); it!= allActiveImgs.end(); ++it) { const SrcPanoImage & img=panorama.getImage(*it); bool consider=true; if(img.YawisLinked()) { for(UIntSet::const_iterator it2=activeImgs.begin(); it2!=activeImgs.end(); ++it2) { if(img.YawisLinkedWith(panorama.getSrcImage(*it2))) { consider=false; break; }; }; }; if(consider) activeImgs.insert(*it); }; for (UIntSet::iterator it = activeImgs.begin(); it != activeImgs.end(); ++it) { remapped.setPanoImage(panorama.getSrcImage(*it), opts, vigra::Rect2D(0,0,360,180)); // calculate alpha channel remapped.calcAlpha(); // copy into global alpha channel. vigra::copyImageIf(vigra_ext::applyRect(remapped.boundingBox(), vigra_ext::srcMaskRange(remapped)), vigra_ext::applyRect(remapped.boundingBox(), vigra_ext::srcMask(remapped)), vigra_ext::applyRect(remapped.boundingBox(), destImage(panoAlpha))); } // get field of view std::vector<int> borders; bool colOccupied = false; for (int h=0; h < 360; h++) { bool curColOccupied = false; for (int v=0; v< 180; v++) { if (panoAlpha(h,v)) { // pixel is valid curColOccupied = true; } } if ((colOccupied && !curColOccupied) || (!colOccupied && curColOccupied)) { // change in position, save point. borders.push_back(h-180); colOccupied = curColOccupied; } } int lastidx = borders.size() -1; if (lastidx == -1) { // empty pano return; } if (colOccupied) { // we have reached the right border, and the pano is still valid // shift right fragments by 360 deg // |11 2222| -> | 222211 | std::vector<int> newBorders; newBorders.push_back(borders[lastidx]); for (int i = 0; i < lastidx; i++) { newBorders.push_back(borders[i]+360); } borders = newBorders; } const double dYaw=(borders[0] + borders[lastidx])/2; // apply yaw shift, takes also translation parameters into account RotatePanorama(panorama, -dYaw, 0, 0).run(); }