// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de) #include "mvs/workspace.h" #include #include "util/threading.h" namespace colmap { namespace mvs { Workspace::Workspace(const Options& options) : options_(options) { StringToLower(&options_.input_type); model_.Read(options_.workspace_path, options_.workspace_format); if (options_.max_image_size > 0) { for (auto& image : model_.images) { image.Downsize(options_.max_image_size, options_.max_image_size); } } depth_map_path_ = EnsureTrailingSlash( JoinPaths(options_.workspace_path, options_.stereo_folder, "depth_maps")); normal_map_path_ = EnsureTrailingSlash(JoinPaths( options_.workspace_path, options_.stereo_folder, "normal_maps")); } std::string Workspace::GetFileName(const int image_idx) const { const auto& image_name = model_.GetImageName(image_idx); return StringPrintf("%s.%s.bin", image_name.c_str(), options_.input_type.c_str()); } void Workspace::Load(const std::vector& image_names) { const size_t num_images = model_.images.size(); bitmaps_.resize(num_images); depth_maps_.resize(num_images); normal_maps_.resize(num_images); auto LoadWorkspaceData = [&, this](const int image_idx) { const size_t width = model_.images.at(image_idx).GetWidth(); const size_t height = model_.images.at(image_idx).GetHeight(); // Read and rescale bitmap bitmaps_[image_idx] = std::make_unique(); bitmaps_[image_idx]->Read(GetBitmapPath(image_idx), options_.image_as_rgb); if (options_.max_image_size > 0) { bitmaps_[image_idx]->Rescale((int)width, (int)height); } // Read and rescale depth map depth_maps_[image_idx] = std::make_unique(); depth_maps_[image_idx]->Read(GetDepthMapPath(image_idx)); if (options_.max_image_size > 0) { depth_maps_[image_idx]->Downsize(width, height); } // Read and rescale normal map normal_maps_[image_idx] = std::make_unique(); normal_maps_[image_idx]->Read(GetNormalMapPath(image_idx)); if (options_.max_image_size > 0) { normal_maps_[image_idx]->Downsize(width, height); } }; const int num_threads = GetEffectiveNumThreads(options_.num_threads); ThreadPool thread_pool(num_threads); Timer timer; timer.Start(); std::cout << StringPrintf("Loading workspace data with %d threads...", num_threads) << std::endl; for (size_t i = 0; i < image_names.size(); ++i) { const int image_idx = model_.GetImageIdx(image_names[i]); if (HasBitmap(image_idx) && HasDepthMap(image_idx)) { thread_pool.AddTask(LoadWorkspaceData, image_idx); } else { std::cout << StringPrintf( "WARNING: Ignoring image %s, because input does not exist.", image_names[i].c_str()) << std::endl; } } thread_pool.Wait(); timer.PrintMinutes(); } const Bitmap& Workspace::GetBitmap(const int image_idx) { return *bitmaps_[image_idx]; } const DepthMap& Workspace::GetDepthMap(const int image_idx) { return *depth_maps_[image_idx]; } const NormalMap& Workspace::GetNormalMap(const int image_idx) { return *normal_maps_[image_idx]; } std::string Workspace::GetBitmapPath(const int image_idx) const { return model_.images.at(image_idx).GetPath(); } std::string Workspace::GetDepthMapPath(const int image_idx) const { return depth_map_path_ + GetFileName(image_idx); } std::string Workspace::GetNormalMapPath(const int image_idx) const { return normal_map_path_ + GetFileName(image_idx); } bool Workspace::HasBitmap(const int image_idx) const { return ExistsFile(GetBitmapPath(image_idx)); } bool Workspace::HasDepthMap(const int image_idx) const { return ExistsFile(GetDepthMapPath(image_idx)); } bool Workspace::HasNormalMap(const int image_idx) const { return ExistsFile(GetNormalMapPath(image_idx)); } CachedWorkspace::CachedImage::CachedImage(CachedImage&& other) { num_bytes = other.num_bytes; bitmap = std::move(other.bitmap); depth_map = std::move(other.depth_map); normal_map = std::move(other.normal_map); } CachedWorkspace::CachedImage& CachedWorkspace::CachedImage::operator=( CachedImage&& other) { if (this != &other) { num_bytes = other.num_bytes; bitmap = std::move(other.bitmap); depth_map = std::move(other.depth_map); normal_map = std::move(other.normal_map); } return *this; } CachedWorkspace::CachedWorkspace(const Options& options) : Workspace(options), cache_((size_t)(1024.0 * 1024.0 * 1024.0 * options.cache_size), [](const int) { return CachedImage(); }) {} const Bitmap& CachedWorkspace::GetBitmap(const int image_idx) { auto& cached_image = cache_.GetMutable(image_idx); if (!cached_image.bitmap) { cached_image.bitmap = std::make_unique(); cached_image.bitmap->Read(GetBitmapPath(image_idx), options_.image_as_rgb); if (options_.max_image_size > 0) { cached_image.bitmap->Rescale(model_.images.at(image_idx).GetWidth(), model_.images.at(image_idx).GetHeight()); } cached_image.num_bytes += cached_image.bitmap->NumBytes(); cache_.UpdateNumBytes(image_idx); } return *cached_image.bitmap; } const DepthMap& CachedWorkspace::GetDepthMap(const int image_idx) { auto& cached_image = cache_.GetMutable(image_idx); if (!cached_image.depth_map) { cached_image.depth_map = std::make_unique(); cached_image.depth_map->Read(GetDepthMapPath(image_idx)); if (options_.max_image_size > 0) { cached_image.depth_map->Downsize(model_.images.at(image_idx).GetWidth(), model_.images.at(image_idx).GetHeight()); } cached_image.num_bytes += cached_image.depth_map->GetNumBytes(); cache_.UpdateNumBytes(image_idx); } return *cached_image.depth_map; } const NormalMap& CachedWorkspace::GetNormalMap(const int image_idx) { auto& cached_image = cache_.GetMutable(image_idx); if (!cached_image.normal_map) { cached_image.normal_map = std::make_unique(); cached_image.normal_map->Read(GetNormalMapPath(image_idx)); if (options_.max_image_size > 0) { cached_image.normal_map->Downsize( model_.images.at(image_idx).GetWidth(), model_.images.at(image_idx).GetHeight()); } cached_image.num_bytes += cached_image.normal_map->GetNumBytes(); cache_.UpdateNumBytes(image_idx); } return *cached_image.normal_map; } void ImportPMVSWorkspace(const Workspace& workspace, const std::string& option_name) { const std::string& workspace_path = workspace.GetOptions().workspace_path; const std::string& stereo_folder = workspace.GetOptions().stereo_folder; CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder)); CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder, "depth_maps")); CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder, "normal_maps")); CreateDirIfNotExists( JoinPaths(workspace_path, stereo_folder, "consistency_graphs")); const auto option_lines = ReadTextFileLines(JoinPaths(workspace_path, option_name)); for (const auto& line : option_lines) { if (!StringStartsWith(line, "timages")) { continue; } const auto elems = StringSplit(line, " "); int num_images = std::stoull(elems[1]); std::vector image_idxs; if (num_images == -1) { CHECK_EQ(elems.size(), 4); const int range_lower = std::stoull(elems[2]); const int range_upper = std::stoull(elems[3]); CHECK_LT(range_lower, range_upper); num_images = range_upper - range_lower; image_idxs.resize(num_images); std::iota(image_idxs.begin(), image_idxs.end(), range_lower); } else { CHECK_EQ(num_images + 2, elems.size()); image_idxs.reserve(num_images); for (size_t i = 2; i < elems.size(); ++i) { const int image_idx = std::stoull(elems[i]); image_idxs.push_back(image_idx); } } std::vector image_names; image_names.reserve(num_images); for (const auto image_idx : image_idxs) { const std::string image_name = workspace.GetModel().GetImageName(image_idx); image_names.push_back(image_name); } const auto& overlapping_images = workspace.GetModel().GetMaxOverlappingImagesFromPMVS(); const auto patch_match_path = JoinPaths(workspace_path, stereo_folder, "patch-match.cfg"); const auto fusion_path = JoinPaths(workspace_path, stereo_folder, "fusion.cfg"); std::ofstream patch_match_file(patch_match_path, std::ios::trunc); std::ofstream fusion_file(fusion_path, std::ios::trunc); CHECK(patch_match_file.is_open()) << patch_match_path; CHECK(fusion_file.is_open()) << fusion_path; for (size_t i = 0; i < image_names.size(); ++i) { const auto& ref_image_name = image_names[i]; patch_match_file << ref_image_name << std::endl; if (overlapping_images.empty()) { patch_match_file << "__auto__, 20" << std::endl; } else { for (const int image_idx : overlapping_images[i]) { patch_match_file << workspace.GetModel().GetImageName(image_idx) << ", "; } patch_match_file << std::endl; } fusion_file << ref_image_name << std::endl; } } } } // namespace mvs } // namespace colmap