diff --git a/src/software/pipeline/main_featureExtraction.cpp b/src/software/pipeline/main_featureExtraction.cpp index 97570e2225..8fefd38385 100644 --- a/src/software/pipeline/main_featureExtraction.cpp +++ b/src/software/pipeline/main_featureExtraction.cpp @@ -43,143 +43,148 @@ namespace fs = boost::filesystem; /// - Export computed data int aliceVision_main(int argc, char **argv) { - // command-line parameters - std::string sfmDataFilename; - std::string masksFolder; - std::string outputFolder; - - // user optional parameters - - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - feature::ConfigurationPreset featDescConfig; - int rangeStart = -1; - int rangeSize = 1; - int maxThreads = 0; - bool forceCpuExtraction = false; - - po::options_description requiredParams("Required parameters"); - requiredParams.add_options() - ("input,i", po::value(&sfmDataFilename)->required(), - "SfMData file.") - ("output,o", po::value(&outputFolder)->required(), - "Output path for the features and descriptors files (*.feat, *.desc)."); - - po::options_description optionalParams("Optional parameters"); - optionalParams.add_options() - ("describerTypes,d", po::value(&describerTypesName)->default_value(describerTypesName), - feature::EImageDescriberType_informations().c_str()) - ("describerPreset,p", po::value(&featDescConfig.descPreset)->default_value(featDescConfig.descPreset), - "Control the ImageDescriber configuration (low, medium, normal, high, ultra).\n" - "Configuration 'ultra' can take long time !") - ("describerQuality", po::value(&featDescConfig.quality)->default_value(featDescConfig.quality), - feature::EFeatureQuality_information().c_str()) - ("gridFiltering", po::value(&featDescConfig.gridFiltering)->default_value(featDescConfig.gridFiltering), - "Enable grid filtering. Highly recommended to ensure usable number of features.") - ("maxNbFeatures", po::value(&featDescConfig.maxNbFeatures)->default_value(featDescConfig.maxNbFeatures), - "Max number of features extracted (0 means default value based on describerPreset).") - ("contrastFiltering", po::value(&featDescConfig.contrastFiltering)->default_value(featDescConfig.contrastFiltering), - feature::EFeatureConstrastFiltering_information().c_str()) - ("relativePeakThreshold", po::value(&featDescConfig.relativePeakThreshold)->default_value(featDescConfig.relativePeakThreshold), - "Peak Threshold relative to median of gradiants.") - ("forceCpuExtraction", po::value(&forceCpuExtraction)->default_value(forceCpuExtraction), - "Use only CPU feature extraction methods.") - ("masksFolder", po::value(&masksFolder), - "Masks folder.") - ("rangeStart", po::value(&rangeStart)->default_value(rangeStart), - "Range image index start.") - ("rangeSize", po::value(&rangeSize)->default_value(rangeSize), - "Range size.") - ("maxThreads", po::value(&maxThreads)->default_value(maxThreads), - "Specifies the maximum number of threads to run simultaneously (0 for automatic mode)."); - - CmdLine cmdline("AliceVision featureExtraction"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(describerTypesName.empty()) - { - ALICEVISION_LOG_ERROR("--describerTypes option is empty."); - return EXIT_FAILURE; - } - - // create output folder - if(!fs::exists(outputFolder)) - { - if(!fs::create_directory(outputFolder)) + // command-line parameters + std::string sfmDataFilename; + std::string masksFolder; + std::string outputFolder; + + // user optional parameters + + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + feature::ConfigurationPreset featDescConfig; + int rangeStart = -1; + int rangeSize = 1; + int maxThreads = 0; + bool forceCpuExtraction = false; + + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("input,i", po::value(&sfmDataFilename)->required(), + "SfMData file.") + ("output,o", po::value(&outputFolder)->required(), + "Output path for the features and descriptors files (*.feat, *.desc)."); + + po::options_description optionalParams("Optional parameters"); + optionalParams.add_options() + ("describerTypes,d", po::value(&describerTypesName)->default_value(describerTypesName), + feature::EImageDescriberType_informations().c_str()) + ("describerPreset,p", po::value(&featDescConfig.descPreset)->default_value(featDescConfig.descPreset), + "Control the ImageDescriber configuration (low, medium, normal, high, ultra).\n" + "Configuration 'ultra' can take long time !") + ("describerQuality", po::value(&featDescConfig.quality)->default_value(featDescConfig.quality), + feature::EFeatureQuality_information().c_str()) + ("gridFiltering", po::value(&featDescConfig.gridFiltering)->default_value(featDescConfig.gridFiltering), + "Enable grid filtering. Highly recommended to ensure usable number of features.") + ("maxNbFeatures", po::value(&featDescConfig.maxNbFeatures)->default_value(featDescConfig.maxNbFeatures), + "Max number of features extracted (0 means default value based on describerPreset).") + ("contrastFiltering", po::value(&featDescConfig.contrastFiltering)->default_value(featDescConfig.contrastFiltering), + feature::EFeatureConstrastFiltering_information().c_str()) + ("relativePeakThreshold", po::value(&featDescConfig.relativePeakThreshold)->default_value(featDescConfig.relativePeakThreshold), + "Peak Threshold relative to median of gradiants.") + ("forceCpuExtraction", po::value(&forceCpuExtraction)->default_value(forceCpuExtraction), + "Use only CPU feature extraction methods.") + ("masksFolder", po::value(&masksFolder), + "Masks folder.") + ("rangeStart", po::value(&rangeStart)->default_value(rangeStart), + "Range image index start.") + ("rangeSize", po::value(&rangeSize)->default_value(rangeSize), + "Range size.") + ("maxThreads", po::value(&maxThreads)->default_value(maxThreads), + "Specifies the maximum number of threads to run simultaneously (0 for automatic mode)."); + + CmdLine cmdline("AliceVision featureExtraction"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - ALICEVISION_LOG_ERROR("Cannot create output folder"); - return EXIT_FAILURE; + return EXIT_FAILURE; + } + + if(describerTypesName.empty()) + { + ALICEVISION_LOG_ERROR("--describerTypes option is empty."); + return EXIT_FAILURE; + } + + // create output folder + if(!fs::exists(outputFolder)) + { + if(!fs::create_directory(outputFolder)) + { + ALICEVISION_LOG_ERROR("Cannot create output folder"); + return EXIT_FAILURE; + } } - } #ifdef ALICEVISION_HAVE_GPU_FEATURES - // Print GPU Information - ALICEVISION_LOG_INFO(gpu::gpuInformationCUDA()); + // Print GPU Information + ALICEVISION_LOG_INFO(gpu::gpuInformationCUDA()); #endif - // load input scene - sfmData::SfMData sfmData; - std::cout << sfmData.getViews().size() << std::endl; - if(!sfmDataIO::Load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilename + "' cannot be read"); - return EXIT_FAILURE; - } - - // create feature extractor - feature::FeatureExtractor extractor(sfmData); - extractor.setMasksFolder(masksFolder); - extractor.setOutputFolder(outputFolder); - - // set maxThreads - HardwareContext hwc = cmdline.getHardwareContext(); - hwc.setUserCoresLimit(maxThreads); - - // set extraction range - if(rangeStart != -1) - { - if(rangeStart < 0 || rangeSize < 0 || - rangeStart > sfmData.getViews().size()) + // load input scene + sfmData::SfMData sfmData; + std::cout << sfmData.getViews().size() << std::endl; + if(!sfmDataIO::Load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) { - ALICEVISION_LOG_ERROR("Range is incorrect"); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilename + "' cannot be read"); + return EXIT_FAILURE; } - if(rangeStart + rangeSize > sfmData.views.size()) - rangeSize = sfmData.views.size() - rangeStart; + // create feature extractor + feature::FeatureExtractor extractor(sfmData); + extractor.setMasksFolder(masksFolder); + extractor.setOutputFolder(outputFolder); - extractor.setRange(rangeStart, rangeSize); - } + // set maxThreads + HardwareContext hwc = cmdline.getHardwareContext(); + hwc.setUserCoresLimit(maxThreads); - // initialize feature extractor imageDescribers - { - std::vector imageDescriberTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + // set extraction range + if(rangeStart != -1) + { + if(rangeStart < 0 || rangeSize < 0) + { + ALICEVISION_LOG_ERROR("Range is incorrect"); + return EXIT_FAILURE; + } + + if(rangeStart + rangeSize > sfmData.views.size()) + rangeSize = sfmData.views.size() - rangeStart; + + if(rangeSize <= 0) + { + ALICEVISION_LOG_WARNING("Nothing to compute."); + return EXIT_SUCCESS; + } + + extractor.setRange(rangeStart, rangeSize); + } - for(const auto& imageDescriberType: imageDescriberTypes) + // initialize feature extractor imageDescribers { - std::shared_ptr imageDescriber = feature::createImageDescriber(imageDescriberType); - imageDescriber->setConfigurationPreset(featDescConfig); - if(forceCpuExtraction) - imageDescriber->setUseCuda(false); + std::vector imageDescriberTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + + for(const auto& imageDescriberType: imageDescriberTypes) + { + std::shared_ptr imageDescriber = feature::createImageDescriber(imageDescriberType); + imageDescriber->setConfigurationPreset(featDescConfig); + if(forceCpuExtraction) + imageDescriber->setUseCuda(false); - extractor.addImageDescriber(imageDescriber); + extractor.addImageDescriber(imageDescriber); + } } - } // feature extraction routines // for each View of the SfMData container: // - if regions file exist continue, // - if no file, compute features - { - system::Timer timer; + { + system::Timer timer; - extractor.process(hwc); + extractor.process(hwc); - ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); - } - return EXIT_SUCCESS; + ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); + } + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_prepareDenseScene.cpp b/src/software/pipeline/main_prepareDenseScene.cpp index a9c67fb202..2451037e49 100644 --- a/src/software/pipeline/main_prepareDenseScene.cpp +++ b/src/software/pipeline/main_prepareDenseScene.cpp @@ -44,34 +44,34 @@ namespace fs = boost::filesystem; template void process(const std::string &dstColorImage, const IntrinsicBase* cam, const oiio::ParamValueList & metadata, const std::string & srcImage, bool evCorrection, float exposureCompensation, MaskFuncT && maskFunc) { - ImageT image, image_ud; - readImage(srcImage, image, image::EImageColorSpace::LINEAR); - - //exposure correction - if(evCorrection) - { - for(int pix = 0; pix < image.Width() * image.Height(); ++pix) - { - image(pix) = image(pix) * exposureCompensation; - } - } - - // mask - maskFunc(image); - - // undistort - if(cam->isValid() && cam->hasDistortion()) - { - // undistort the image and save it - using Pix = typename ImageT::Tpixel; - Pix pixZero(Pix::Zero()); - UndistortImage(image, cam, image_ud, pixZero); - writeImage(dstColorImage, image_ud, image::ImageWriteOptions(), metadata); - } - else - { - writeImage(dstColorImage, image, image::ImageWriteOptions(), metadata); - } + ImageT image, image_ud; + readImage(srcImage, image, image::EImageColorSpace::LINEAR); + + // exposure correction + if(evCorrection) + { + for(int pix = 0; pix < image.Width() * image.Height(); ++pix) + { + image(pix) = image(pix) * exposureCompensation; + } + } + + // mask + maskFunc(image); + + // undistort + if(cam->isValid() && cam->hasDistortion()) + { + // undistort the image and save it + using Pix = typename ImageT::Tpixel; + Pix pixZero(Pix::Zero()); + UndistortImage(image, cam, image_ud, pixZero); + writeImage(dstColorImage, image_ud, image::ImageWriteOptions(), metadata); + } + else + { + writeImage(dstColorImage, image, image::ImageWriteOptions(), metadata); + } } bool prepareDenseScene(const SfMData& sfmData, @@ -85,287 +85,293 @@ bool prepareDenseScene(const SfMData& sfmData, bool saveMatricesFiles, bool evCorrection) { - // defined view Ids - std::set viewIds; + // defined view Ids + std::set viewIds; - sfmData::Views::const_iterator itViewBegin = sfmData.getViews().begin(); - sfmData::Views::const_iterator itViewEnd = sfmData.getViews().end(); + sfmData::Views::const_iterator itViewBegin = sfmData.getViews().begin(); + sfmData::Views::const_iterator itViewEnd = sfmData.getViews().end(); - if(endIndex > 0) - { - itViewEnd = itViewBegin; - std::advance(itViewEnd, endIndex); - } - - std::advance(itViewBegin, (beginIndex < 0) ? 0 : beginIndex); + if(endIndex > 0) + { + itViewEnd = itViewBegin; + std::advance(itViewEnd, endIndex); + } - // export valid views as projective cameras - for(auto it = itViewBegin; it != itViewEnd; ++it) - { - const View* view = it->second.get(); - if (!sfmData.isPoseAndIntrinsicDefined(view)) - continue; - viewIds.insert(view->getViewId()); - } + std::advance(itViewBegin, (beginIndex < 0) ? 0 : beginIndex); - if((outputFileType != image::EImageFileType::EXR) && saveMetadata) - ALICEVISION_LOG_WARNING("Cannot save informations in images metadata.\n" - "Choose '.exr' file type if you want AliceVision custom metadata"); + // export valid views as projective cameras + for(auto it = itViewBegin; it != itViewEnd; ++it) + { + const View* view = it->second.get(); + if (!sfmData.isPoseAndIntrinsicDefined(view)) + continue; + viewIds.insert(view->getViewId()); + } - // export data - auto progressDisplay = system::createConsoleProgressDisplay(viewIds.size(), std::cout, - "Exporting Scene Undistorted Images\n"); + if((outputFileType != image::EImageFileType::EXR) && saveMetadata) + ALICEVISION_LOG_WARNING("Cannot save informations in images metadata.\n" + "Choose '.exr' file type if you want AliceVision custom metadata"); - // for exposure correction - const double medianCameraExposure = sfmData.getMedianCameraExposureSetting().getExposure(); - ALICEVISION_LOG_INFO("Median Camera Exposure: " << medianCameraExposure << ", Median EV: " << std::log2(1.0/medianCameraExposure)); + // export data + auto progressDisplay = system::createConsoleProgressDisplay(viewIds.size(), std::cout, + "Exporting Scene Undistorted Images\n"); -#pragma omp parallel for num_threads(3) - for(int i = 0; i < viewIds.size(); ++i) - { - auto itView = viewIds.begin(); - std::advance(itView, i); + // for exposure correction + const double medianCameraExposure = sfmData.getMedianCameraExposureSetting().getExposure(); + ALICEVISION_LOG_INFO("Median Camera Exposure: " << medianCameraExposure << ", Median EV: " << std::log2(1.0/medianCameraExposure)); - const IndexT viewId = *itView; - const View* view = sfmData.getViews().at(viewId).get(); + #pragma omp parallel for num_threads(3) + for(int i = 0; i < viewIds.size(); ++i) + { + auto itView = viewIds.begin(); + std::advance(itView, i); - Intrinsics::const_iterator iterIntrinsic = sfmData.getIntrinsics().find(view->getIntrinsicId()); + const IndexT viewId = *itView; + const View* view = sfmData.getViews().at(viewId).get(); - //we have a valid view with a corresponding camera & pose - const std::string baseFilename = std::to_string(viewId); + Intrinsics::const_iterator iterIntrinsic = sfmData.getIntrinsics().find(view->getIntrinsicId()); - // get metadata from source image to be sure we get all metadata. We don't use the metadatas from the Views inside the SfMData to avoid type conversion problems with string maps. - std::string srcImage = view->getImagePath(); - oiio::ParamValueList metadata = image::readImageMetadata(srcImage); + // we have a valid view with a corresponding camera & pose + const std::string baseFilename = std::to_string(viewId); - // export camera - if(saveMetadata || saveMatricesFiles) - { - // get camera pose / projection - const Pose3 pose = sfmData.getPose(*view).getTransform(); - - std::shared_ptr cam = iterIntrinsic->second; - std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); - if (!camPinHole) { - ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); - continue; - } - - Mat34 P = camPinHole->getProjectiveEquivalent(pose); - - // get camera intrinsics matrices - const Mat3 K = dynamic_cast(sfmData.getIntrinsicPtr(view->getIntrinsicId()))->K(); - const Mat3& R = pose.rotation(); - const Vec3& t = pose.translation(); - - if(saveMatricesFiles) - { - std::ofstream fileP((fs::path(outFolder) / (baseFilename + "_P.txt")).string()); - fileP << std::setprecision(10) - << P(0, 0) << " " << P(0, 1) << " " << P(0, 2) << " " << P(0, 3) << "\n" - << P(1, 0) << " " << P(1, 1) << " " << P(1, 2) << " " << P(1, 3) << "\n" - << P(2, 0) << " " << P(2, 1) << " " << P(2, 2) << " " << P(2, 3) << "\n"; - fileP.close(); - - std::ofstream fileKRt((fs::path(outFolder) / (baseFilename + "_KRt.txt")).string()); - fileKRt << std::setprecision(10) - << K(0, 0) << " " << K(0, 1) << " " << K(0, 2) << "\n" - << K(1, 0) << " " << K(1, 1) << " " << K(1, 2) << "\n" - << K(2, 0) << " " << K(2, 1) << " " << K(2, 2) << "\n" - << "\n" - << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << "\n" - << R(1, 0) << " " << R(1, 1) << " " << R(1, 2) << "\n" - << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << "\n" - << "\n" - << t(0) << " " << t(1) << " " << t(2) << "\n"; - fileKRt.close(); - } - - if(saveMetadata) - { - // convert to 44 matix - Mat4 projectionMatrix; - projectionMatrix << P(0, 0), P(0, 1), P(0, 2), P(0, 3), - P(1, 0), P(1, 1), P(1, 2), P(1, 3), - P(2, 0), P(2, 1), P(2, 2), P(2, 3), - 0, 0, 0, 1; - - // convert matrices to rowMajor - std::vector vP(projectionMatrix.size()); - std::vector vK(K.size()); - std::vector vR(R.size()); - - typedef Eigen::Matrix RowMatrixXd; - Eigen::Map(vP.data(), projectionMatrix.rows(), projectionMatrix.cols()) = projectionMatrix; - Eigen::Map(vK.data(), K.rows(), K.cols()) = K; - Eigen::Map(vR.data(), R.rows(), R.cols()) = R; - - // add metadata - metadata.push_back(oiio::ParamValue("AliceVision:downscale", 1)); - metadata.push_back(oiio::ParamValue("AliceVision:P", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX44), 1, vP.data())); - metadata.push_back(oiio::ParamValue("AliceVision:K", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX33), 1, vK.data())); - metadata.push_back(oiio::ParamValue("AliceVision:R", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX33), 1, vR.data())); - metadata.push_back(oiio::ParamValue("AliceVision:t", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::VEC3), 1, t.data())); - } - } + // get metadata from source image to be sure we get all metadata. We don't use the metadatas from the Views inside the SfMData to avoid type conversion problems with string maps. + std::string srcImage = view->getImagePath(); + oiio::ParamValueList metadata = image::readImageMetadata(srcImage); - // export undistort image - { - if(!imagesFolders.empty()) - { - std::vector paths = sfmDataIO::viewPathsFromFolders(*view, imagesFolders); - - // if path was not found - if(paths.empty()) - { - throw std::runtime_error("Cannot find view '" + std::to_string(view->getViewId()) + "' image file in given folder(s)"); - } - else if(paths.size() > 1) - { - throw std::runtime_error( "Ambiguous case: Multiple source image files found in given folder(s) for the view '" + - std::to_string(view->getViewId()) + "'."); - } - - srcImage = paths[0]; - } - const std::string dstColorImage = (fs::path(outFolder) / (baseFilename + "." + image::EImageFileType_enumToString(outputFileType))).string(); - const IntrinsicBase* cam = iterIntrinsic->second.get(); - - // add exposure values to images metadata - const double cameraExposure = view->getCameraExposureSetting().getExposure(); - const double ev = std::log2(1.0 / cameraExposure); - const float exposureCompensation = float(medianCameraExposure / cameraExposure); - metadata.push_back(oiio::ParamValue("AliceVision:EV", float(ev))); - metadata.push_back(oiio::ParamValue("AliceVision:EVComp", exposureCompensation)); - - if(evCorrection) - { - ALICEVISION_LOG_INFO("image " << viewId << ", exposure: " << cameraExposure << ", Ev " << ev << " Ev compensation: " + std::to_string(exposureCompensation)); - } - - image::Image mask; - if(tryLoadMask(&mask, masksFolders, viewId, srcImage)) - { - process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, [&mask] (Image & image) + // export camera + if(saveMetadata || saveMatricesFiles) { - if(image.Width() * image.Height() != mask.Width() * mask.Height()) - { - ALICEVISION_LOG_WARNING("Invalid image mask size: mask is ignored."); - return; - } - - for(int pix = 0; pix < image.Width() * image.Height(); ++pix) - { - const bool masked = (mask(pix) == 0); - image(pix).a() = masked ? 0.f : 1.f; - } - }); - } - else - { - const auto noMaskingFunc = [] (Image & image) {}; - process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, noMaskingFunc); - } + // get camera pose / projection + const Pose3 pose = sfmData.getPose(*view).getTransform(); + + std::shared_ptr cam = iterIntrinsic->second; + std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); + if (!camPinHole) { + ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); + continue; + } + + Mat34 P = camPinHole->getProjectiveEquivalent(pose); + + // get camera intrinsics matrices + const Mat3 K = dynamic_cast(sfmData.getIntrinsicPtr(view->getIntrinsicId()))->K(); + const Mat3& R = pose.rotation(); + const Vec3& t = pose.translation(); + + if(saveMatricesFiles) + { + std::ofstream fileP((fs::path(outFolder) / (baseFilename + "_P.txt")).string()); + fileP << std::setprecision(10) + << P(0, 0) << " " << P(0, 1) << " " << P(0, 2) << " " << P(0, 3) << "\n" + << P(1, 0) << " " << P(1, 1) << " " << P(1, 2) << " " << P(1, 3) << "\n" + << P(2, 0) << " " << P(2, 1) << " " << P(2, 2) << " " << P(2, 3) << "\n"; + fileP.close(); + + std::ofstream fileKRt((fs::path(outFolder) / (baseFilename + "_KRt.txt")).string()); + fileKRt << std::setprecision(10) + << K(0, 0) << " " << K(0, 1) << " " << K(0, 2) << "\n" + << K(1, 0) << " " << K(1, 1) << " " << K(1, 2) << "\n" + << K(2, 0) << " " << K(2, 1) << " " << K(2, 2) << "\n" + << "\n" + << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << "\n" + << R(1, 0) << " " << R(1, 1) << " " << R(1, 2) << "\n" + << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << "\n" + << "\n" + << t(0) << " " << t(1) << " " << t(2) << "\n"; + fileKRt.close(); + } + + if(saveMetadata) + { + // convert to 44 matix + Mat4 projectionMatrix; + projectionMatrix << P(0, 0), P(0, 1), P(0, 2), P(0, 3), + P(1, 0), P(1, 1), P(1, 2), P(1, 3), + P(2, 0), P(2, 1), P(2, 2), P(2, 3), + 0, 0, 0, 1; + + // convert matrices to rowMajor + std::vector vP(projectionMatrix.size()); + std::vector vK(K.size()); + std::vector vR(R.size()); + + typedef Eigen::Matrix RowMatrixXd; + Eigen::Map(vP.data(), projectionMatrix.rows(), projectionMatrix.cols()) = projectionMatrix; + Eigen::Map(vK.data(), K.rows(), K.cols()) = K; + Eigen::Map(vR.data(), R.rows(), R.cols()) = R; + + // add metadata + metadata.push_back(oiio::ParamValue("AliceVision:downscale", 1)); + metadata.push_back(oiio::ParamValue("AliceVision:P", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX44), 1, vP.data())); + metadata.push_back(oiio::ParamValue("AliceVision:K", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX33), 1, vK.data())); + metadata.push_back(oiio::ParamValue("AliceVision:R", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX33), 1, vR.data())); + metadata.push_back(oiio::ParamValue("AliceVision:t", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::VEC3), 1, t.data())); + } + } + + // export undistort image + { + if(!imagesFolders.empty()) + { + std::vector paths = sfmDataIO::viewPathsFromFolders(*view, imagesFolders); + + // if path was not found + if(paths.empty()) + { + throw std::runtime_error("Cannot find view '" + std::to_string(view->getViewId()) + "' image file in given folder(s)"); + } + else if(paths.size() > 1) + { + throw std::runtime_error( "Ambiguous case: Multiple source image files found in given folder(s) for the view '" + + std::to_string(view->getViewId()) + "'."); + } + + srcImage = paths[0]; + } + const std::string dstColorImage = (fs::path(outFolder) / (baseFilename + "." + image::EImageFileType_enumToString(outputFileType))).string(); + const IntrinsicBase* cam = iterIntrinsic->second.get(); + + // add exposure values to images metadata + const double cameraExposure = view->getCameraExposureSetting().getExposure(); + const double ev = std::log2(1.0 / cameraExposure); + const float exposureCompensation = float(medianCameraExposure / cameraExposure); + metadata.push_back(oiio::ParamValue("AliceVision:EV", float(ev))); + metadata.push_back(oiio::ParamValue("AliceVision:EVComp", exposureCompensation)); + + if(evCorrection) + { + ALICEVISION_LOG_INFO("image " << viewId << ", exposure: " << cameraExposure << ", Ev " << ev << " Ev compensation: " + std::to_string(exposureCompensation)); + } + + image::Image mask; + if(tryLoadMask(&mask, masksFolders, viewId, srcImage)) + { + process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, [&mask] (Image & image) + { + if(image.Width() * image.Height() != mask.Width() * mask.Height()) + { + ALICEVISION_LOG_WARNING("Invalid image mask size: mask is ignored."); + return; + } + + for(int pix = 0; pix < image.Width() * image.Height(); ++pix) + { + const bool masked = (mask(pix) == 0); + image(pix).a() = masked ? 0.f : 1.f; + } + }); + } + else + { + const auto noMaskingFunc = [] (Image & image) {}; + process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, noMaskingFunc); + } + } + + ++progressDisplay; } - ++progressDisplay; - } - - return true; + return true; } int aliceVision_main(int argc, char *argv[]) { - // command-line parameters - - std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); - std::string sfmDataFilename; - std::string outFolder; - std::string outImageFileTypeName = image::EImageFileType_enumToString(image::EImageFileType::EXR); - std::vector imagesFolders; - std::vector masksFolders; - int rangeStart = -1; - int rangeSize = 1; - bool saveMetadata = true; - bool saveMatricesTxtFiles = false; - bool evCorrection = false; - - po::options_description requiredParams("Required parameters"); - requiredParams.add_options() - ("input,i", po::value(&sfmDataFilename)->required(), - "SfMData file.") - ("output,o", po::value(&outFolder)->required(), - "Output folder."); - - po::options_description optionalParams("Optional parameters"); - optionalParams.add_options() - ("imagesFolders", po::value>(&imagesFolders)->multitoken(), - "Use images from specific folder(s) instead of those specify in the SfMData file.\n" - "Filename should be the same or the image uid.") - ("masksFolders", po::value>(&masksFolders)->multitoken(), - "Use masks from specific folder(s).\n" - "Filename should be the same or the image uid.") - ("outputFileType", po::value(&outImageFileTypeName)->default_value(outImageFileTypeName), - image::EImageFileType_informations().c_str()) - ("saveMetadata", po::value(&saveMetadata)->default_value(saveMetadata), - "Save projections and intrinsics information in images metadata.") - ("saveMatricesTxtFiles", po::value(&saveMatricesTxtFiles)->default_value(saveMatricesTxtFiles), - "Save projections and intrinsics information in text files.") - ("rangeStart", po::value(&rangeStart)->default_value(rangeStart), - "Range image index start.") - ("rangeSize", po::value(&rangeSize)->default_value(rangeSize), - "Range size.") - ("evCorrection", po::value(&evCorrection)->default_value(evCorrection), - "Correct exposure value."); - - CmdLine cmdline("AliceVision prepareDenseScene"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } + // command-line parameters + + std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); + std::string sfmDataFilename; + std::string outFolder; + std::string outImageFileTypeName = image::EImageFileType_enumToString(image::EImageFileType::EXR); + std::vector imagesFolders; + std::vector masksFolders; + int rangeStart = -1; + int rangeSize = 1; + bool saveMetadata = true; + bool saveMatricesTxtFiles = false; + bool evCorrection = false; + + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("input,i", po::value(&sfmDataFilename)->required(), + "SfMData file.") + ("output,o", po::value(&outFolder)->required(), + "Output folder."); + + po::options_description optionalParams("Optional parameters"); + optionalParams.add_options() + ("imagesFolders", po::value>(&imagesFolders)->multitoken(), + "Use images from specific folder(s) instead of those specify in the SfMData file.\n" + "Filename should be the same or the image uid.") + ("masksFolders", po::value>(&masksFolders)->multitoken(), + "Use masks from specific folder(s).\n" + "Filename should be the same or the image uid.") + ("outputFileType", po::value(&outImageFileTypeName)->default_value(outImageFileTypeName), + image::EImageFileType_informations().c_str()) + ("saveMetadata", po::value(&saveMetadata)->default_value(saveMetadata), + "Save projections and intrinsics information in images metadata.") + ("saveMatricesTxtFiles", po::value(&saveMatricesTxtFiles)->default_value(saveMatricesTxtFiles), + "Save projections and intrinsics information in text files.") + ("rangeStart", po::value(&rangeStart)->default_value(rangeStart), + "Range image index start.") + ("rangeSize", po::value(&rangeSize)->default_value(rangeSize), + "Range size.") + ("evCorrection", po::value(&evCorrection)->default_value(evCorrection), + "Correct exposure value."); + + CmdLine cmdline("AliceVision prepareDenseScene"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } - // set output file type - image::EImageFileType outputFileType = image::EImageFileType_stringToEnum(outImageFileTypeName); + // set output file type + image::EImageFileType outputFileType = image::EImageFileType_stringToEnum(outImageFileTypeName); - // Create output dir - if(!fs::exists(outFolder)) - fs::create_directory(outFolder); + // Create output dir + if(!fs::exists(outFolder)) + fs::create_directory(outFolder); - // Read the input SfM scene - SfMData sfmData; - if(!sfmDataIO::Load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } + // Read the input SfM scene + SfMData sfmData; + if(!sfmDataIO::Load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } - int rangeEnd = sfmData.getViews().size(); + int rangeEnd = sfmData.getViews().size(); - // set range - if(rangeStart != -1) - { - if(rangeStart < 0 || rangeSize < 0 || - rangeStart > sfmData.getViews().size()) + // set range + if(rangeStart != -1) { - ALICEVISION_LOG_ERROR("Range is incorrect"); - return EXIT_FAILURE; - } + if(rangeStart < 0 || rangeSize < 0) + { + ALICEVISION_LOG_ERROR("Range is incorrect"); + return EXIT_FAILURE; + } - if(rangeStart + rangeSize > sfmData.views.size()) - rangeSize = sfmData.views.size() - rangeStart; + if(rangeStart + rangeSize > sfmData.views.size()) + rangeSize = sfmData.views.size() - rangeStart; - rangeEnd = rangeStart + rangeSize; - } - else - { - rangeStart = 0; - } + rangeEnd = rangeStart + rangeSize; + + if(rangeSize <= 0) + { + ALICEVISION_LOG_WARNING("Nothing to compute."); + return EXIT_SUCCESS; + } + } + else + { + rangeStart = 0; + } - // export - if(prepareDenseScene(sfmData, imagesFolders, masksFolders, rangeStart, rangeEnd, outFolder, outputFileType, saveMetadata, saveMatricesTxtFiles, evCorrection)) - return EXIT_SUCCESS; + // export + if(prepareDenseScene(sfmData, imagesFolders, masksFolders, rangeStart, rangeEnd, + outFolder, outputFileType, saveMetadata, saveMatricesTxtFiles, evCorrection)) + return EXIT_SUCCESS; - return EXIT_FAILURE; + return EXIT_FAILURE; }