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