1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31
32 #include "mvs/workspace.h"
33
34 #include <numeric>
35
36 #include "util/misc.h"
37
38 namespace colmap {
39 namespace mvs {
40
CachedImage()41 Workspace::CachedImage::CachedImage() {}
42
CachedImage(CachedImage && other)43 Workspace::CachedImage::CachedImage(CachedImage&& other) {
44 num_bytes = other.num_bytes;
45 bitmap = std::move(other.bitmap);
46 depth_map = std::move(other.depth_map);
47 normal_map = std::move(other.normal_map);
48 }
49
operator =(CachedImage && other)50 Workspace::CachedImage& Workspace::CachedImage::operator=(CachedImage&& other) {
51 if (this != &other) {
52 num_bytes = other.num_bytes;
53 bitmap = std::move(other.bitmap);
54 depth_map = std::move(other.depth_map);
55 normal_map = std::move(other.normal_map);
56 }
57 return *this;
58 }
59
NumBytes() const60 size_t Workspace::CachedImage::NumBytes() const { return num_bytes; }
61
Workspace(const Options & options)62 Workspace::Workspace(const Options& options)
63 : options_(options),
64 cache_(1024 * 1024 * 1024 * options_.cache_size,
65 [](const int) { return CachedImage(); }) {
66 StringToLower(&options_.input_type);
67 model_.Read(options_.workspace_path, options_.workspace_format);
68 if (options_.max_image_size > 0) {
69 for (auto& image : model_.images) {
70 image.Downsize(options_.max_image_size, options_.max_image_size);
71 }
72 }
73
74 depth_map_path_ = EnsureTrailingSlash(
75 JoinPaths(options_.workspace_path, options_.stereo_folder, "depth_maps"));
76 normal_map_path_ = EnsureTrailingSlash(JoinPaths(
77 options_.workspace_path, options_.stereo_folder, "normal_maps"));
78 }
79
ClearCache()80 void Workspace::ClearCache() { cache_.Clear(); }
81
GetOptions() const82 const Workspace::Options& Workspace::GetOptions() const { return options_; }
83
GetModel() const84 const Model& Workspace::GetModel() const { return model_; }
85
GetBitmap(const int image_idx)86 const Bitmap& Workspace::GetBitmap(const int image_idx) {
87 auto& cached_image = cache_.GetMutable(image_idx);
88 if (!cached_image.bitmap) {
89 cached_image.bitmap.reset(new Bitmap());
90 cached_image.bitmap->Read(GetBitmapPath(image_idx), options_.image_as_rgb);
91 if (options_.max_image_size > 0) {
92 cached_image.bitmap->Rescale(model_.images.at(image_idx).GetWidth(),
93 model_.images.at(image_idx).GetHeight());
94 }
95 cached_image.num_bytes += cached_image.bitmap->NumBytes();
96 cache_.UpdateNumBytes(image_idx);
97 }
98 return *cached_image.bitmap;
99 }
100
GetDepthMap(const int image_idx)101 const DepthMap& Workspace::GetDepthMap(const int image_idx) {
102 auto& cached_image = cache_.GetMutable(image_idx);
103 if (!cached_image.depth_map) {
104 cached_image.depth_map.reset(new DepthMap());
105 cached_image.depth_map->Read(GetDepthMapPath(image_idx));
106 if (options_.max_image_size > 0) {
107 cached_image.depth_map->Downsize(model_.images.at(image_idx).GetWidth(),
108 model_.images.at(image_idx).GetHeight());
109 }
110 cached_image.num_bytes += cached_image.depth_map->GetNumBytes();
111 cache_.UpdateNumBytes(image_idx);
112 }
113 return *cached_image.depth_map;
114 }
115
GetNormalMap(const int image_idx)116 const NormalMap& Workspace::GetNormalMap(const int image_idx) {
117 auto& cached_image = cache_.GetMutable(image_idx);
118 if (!cached_image.normal_map) {
119 cached_image.normal_map.reset(new NormalMap());
120 cached_image.normal_map->Read(GetNormalMapPath(image_idx));
121 if (options_.max_image_size > 0) {
122 cached_image.normal_map->Downsize(
123 model_.images.at(image_idx).GetWidth(),
124 model_.images.at(image_idx).GetHeight());
125 }
126 cached_image.num_bytes += cached_image.normal_map->GetNumBytes();
127 cache_.UpdateNumBytes(image_idx);
128 }
129 return *cached_image.normal_map;
130 }
131
GetBitmapPath(const int image_idx) const132 std::string Workspace::GetBitmapPath(const int image_idx) const {
133 return model_.images.at(image_idx).GetPath();
134 }
135
GetDepthMapPath(const int image_idx) const136 std::string Workspace::GetDepthMapPath(const int image_idx) const {
137 return depth_map_path_ + GetFileName(image_idx);
138 }
139
GetNormalMapPath(const int image_idx) const140 std::string Workspace::GetNormalMapPath(const int image_idx) const {
141 return normal_map_path_ + GetFileName(image_idx);
142 }
143
HasBitmap(const int image_idx) const144 bool Workspace::HasBitmap(const int image_idx) const {
145 return ExistsFile(GetBitmapPath(image_idx));
146 }
147
HasDepthMap(const int image_idx) const148 bool Workspace::HasDepthMap(const int image_idx) const {
149 return ExistsFile(GetDepthMapPath(image_idx));
150 }
151
HasNormalMap(const int image_idx) const152 bool Workspace::HasNormalMap(const int image_idx) const {
153 return ExistsFile(GetNormalMapPath(image_idx));
154 }
155
GetFileName(const int image_idx) const156 std::string Workspace::GetFileName(const int image_idx) const {
157 const auto& image_name = model_.GetImageName(image_idx);
158 return StringPrintf("%s.%s.bin", image_name.c_str(),
159 options_.input_type.c_str());
160 }
161
ImportPMVSWorkspace(const Workspace & workspace,const std::string & option_name)162 void ImportPMVSWorkspace(const Workspace& workspace,
163 const std::string& option_name) {
164 const std::string& workspace_path = workspace.GetOptions().workspace_path;
165 const std::string& stereo_folder = workspace.GetOptions().stereo_folder;
166
167 CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder));
168 CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder, "depth_maps"));
169 CreateDirIfNotExists(JoinPaths(workspace_path, stereo_folder, "normal_maps"));
170 CreateDirIfNotExists(
171 JoinPaths(workspace_path, stereo_folder, "consistency_graphs"));
172
173 const auto option_lines =
174 ReadTextFileLines(JoinPaths(workspace_path, option_name));
175 for (const auto& line : option_lines) {
176 if (!StringStartsWith(line, "timages")) {
177 continue;
178 }
179
180 const auto elems = StringSplit(line, " ");
181 int num_images = std::stoull(elems[1]);
182
183 std::vector<int> image_idxs;
184 if (num_images == -1) {
185 CHECK_EQ(elems.size(), 4);
186 const int range_lower = std::stoull(elems[2]);
187 const int range_upper = std::stoull(elems[3]);
188 CHECK_LT(range_lower, range_upper);
189 num_images = range_upper - range_lower;
190 image_idxs.resize(num_images);
191 std::iota(image_idxs.begin(), image_idxs.end(), range_lower);
192 } else {
193 CHECK_EQ(num_images + 2, elems.size());
194 image_idxs.reserve(num_images);
195 for (size_t i = 2; i < elems.size(); ++i) {
196 const int image_idx = std::stoull(elems[i]);
197 image_idxs.push_back(image_idx);
198 }
199 }
200
201 std::vector<std::string> image_names;
202 image_names.reserve(num_images);
203 for (const auto image_idx : image_idxs) {
204 const std::string image_name =
205 workspace.GetModel().GetImageName(image_idx);
206 image_names.push_back(image_name);
207 }
208
209 const auto& overlapping_images =
210 workspace.GetModel().GetMaxOverlappingImagesFromPMVS();
211
212 const auto patch_match_path =
213 JoinPaths(workspace_path, stereo_folder, "patch-match.cfg");
214 const auto fusion_path =
215 JoinPaths(workspace_path, stereo_folder, "fusion.cfg");
216 std::ofstream patch_match_file(patch_match_path, std::ios::trunc);
217 std::ofstream fusion_file(fusion_path, std::ios::trunc);
218 CHECK(patch_match_file.is_open()) << patch_match_path;
219 CHECK(fusion_file.is_open()) << fusion_path;
220 for (size_t i = 0; i < image_names.size(); ++i) {
221 const auto& ref_image_name = image_names[i];
222 patch_match_file << ref_image_name << std::endl;
223 if (overlapping_images.empty()) {
224 patch_match_file << "__auto__, 20" << std::endl;
225 } else {
226 for (const int image_idx : overlapping_images[i]) {
227 patch_match_file << workspace.GetModel().GetImageName(image_idx)
228 << ", ";
229 }
230 patch_match_file << std::endl;
231 }
232 fusion_file << ref_image_name << std::endl;
233 }
234 }
235 }
236
237 } // namespace mvs
238 } // namespace colmap
239