diff --git a/meshroom/aliceVision/MaskRandomApplying.py b/meshroom/aliceVision/MaskRandomApplying.py new file mode 100644 index 0000000000..230283867b --- /dev/null +++ b/meshroom/aliceVision/MaskRandomApplying.py @@ -0,0 +1,51 @@ +__version__ = "1.0" + +from meshroom.core import desc +from meshroom.core.utils import DESCRIBER_TYPES, VERBOSE_LEVEL + +import os.path + + +class MaskRandomApplying(desc.AVCommandLineNode): + commandLine = "aliceVision_maskRandomApplying {allParams}" + + size = desc.DynamicNodeSize("input") + category = "Utils" + documentation = """ Apply random values to images on the pixel whose mask value is 0 """ + + inputs = [ + desc.File( + name="input", + label="SfmData", + description="Input SfmData with the list of views to process", + value="", + ), + desc.File( + name="masks", + label="Input Masks", + description="Input Masks assumed to have same names than input images", + value="", + ), + desc.ChoiceParam( + name="verboseLevel", + label="Verbose Level", + description="Verbosity level (fatal, error, warning, info, debug, trace).", + values=VERBOSE_LEVEL, + value="info", + ) + ] + + outputs = [ + desc.File( + name="outputSfmData", + label="Output", + description="Path to the output sfmData.", + value="{nodeCacheFolder}/sfmData.abc", + ), + desc.File( + name="outputDirectory", + label="Output Images Directory", + description="Path to the directory with modified images.", + value="{nodeCacheFolder}", + ) + ] diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index 4eed8fc848..c6d9ed9161 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -71,6 +71,18 @@ if (ALICEVISION_BUILD_SFM) ${Boost_LIBRARIES} ) + alicevision_add_software(aliceVision_maskRandomApplying + SOURCE main_maskRandomApplying.cpp + FOLDER ${FOLDER_SOFTWARE_UTILS} + LINKS aliceVision_system + aliceVision_cmdline + aliceVision_image + aliceVision_sfm + aliceVision_sfmData + aliceVision_sfmDataIO + ${Boost_LIBRARIES} + ) + # Track builder alicevision_add_software(aliceVision_tracksSimulating SOURCE main_tracksSimulating.cpp diff --git a/src/software/utils/main_maskRandomApplying.cpp b/src/software/utils/main_maskRandomApplying.cpp new file mode 100644 index 0000000000..b335061808 --- /dev/null +++ b/src/software/utils/main_maskRandomApplying.cpp @@ -0,0 +1,160 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +// These constants define the current software version. +// They must be updated when the command line is changed. +#define ALICEVISION_SOFTWARE_VERSION_MAJOR 1 +#define ALICEVISION_SOFTWARE_VERSION_MINOR 0 + +using namespace aliceVision; + +namespace po = boost::program_options; + + +int aliceVision_main(int argc, char** argv) +{ + // command-line parameters + std::string inputSfmDataFilename; + std::string outputSfmDataFilename; + std::string outputDirectory; + std::string masks; + + // clang-format off + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("input,i", po::value(&inputSfmDataFilename)->required(), + "Input SfmData.") + ("outputSfmData", po::value(&outputSfmDataFilename)->required(), + "Output SfmData.") + ("masks", po::value(&masks)->required(), + "Input Masks.") + ("outputDirectory", po::value(&outputDirectory)->required(), + "Output directory with modified images."); + // clang-format on + + CmdLine cmdline("AliceVision maskRandomApplying"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // set maxThreads + HardwareContext hwc = cmdline.getHardwareContext(); + omp_set_num_threads(hwc.getMaxThreads()); + + if (!utils::exists(outputDirectory)) + { + ALICEVISION_LOG_ERROR("Output Directory does not exists."); + return EXIT_FAILURE; + } + + if (!utils::exists(masks)) + { + ALICEVISION_LOG_ERROR("Masks Directory does not exists."); + return EXIT_FAILURE; + } + + // Load input scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, inputSfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << inputSfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; + } + + // List input images + std::vector viewIds; + std::vector imagePaths; + for (const auto [idview, view]: sfmData.getViews().valueRange()) + { + std::filesystem::path p(view.getImage().getImagePath()); + imagePaths.push_back(p); + viewIds.push_back(idview); + } + + std::mt19937 gen; + std::uniform_real_distribution dis(0.0f, 1.0f); + + #pragma omp parallel for schedule(dynamic) + for (int chunkId = 0; chunkId < imagePaths.size(); chunkId++) + { + IndexT viewId = viewIds[chunkId]; + std::filesystem::path path = imagePaths[chunkId]; + + + std::string maskFileName = masks + "/" + path.filename().string(); + if (!utils::exists(maskFileName)) + { + ALICEVISION_LOG_INFO(maskFileName << " not found."); + continue; + } + + std::string outFileName = outputDirectory + "/" + path.filename().string(); + + + ALICEVISION_LOG_INFO("Processing " << path.string()); + + image::Image img; + aliceVision::image::readImage(path.string(), img, image::EImageColorSpace::NO_CONVERSION); + + image::Image mask; + aliceVision::image::readImage(maskFileName, mask, image::EImageColorSpace::NO_CONVERSION); + + if (img.height() != mask.height()) + { + ALICEVISION_LOG_INFO("Incompatible sizes."); + continue; + } + + if (img.width() != mask.width()) + { + ALICEVISION_LOG_INFO("Incompatible sizes."); + continue; + } + + for (int i = 0; i < mask.height(); i++) + { + for (int j = 0; j < mask.width(); j++) + { + if (!mask(i, j)) + { + auto & p = img(i, j); + p.r() = dis(gen); + p.g() = dis(gen); + p.b() = dis(gen); + } + } + } + + aliceVision::image::ImageWriteOptions wopt; + aliceVision::image::writeImage(outFileName, img, wopt); + + sfmData.getView(viewId).getImage().setImagePath(outFileName); + } + + if (!sfmDataIO::save(sfmData, outputSfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outputSfmDataFilename << "'"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} +