1 // -*- c-basic-offset: 4 -*- 2 /** @file PanoCommand.cpp 3 * 4 * @author Pablo d'Angelo <pablo.dangelo@web.de> 5 */ 6 /* 7 * This is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This software is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public 18 * License along with this software. If not, see 19 * <http://www.gnu.org/licenses/>. 20 * 21 */ 22 23 #include "PanoCommand.h" 24 #include <fstream> 25 #include <panotools/PanoToolsUtils.h> 26 27 #include <algorithms/nona/CenterHorizontally.h> 28 #include <algorithms/nona/FitPanorama.h> 29 #include <algorithms/basic/RotatePanorama.h> 30 #include <algorithms/basic/TranslatePanorama.h> 31 #include <algorithms/basic/CalculateMeanExposure.h> 32 #include <algorithms/basic/StraightenPanorama.h> 33 #include <panodata/ParseExp.h> 34 35 namespace PanoCommand 36 { ~CombinedPanoCommand()37 CombinedPanoCommand::~CombinedPanoCommand() 38 { 39 for (std::vector<PanoCommand*>::iterator it = commands.begin(); it != commands.end(); ++it) 40 { 41 delete *it; 42 } 43 } 44 processPanorama(HuginBase::Panorama & pano)45 bool CombinedPanoCommand::processPanorama(HuginBase::Panorama & pano) 46 { 47 bool result = true; 48 for (std::vector<PanoCommand*>::iterator it = commands.begin(); it != commands.end(); ++it) 49 { 50 result &= (**it).processPanorama(pano); 51 } 52 /// @todo Should I revert if processing fails? 53 return result; 54 }; 55 processPanorama(HuginBase::Panorama & pano)56 bool NewPanoCmd::processPanorama(HuginBase::Panorama& pano) 57 { 58 pano.reset(); 59 return true; 60 } 61 processPanorama(HuginBase::Panorama & pano)62 bool AddImagesCmd::processPanorama(HuginBase::Panorama& pano) 63 { 64 std::vector<HuginBase::SrcPanoImage>::const_iterator it; 65 for (it = imgs.begin(); it != imgs.end(); ++it) 66 { 67 pano.addImage(*it); 68 } 69 return true; 70 } 71 processPanorama(HuginBase::Panorama & pano)72 bool RemoveImageCmd::processPanorama(HuginBase::Panorama& pano) 73 { 74 pano.removeImage(imgNr); 75 return true; 76 } 77 processPanorama(HuginBase::Panorama & pano)78 bool RemoveImagesCmd::processPanorama(HuginBase::Panorama& pano) 79 { 80 for (HuginBase::UIntSet::reverse_iterator it = imgNrs.rbegin(); 81 it != imgNrs.rend(); ++it) 82 { 83 pano.removeImage(*it); 84 } 85 return true; 86 } 87 processPanorama(HuginBase::Panorama & pano)88 bool UpdateVariablesCmd::processPanorama(HuginBase::Panorama& pano) 89 { 90 pano.updateVariables(vars); 91 HuginBase::PTools::calcCtrlPointErrors(pano); 92 return true; 93 } 94 processPanorama(HuginBase::Panorama & pano)95 bool UpdateCPsCmd::processPanorama(HuginBase::Panorama& pano) 96 { 97 HuginBase::CPVector::const_iterator it; 98 unsigned int i = 0; 99 for (it = cps.begin(); it != cps.end(); ++it, i++) 100 { 101 pano.changeControlPoint(i, *it); 102 } 103 if (updateCPError) 104 { 105 HuginBase::PTools::calcCtrlPointErrors(pano); 106 }; 107 108 return true; 109 } 110 processPanorama(HuginBase::Panorama & pano)111 bool UpdateVariablesCPCmd::processPanorama(HuginBase::Panorama& pano) 112 { 113 pano.updateVariables(vars); 114 pano.updateCtrlPointErrors(cps); 115 return true; 116 } 117 processPanorama(HuginBase::Panorama & pano)118 bool UpdateVariablesCPSetCmd::processPanorama(HuginBase::Panorama& pano) 119 { 120 pano.updateVariables(m_imgs, vars); 121 pano.updateCtrlPointErrors(m_imgs, cps); 122 pano.markAsOptimized(); 123 return true; 124 } 125 processPanorama(HuginBase::Panorama & pano)126 bool UpdateImageVariablesCmd::processPanorama(HuginBase::Panorama& pano) 127 { 128 pano.updateVariables(imgNr, vars); 129 HuginBase::PTools::calcCtrlPointErrors(pano); 130 return true; 131 } 132 processPanorama(HuginBase::Panorama & pano)133 bool UpdateImagesVariablesCmd::processPanorama(HuginBase::Panorama& pano) 134 { 135 HuginBase::UIntSet::iterator it; 136 HuginBase::VariableMapVector::const_iterator v_it = vars.begin(); 137 for (it = change.begin(); it != change.end(); ++it) 138 { 139 pano.updateVariables(*it, *v_it); 140 ++v_it; 141 } 142 HuginBase::PTools::calcCtrlPointErrors(pano); 143 return true; 144 } 145 processPanorama(HuginBase::Panorama & pano)146 bool UpdateVariablesByParseExpression::processPanorama(HuginBase::Panorama& pano) 147 { 148 if (!m_expression.empty()) 149 { 150 Parser::PanoParseExpression(pano, m_expression); 151 HuginBase::PTools::calcCtrlPointErrors(pano); 152 return true; 153 } 154 else 155 { 156 return false; 157 }; 158 } 159 processPanorama(HuginBase::Panorama & pano)160 bool UpdateOptimizeVectorCmd::processPanorama(HuginBase::Panorama & pano) 161 { 162 pano.setOptimizeVector(m_optvec); 163 return true; 164 } 165 166 processPanorama(HuginBase::Panorama & pano)167 bool UpdateOptimizerSwitchCmd::processPanorama(HuginBase::Panorama & pano) 168 { 169 pano.setOptimizerSwitch(m_mode); 170 return true; 171 } 172 processPanorama(HuginBase::Panorama & pano)173 bool UpdatePhotometricOptimizerSwitchCmd::processPanorama(HuginBase::Panorama & pano) 174 { 175 pano.setPhotometricOptimizerSwitch(m_mode); 176 return true; 177 } 178 processPanorama(HuginBase::Panorama & pano)179 bool SetVariableCmd::processPanorama(HuginBase::Panorama& pano) 180 { 181 HuginBase::UIntSet::iterator it; 182 for (it = images.begin(); it != images.end(); ++it) 183 { 184 pano.updateVariable(*it, var); 185 } 186 HuginBase::PTools::calcCtrlPointErrors(pano); 187 188 return true; 189 } 190 processPanorama(HuginBase::Panorama & pano)191 bool CenterPanoCmd::processPanorama(HuginBase::Panorama& pano) 192 { 193 HuginBase::CenterHorizontally(pano).run(); 194 // adjust canvas size 195 HuginBase::CalculateFitPanorama fitPano(pano); 196 fitPano.run(); 197 HuginBase::PanoramaOptions opts = pano.getOptions(); 198 opts.setHFOV(fitPano.getResultHorizontalFOV()); 199 opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight())); 200 pano.setOptions(opts); 201 HuginBase::PTools::calcCtrlPointErrors(pano); 202 203 return true; 204 } 205 processPanorama(HuginBase::Panorama & pano)206 bool StraightenPanoCmd::processPanorama(HuginBase::Panorama& pano) 207 { 208 HuginBase::StraightenPanorama(pano).run(); 209 210 HuginBase::PanoramaOptions opts = pano.getOptions(); 211 if (opts.getHFOV()<360) 212 { 213 // center non 360 deg panos 214 HuginBase::CenterHorizontally(pano).run(); 215 }; 216 // adjust canvas size 217 HuginBase::CalculateFitPanorama fitPano(pano); 218 fitPano.run(); 219 opts.setHFOV(fitPano.getResultHorizontalFOV()); 220 opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight())); 221 pano.setOptions(opts); 222 HuginBase::PTools::calcCtrlPointErrors(pano); 223 224 return true; 225 } 226 processPanorama(HuginBase::Panorama & pano)227 bool AddCtrlPointCmd::processPanorama(HuginBase::Panorama& pano) 228 { 229 pano.addCtrlPoint(point); 230 HuginBase::PTools::calcCtrlPointErrors(pano); 231 return true; 232 } 233 processPanorama(HuginBase::Panorama & pano)234 bool AddCtrlPointsCmd::processPanorama(HuginBase::Panorama& pano) 235 { 236 for (HuginBase::CPVector::iterator it = cps.begin(); it != cps.end(); ++it) 237 { 238 pano.addCtrlPoint(*it); 239 } 240 HuginBase::PTools::calcCtrlPointErrors(pano); 241 return true; 242 } 243 processPanorama(HuginBase::Panorama & pano)244 bool RemoveCtrlPointCmd::processPanorama(HuginBase::Panorama& pano) 245 { 246 pano.removeCtrlPoint(pointNr); 247 return true; 248 } 249 processPanorama(HuginBase::Panorama & pano)250 bool RemoveCtrlPointsCmd::processPanorama(HuginBase::Panorama& pano) 251 { 252 for (HuginBase::UIntSet::reverse_iterator it = m_points.rbegin(); it != m_points.rend(); ++it) 253 { 254 pano.removeCtrlPoint(*it); 255 } 256 return true; 257 } 258 processPanorama(HuginBase::Panorama & pano)259 bool ChangeCtrlPointCmd::processPanorama(HuginBase::Panorama& pano) 260 { 261 pano.changeControlPoint(pNr, point); 262 HuginBase::PTools::calcCtrlPointErrors(pano); 263 return true; 264 } 265 processPanorama(HuginBase::Panorama & pano)266 bool SetActiveImagesCmd::processPanorama(HuginBase::Panorama& pano) 267 { 268 HuginBase::UIntSet::iterator it; 269 for (unsigned int i = 0; i < pano.getNrOfImages(); i++) 270 { 271 if (set_contains(m_active, i)) 272 { 273 pano.activateImage(i, true); 274 } 275 else 276 { 277 pano.activateImage(i, false); 278 }; 279 } 280 return true; 281 } 282 processPanorama(HuginBase::Panorama & pano)283 bool SwapImagesCmd::processPanorama(HuginBase::Panorama& pano) 284 { 285 pano.swapImages(m_i1, m_i2); 286 return true; 287 } 288 processPanorama(HuginBase::Panorama & pano)289 bool MoveImageCmd::processPanorama(HuginBase::Panorama& pano) 290 { 291 pano.moveImage(m_i1, m_i2); 292 return true; 293 } 294 processPanorama(HuginBase::Panorama & pano)295 bool MergePanoCmd::processPanorama(HuginBase::Panorama& pano) 296 { 297 pano.mergePanorama(newPano); 298 HuginBase::PTools::calcCtrlPointErrors(pano); 299 return true; 300 } 301 processPanorama(HuginBase::Panorama & pano)302 bool UpdateSrcImageCmd::processPanorama(HuginBase::Panorama& pano) 303 { 304 pano.setSrcImage(imgNr, img); 305 HuginBase::PTools::calcCtrlPointErrors(pano); 306 return true; 307 } 308 processPanorama(HuginBase::Panorama & pano)309 bool UpdateSrcImagesCmd::processPanorama(HuginBase::Panorama& pano) 310 { 311 int i = 0; 312 for (HuginBase::UIntSet::iterator it = imgNrs.begin(); it != imgNrs.end(); ++it) 313 { 314 pano.setSrcImage(*it, imgs[i]); 315 i++; 316 } 317 HuginBase::PTools::calcCtrlPointErrors(pano); 318 return true; 319 } 320 processPanorama(HuginBase::Panorama & pano)321 bool SetPanoOptionsCmd::processPanorama(HuginBase::Panorama& pano) 322 { 323 pano.setOptions(options); 324 HuginBase::PTools::calcCtrlPointErrors(pano); 325 return true; 326 } 327 LoadPTProjectCmd(HuginBase::Panorama & p,const std::string & filename,const std::string & prefix)328 LoadPTProjectCmd::LoadPTProjectCmd(HuginBase::Panorama & p, const std::string & filename, const std::string & prefix) 329 : PanoCommand(p), filename(filename), prefix(prefix) 330 { 331 m_clearDirty = true; 332 } 333 processPanorama(HuginBase::Panorama & pano)334 bool LoadPTProjectCmd::processPanorama(HuginBase::Panorama& pano) 335 { 336 std::ifstream in(filename.c_str()); 337 AppBase::DocumentData::ReadWriteError err = pano.readData(in); 338 if (err != AppBase::DocumentData::SUCCESSFUL) 339 { 340 DEBUG_ERROR("could not load panotools script"); 341 return false; 342 } 343 in.close(); 344 return true; 345 } 346 processPanorama(HuginBase::Panorama & pano)347 bool RotatePanoCmd::processPanorama(HuginBase::Panorama& pano) 348 { 349 HuginBase::RotatePanorama(pano, y, p, r).run(); 350 HuginBase::PTools::calcCtrlPointErrors(pano); 351 return true; 352 } 353 processPanorama(HuginBase::Panorama & pano)354 bool TranslatePanoCmd::processPanorama(HuginBase::Panorama& pano) 355 { 356 HuginBase::TranslatePanorama(pano, X, Y, Z).run(); 357 HuginBase::PTools::calcCtrlPointErrors(pano); 358 return true; 359 } 360 processPanorama(HuginBase::Panorama & pano)361 bool UpdateFocalLengthCmd::processPanorama(HuginBase::Panorama& pano) 362 { 363 pano.UpdateFocalLength(imgNrs, m_focalLength); 364 HuginBase::PTools::calcCtrlPointErrors(pano); 365 return true; 366 } 367 processPanorama(HuginBase::Panorama & pano)368 bool UpdateCropFactorCmd::processPanorama(HuginBase::Panorama& pano) 369 { 370 //search all image with the same lens, otherwise the crop factor is updated via links, 371 //but not the hfov if the image is not the given HuginBase::UIntSet 372 HuginBase::UIntSet allImgWithSameLens; 373 HuginBase::UIntSet testedLens; 374 HuginBase::StandardImageVariableGroups variable_groups(pano); 375 HuginBase::ImageVariableGroup & lenses = variable_groups.getLenses(); 376 for (HuginBase::UIntSet::const_iterator it = imgNrs.begin(); it != imgNrs.end(); ++it) 377 { 378 allImgWithSameLens.insert(*it); 379 unsigned int lensNr = lenses.getPartNumber(*it); 380 if (set_contains(testedLens, lensNr)) 381 { 382 continue; 383 }; 384 testedLens.insert(lensNr); 385 for (unsigned int i = 0; i<pano.getNrOfImages(); i++) 386 { 387 if (lenses.getPartNumber(i) == lensNr) 388 { 389 allImgWithSameLens.insert(i); 390 }; 391 }; 392 }; 393 pano.UpdateCropFactor(allImgWithSameLens, m_cropFactor); 394 HuginBase::PTools::calcCtrlPointErrors(pano); 395 return true; 396 } 397 processPanorama(HuginBase::Panorama & pano)398 bool ChangePartNumberCmd::processPanorama(HuginBase::Panorama& pano) 399 { 400 // it might change as we are setting them 401 std::size_t new_new_part_number = new_part_number; 402 HuginBase::ImageVariableGroup group(variables, pano); 403 for (HuginBase::UIntSet::iterator it = image_numbers.begin(); it != image_numbers.end(); ++it) 404 { 405 group.switchParts(*it, new_new_part_number); 406 // update the lens number if it changes. 407 new_new_part_number = group.getPartNumber(*it); 408 } 409 return true; 410 } 411 processPanorama(HuginBase::Panorama & pano)412 bool ChangePartImagesLinkingCmd::processPanorama(HuginBase::Panorama& pano) 413 { 414 HuginBase::ImageVariableGroup group(groupVariables, pano); 415 if (new_linked_state) 416 { 417 for (HuginBase::UIntSet::iterator imageIt = image_numbers.begin(); imageIt != image_numbers.end(); ++imageIt) 418 { 419 // link the variables 420 for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator variableIt = changeVariables.begin(); 421 variableIt != changeVariables.end(); ++variableIt) 422 { 423 group.linkVariableImage(*variableIt, *imageIt); 424 } 425 } 426 } 427 else 428 { 429 for (HuginBase::UIntSet::iterator imageIt = image_numbers.begin(); imageIt != image_numbers.end(); ++imageIt) 430 { 431 // unlink the variable 432 for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator variableIt = changeVariables.begin(); 433 variableIt != changeVariables.end(); ++variableIt) 434 { 435 group.unlinkVariableImage(*variableIt, *imageIt); 436 group.updatePartNumbers(); 437 } 438 } 439 } 440 return true; 441 } 442 processPanorama(HuginBase::Panorama & pano)443 bool LinkLensVarsCmd::processPanorama(HuginBase::Panorama& pano) 444 { 445 HuginBase::StandardImageVariableGroups variable_groups(pano); 446 HuginBase::ImageVariableGroup & lenses = variable_groups.getLenses(); 447 std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it; 448 for (it = variables.begin(); it != variables.end(); ++it) 449 { 450 lenses.linkVariablePart(*it, lens_number); 451 } 452 return true; 453 } 454 455 /// @todo avoid copying image data in processPanorama 456 #define image_variable( name, type, default_value )\ 457 bool ChangeImage##name##Cmd::processPanorama(HuginBase::Panorama& pano)\ 458 {\ 459 for (HuginBase::UIntSet::iterator it = image_numbers.begin(); it != image_numbers.end(); it++)\ 460 {\ 461 HuginBase::SrcPanoImage img = pano.getSrcImage(*it);\ 462 img.set##name(value);\ 463 pano.setSrcImage(*it, img);\ 464 }\ 465 return true;\ 466 }; 467 #include <panodata/image_variables.h> 468 #undef image_variable 469 processPanorama(HuginBase::Panorama & pano)470 bool NewPartCmd::processPanorama(HuginBase::Panorama& pano) 471 { 472 // unlink all the variables in the first image. 473 DEBUG_ASSERT(!image_numbers.empty()); 474 unsigned int image_index = *image_numbers.begin(); 475 for (std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it = vars.begin(); it != vars.end(); ++it) 476 { 477 switch (*it) 478 { 479 #define image_variable( name, type, default_value )\ 480 case HuginBase::ImageVariableGroup::IVE_##name:\ 481 pano.unlinkImageVariable##name(image_index);\ 482 break; 483 #include <panodata/image_variables.h> 484 #undef image_variable 485 } 486 } 487 // now the first image should have a new part in the group. 488 // we want to switch the rest of the images to the new part. 489 HuginBase::ImageVariableGroup group(vars, pano); 490 for (HuginBase::UIntSet::iterator it = ++image_numbers.begin(); it != image_numbers.end(); ++it) 491 { 492 std::size_t part_number = group.getPartNumber(image_index); 493 group.switchParts(*it, part_number); 494 } 495 return true; 496 } 497 processPanorama(HuginBase::Panorama & pano)498 bool UpdateMaskForImgCmd::processPanorama(HuginBase::Panorama& pano) 499 { 500 pano.updateMasksForImage(m_img, m_mask); 501 return true; 502 } 503 processPanorama(HuginBase::Panorama & pano)504 bool UpdateWhiteBalance::processPanorama(HuginBase::Panorama& pano) 505 { 506 pano.updateWhiteBalance(m_red, m_blue); 507 return true; 508 } 509 processPanorama(HuginBase::Panorama & pano)510 bool ResetToMeanExposure::processPanorama(HuginBase::Panorama& pano) 511 { 512 HuginBase::PanoramaOptions opts = pano.getOptions(); 513 opts.outputExposureValue = HuginBase::CalculateMeanExposure::calcMeanExposure(pano); 514 pano.setOptions(opts); 515 return true; 516 } 517 processPanorama(HuginBase::Panorama & pano)518 bool DistributeImagesCmd::processPanorama(HuginBase::Panorama& pano) 519 { 520 const size_t nrImages = pano.getNrOfImages(); 521 if (nrImages>0) 522 { 523 const HuginBase::SrcPanoImage& img = pano.getImage(0); 524 const double hfov = img.getHFOV(); 525 size_t imgsPerRow; 526 //distribute all images 527 //for rectilinear images calculate number of rows 528 if (img.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR) 529 { 530 imgsPerRow = std::max(3, int(360 / (0.8*hfov))); 531 imgsPerRow = std::min(imgsPerRow, nrImages); 532 } 533 else 534 { 535 //all other images do in one row to prevent cluttered images with fisheye images and the like 536 imgsPerRow = nrImages; 537 }; 538 double offset = 0.75*hfov; 539 if ((imgsPerRow - 1.0)*offset>360) 540 { 541 offset = 360 / (imgsPerRow - 1.0); 542 }; 543 double yaw = -(imgsPerRow - 1.0) / 2.0*offset; 544 double pitch = 0; 545 if (imgsPerRow<nrImages) 546 { 547 pitch = (-(std::ceil(double(nrImages) / double(imgsPerRow)) - 1.0) / 2.0*offset); 548 }; 549 HuginBase::VariableMapVector varsVec = pano.getVariables(); 550 size_t counter = 0; 551 for (size_t i = 0; i<nrImages; i++) 552 { 553 HuginBase::VariableMap::iterator it = varsVec[i].find("y"); 554 if (it != varsVec[i].end()) 555 { 556 it->second.setValue(yaw); 557 }; 558 it = varsVec[i].find("p"); 559 if (it != varsVec[i].end()) 560 { 561 it->second.setValue(pitch); 562 }; 563 yaw += offset; 564 counter++; 565 if (counter == imgsPerRow) 566 { 567 counter = 0; 568 pitch += offset; 569 yaw = -(imgsPerRow - 1.0) / 2.0*offset; 570 }; 571 }; 572 pano.updateVariables(varsVec); 573 }; 574 return true; 575 } 576 577 } // namespace PanoCommand 578