| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "mvs/workspace.h" |
| |
|
| | #include <numeric> |
| |
|
| | #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<std::string>& 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(); |
| |
|
| | |
| | bitmaps_[image_idx] = std::make_unique<Bitmap>(); |
| | 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); |
| | } |
| |
|
| | |
| | depth_maps_[image_idx] = std::make_unique<DepthMap>(); |
| | depth_maps_[image_idx]->Read(GetDepthMapPath(image_idx)); |
| | if (options_.max_image_size > 0) { |
| | depth_maps_[image_idx]->Downsize(width, height); |
| | } |
| |
|
| | |
| | normal_maps_[image_idx] = std::make_unique<NormalMap>(); |
| | 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<Bitmap>(); |
| | 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<DepthMap>(); |
| | 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<NormalMap>(); |
| | 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<int> 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<std::string> 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; |
| | } |
| | } |
| | } |
| |
|
| | } |
| | } |
| |
|