/src/draco/src/draco/point_cloud/point_cloud.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 The Draco Authors. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | #include "draco/point_cloud/point_cloud.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <unordered_map> |
19 | | #include <utility> |
20 | | |
21 | | #ifdef DRACO_TRANSCODER_SUPPORTED |
22 | | #include "draco/attributes/point_attribute.h" |
23 | | #endif |
24 | | |
25 | | namespace draco { |
26 | | |
27 | 801 | PointCloud::PointCloud() : num_points_(0) {} |
28 | | |
29 | | #ifdef DRACO_TRANSCODER_SUPPORTED |
30 | | void PointCloud::Copy(const PointCloud &src) { |
31 | | num_points_ = src.num_points_; |
32 | | for (int i = 0; i < GeometryAttribute::NAMED_ATTRIBUTES_COUNT; ++i) { |
33 | | named_attribute_index_[i] = src.named_attribute_index_[i]; |
34 | | } |
35 | | attributes_.resize(src.attributes_.size()); |
36 | | for (int i = 0; i < src.attributes_.size(); ++i) { |
37 | | attributes_[i] = std::unique_ptr<PointAttribute>(new PointAttribute()); |
38 | | attributes_[i]->CopyFrom(*src.attributes_[i]); |
39 | | } |
40 | | compression_enabled_ = src.compression_enabled_; |
41 | | compression_options_ = src.compression_options_; |
42 | | CopyMetadata(src); |
43 | | } |
44 | | |
45 | | void PointCloud::CopyMetadata(const PointCloud &src) { |
46 | | if (src.metadata_ == nullptr) { |
47 | | metadata_ = nullptr; |
48 | | } else { |
49 | | // Copy base metadata. |
50 | | const GeometryMetadata *const metadata = src.metadata_.get(); |
51 | | metadata_.reset(new GeometryMetadata(*metadata)); |
52 | | } |
53 | | } |
54 | | #endif |
55 | | |
56 | 181 | int32_t PointCloud::NumNamedAttributes(GeometryAttribute::Type type) const { |
57 | 181 | if (type == GeometryAttribute::INVALID || |
58 | 181 | type >= GeometryAttribute::NAMED_ATTRIBUTES_COUNT) { |
59 | 0 | return 0; |
60 | 0 | } |
61 | 181 | return static_cast<int32_t>(named_attribute_index_[type].size()); |
62 | 181 | } |
63 | | |
64 | 181 | int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type) const { |
65 | 181 | return GetNamedAttributeId(type, 0); |
66 | 181 | } |
67 | | |
68 | | int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type, |
69 | 181 | int i) const { |
70 | 181 | if (NumNamedAttributes(type) <= i) { |
71 | 0 | return -1; |
72 | 0 | } |
73 | 181 | return named_attribute_index_[type][i]; |
74 | 181 | } |
75 | | |
76 | | const PointAttribute *PointCloud::GetNamedAttribute( |
77 | 0 | GeometryAttribute::Type type) const { |
78 | 0 | return GetNamedAttribute(type, 0); |
79 | 0 | } |
80 | | |
81 | | const PointAttribute *PointCloud::GetNamedAttribute( |
82 | 0 | GeometryAttribute::Type type, int i) const { |
83 | 0 | const int32_t att_id = GetNamedAttributeId(type, i); |
84 | 0 | if (att_id == -1) { |
85 | 0 | return nullptr; |
86 | 0 | } |
87 | 0 | return attributes_[att_id].get(); |
88 | 0 | } |
89 | | |
90 | | const PointAttribute *PointCloud::GetNamedAttributeByUniqueId( |
91 | 0 | GeometryAttribute::Type type, uint32_t unique_id) const { |
92 | 0 | for (size_t att_id = 0; att_id < named_attribute_index_[type].size(); |
93 | 0 | ++att_id) { |
94 | 0 | if (attributes_[named_attribute_index_[type][att_id]]->unique_id() == |
95 | 0 | unique_id) { |
96 | 0 | return attributes_[named_attribute_index_[type][att_id]].get(); |
97 | 0 | } |
98 | 0 | } |
99 | 0 | return nullptr; |
100 | 0 | } |
101 | | |
102 | | const PointAttribute *PointCloud::GetAttributeByUniqueId( |
103 | 0 | uint32_t unique_id) const { |
104 | 0 | const int32_t att_id = GetAttributeIdByUniqueId(unique_id); |
105 | 0 | if (att_id == -1) { |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | 0 | return attributes_[att_id].get(); |
109 | 0 | } |
110 | | |
111 | | #ifdef DRACO_TRANSCODER_SUPPORTED |
112 | | const PointAttribute *PointCloud::GetNamedAttributeByName( |
113 | | GeometryAttribute::Type type, const std::string &name) const { |
114 | | const auto &index = named_attribute_index_; |
115 | | for (size_t i = 0; i < index[type].size(); ++i) { |
116 | | const PointAttribute *const att = attributes_[index[type][i]].get(); |
117 | | if (att->name() == name) { |
118 | | return att; |
119 | | } |
120 | | } |
121 | | return nullptr; |
122 | | } |
123 | | #endif // DRACO_TRANSCODER_SUPPORTED |
124 | | |
125 | 0 | int32_t PointCloud::GetAttributeIdByUniqueId(uint32_t unique_id) const { |
126 | 0 | for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) { |
127 | 0 | if (attributes_[att_id]->unique_id() == unique_id) { |
128 | 0 | return static_cast<int32_t>(att_id); |
129 | 0 | } |
130 | 0 | } |
131 | 0 | return -1; |
132 | 0 | } |
133 | | |
134 | 9.97k | int PointCloud::AddAttribute(std::unique_ptr<PointAttribute> pa) { |
135 | 9.97k | SetAttribute(static_cast<int>(attributes_.size()), std::move(pa)); |
136 | 9.97k | return static_cast<int>(attributes_.size() - 1); |
137 | 9.97k | } |
138 | | |
139 | | int PointCloud::AddAttribute( |
140 | | const GeometryAttribute &att, bool identity_mapping, |
141 | 0 | AttributeValueIndex::ValueType num_attribute_values) { |
142 | 0 | auto pa = CreateAttribute(att, identity_mapping, num_attribute_values); |
143 | 0 | if (!pa) { |
144 | 0 | return -1; |
145 | 0 | } |
146 | 0 | const int32_t att_id = AddAttribute(std::move(pa)); |
147 | 0 | return att_id; |
148 | 0 | } |
149 | | |
150 | | std::unique_ptr<PointAttribute> PointCloud::CreateAttribute( |
151 | | const GeometryAttribute &att, bool identity_mapping, |
152 | 0 | AttributeValueIndex::ValueType num_attribute_values) const { |
153 | 0 | if (att.attribute_type() == GeometryAttribute::INVALID) { |
154 | 0 | return nullptr; |
155 | 0 | } |
156 | 0 | std::unique_ptr<PointAttribute> pa = |
157 | 0 | std::unique_ptr<PointAttribute>(new PointAttribute(att)); |
158 | | // Initialize point cloud specific attribute data. |
159 | 0 | if (!identity_mapping) { |
160 | | // First create mapping between indices. |
161 | 0 | pa->SetExplicitMapping(num_points_); |
162 | 0 | } else { |
163 | 0 | pa->SetIdentityMapping(); |
164 | 0 | num_attribute_values = std::max(num_points_, num_attribute_values); |
165 | 0 | } |
166 | 0 | if (num_attribute_values > 0) { |
167 | 0 | pa->Reset(num_attribute_values); |
168 | 0 | } |
169 | 0 | return pa; |
170 | 0 | } |
171 | | |
172 | 9.97k | void PointCloud::SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa) { |
173 | 9.97k | DRACO_DCHECK(att_id >= 0); |
174 | 9.97k | if (static_cast<int>(attributes_.size()) <= att_id) { |
175 | 9.97k | attributes_.resize(att_id + 1); |
176 | 9.97k | } |
177 | 9.97k | if (pa->attribute_type() < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) { |
178 | 9.97k | named_attribute_index_[pa->attribute_type()].push_back(att_id); |
179 | 9.97k | } |
180 | 9.97k | pa->set_unique_id(att_id); |
181 | 9.97k | attributes_[att_id] = std::move(pa); |
182 | 9.97k | } |
183 | | |
184 | 0 | void PointCloud::DeleteAttribute(int att_id) { |
185 | 0 | if (att_id < 0 || att_id >= attributes_.size()) { |
186 | 0 | return; // Attribute does not exist. |
187 | 0 | } |
188 | 0 | const GeometryAttribute::Type att_type = |
189 | 0 | attributes_[att_id]->attribute_type(); |
190 | 0 | const uint32_t unique_id = attribute(att_id)->unique_id(); |
191 | 0 | attributes_.erase(attributes_.begin() + att_id); |
192 | | // Remove metadata if applicable. |
193 | 0 | if (metadata_) { |
194 | 0 | metadata_->DeleteAttributeMetadataByUniqueId(unique_id); |
195 | 0 | } |
196 | | |
197 | | // Remove the attribute from the named attribute list if applicable. |
198 | 0 | if (att_type < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) { |
199 | 0 | const auto it = std::find(named_attribute_index_[att_type].begin(), |
200 | 0 | named_attribute_index_[att_type].end(), att_id); |
201 | 0 | if (it != named_attribute_index_[att_type].end()) { |
202 | 0 | named_attribute_index_[att_type].erase(it); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | // Update ids of all subsequent named attributes (decrease them by one). |
207 | 0 | for (int i = 0; i < GeometryAttribute::NAMED_ATTRIBUTES_COUNT; ++i) { |
208 | 0 | for (int j = 0; j < named_attribute_index_[i].size(); ++j) { |
209 | 0 | if (named_attribute_index_[i][j] > att_id) { |
210 | 0 | named_attribute_index_[i][j]--; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | #ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED |
217 | 0 | void PointCloud::DeduplicatePointIds() { |
218 | | // Hashing function for a single vertex. |
219 | 0 | auto point_hash = [this](PointIndex p) { |
220 | 0 | PointIndex::ValueType hash = 0; |
221 | 0 | for (int32_t i = 0; i < this->num_attributes(); ++i) { |
222 | 0 | const AttributeValueIndex att_id = attribute(i)->mapped_index(p); |
223 | 0 | hash = static_cast<uint32_t>(HashCombine(att_id.value(), hash)); |
224 | 0 | } |
225 | 0 | return hash; |
226 | 0 | }; |
227 | | // Comparison function between two vertices. |
228 | 0 | auto point_compare = [this](PointIndex p0, PointIndex p1) { |
229 | 0 | for (int32_t i = 0; i < this->num_attributes(); ++i) { |
230 | 0 | const AttributeValueIndex att_id0 = attribute(i)->mapped_index(p0); |
231 | 0 | const AttributeValueIndex att_id1 = attribute(i)->mapped_index(p1); |
232 | 0 | if (att_id0 != att_id1) { |
233 | 0 | return false; |
234 | 0 | } |
235 | 0 | } |
236 | 0 | return true; |
237 | 0 | }; |
238 | |
|
239 | 0 | std::unordered_map<PointIndex, PointIndex, decltype(point_hash), |
240 | 0 | decltype(point_compare)> |
241 | 0 | unique_point_map(num_points_, point_hash, point_compare); |
242 | 0 | int32_t num_unique_points = 0; |
243 | 0 | IndexTypeVector<PointIndex, PointIndex> index_map(num_points_); |
244 | 0 | std::vector<PointIndex> unique_points; |
245 | | // Go through all vertices and find their duplicates. |
246 | 0 | for (PointIndex i(0); i < num_points_; ++i) { |
247 | 0 | const auto it = unique_point_map.find(i); |
248 | 0 | if (it != unique_point_map.end()) { |
249 | 0 | index_map[i] = it->second; |
250 | 0 | } else { |
251 | 0 | unique_point_map.insert(std::make_pair(i, PointIndex(num_unique_points))); |
252 | 0 | index_map[i] = num_unique_points++; |
253 | 0 | unique_points.push_back(i); |
254 | 0 | } |
255 | 0 | } |
256 | 0 | if (num_unique_points == num_points_) { |
257 | 0 | return; // All vertices are already unique. |
258 | 0 | } |
259 | | |
260 | 0 | ApplyPointIdDeduplication(index_map, unique_points); |
261 | 0 | set_num_points(num_unique_points); |
262 | 0 | } |
263 | | |
264 | | void PointCloud::ApplyPointIdDeduplication( |
265 | | const IndexTypeVector<PointIndex, PointIndex> &id_map, |
266 | 0 | const std::vector<PointIndex> &unique_point_ids) { |
267 | 0 | int32_t num_unique_points = 0; |
268 | 0 | for (PointIndex i : unique_point_ids) { |
269 | 0 | const PointIndex new_point_id = id_map[i]; |
270 | 0 | if (new_point_id >= num_unique_points) { |
271 | | // New unique vertex reached. Copy attribute indices to the proper |
272 | | // position. |
273 | 0 | for (int32_t a = 0; a < num_attributes(); ++a) { |
274 | 0 | attribute(a)->SetPointMapEntry(new_point_id, |
275 | 0 | attribute(a)->mapped_index(i)); |
276 | 0 | } |
277 | 0 | num_unique_points = new_point_id.value() + 1; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | for (int32_t a = 0; a < num_attributes(); ++a) { |
281 | 0 | attribute(a)->SetExplicitMapping(num_unique_points); |
282 | 0 | } |
283 | 0 | } |
284 | | #endif |
285 | | |
286 | | #ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED |
287 | 0 | bool PointCloud::DeduplicateAttributeValues() { |
288 | | // Go over all attributes and create mapping between duplicate entries. |
289 | 0 | if (num_points() == 0) { |
290 | 0 | return true; // Nothing to deduplicate. |
291 | 0 | } |
292 | | // Deduplicate all attributes. |
293 | 0 | for (int32_t att_id = 0; att_id < num_attributes(); ++att_id) { |
294 | 0 | if (!attribute(att_id)->DeduplicateValues(*attribute(att_id))) { |
295 | 0 | return false; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | return true; |
299 | 0 | } |
300 | | #endif |
301 | | |
302 | | // TODO(b/199760503): Consider to cache the BBox. |
303 | 0 | BoundingBox PointCloud::ComputeBoundingBox() const { |
304 | 0 | BoundingBox bounding_box; |
305 | 0 | auto pc_att = GetNamedAttribute(GeometryAttribute::POSITION); |
306 | 0 | if (pc_att == nullptr) { |
307 | | // Return default invalid bounding box. |
308 | 0 | return bounding_box; |
309 | 0 | } |
310 | | |
311 | | // TODO(b/199760503): Make the BoundingBox a template type, it may not be easy |
312 | | // because PointCloud is not a template. |
313 | | // Or simply add some preconditioning here to make sure the position attribute |
314 | | // is valid, because the current code works only if the position attribute is |
315 | | // defined with 3 components of DT_FLOAT32. |
316 | | // Consider using pc_att->ConvertValue<float, 3>(i, &p[0]) (Enforced |
317 | | // transformation from Vector with any dimension to Vector3f) |
318 | 0 | Vector3f p; |
319 | 0 | for (AttributeValueIndex i(0); i < static_cast<uint32_t>(pc_att->size()); |
320 | 0 | ++i) { |
321 | 0 | pc_att->GetValue(i, &p[0]); |
322 | 0 | bounding_box.Update(p); |
323 | 0 | } |
324 | 0 | return bounding_box; |
325 | 0 | } |
326 | | } // namespace draco |