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 // TODO(b/199760123): Make this a wrapper using 16 // DynamicIntegerPointsKdTreeEncoder. 17 #ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ 18 #define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ 19 20 #include <algorithm> 21 #include <array> 22 #include <memory> 23 #include <vector> 24 25 #include "draco/compression/bit_coders/adaptive_rans_bit_encoder.h" 26 #include "draco/compression/bit_coders/direct_bit_encoder.h" 27 #include "draco/compression/bit_coders/folded_integer_bit_encoder.h" 28 #include "draco/compression/bit_coders/rans_bit_encoder.h" 29 #include "draco/compression/point_cloud/algorithms/point_cloud_types.h" 30 #include "draco/compression/point_cloud/algorithms/queuing_policy.h" 31 #include "draco/core/bit_utils.h" 32 #include "draco/core/encoder_buffer.h" 33 #include "draco/core/math_utils.h" 34 35 namespace draco { 36 37 // This policy class provides several configurations for the encoder that allow 38 // to trade speed vs compression rate. Level 0 is fastest while 10 is the best 39 // compression rate. The decoder must select the same level. 40 template <int compression_level_t> 41 struct IntegerPointsKdTreeEncoderCompressionPolicy 42 : public IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t - 43 1> {}; 44 45 template <> 46 struct IntegerPointsKdTreeEncoderCompressionPolicy<0> { 47 typedef DirectBitEncoder NumbersEncoder; 48 typedef DirectBitEncoder AxisEncoder; 49 typedef DirectBitEncoder HalfEncoder; 50 typedef DirectBitEncoder RemainingBitsEncoder; 51 static constexpr bool select_axis = false; 52 53 template <class T> 54 using QueuingStrategy = Stack<T>; 55 }; 56 57 template <> 58 struct IntegerPointsKdTreeEncoderCompressionPolicy<2> 59 : public IntegerPointsKdTreeEncoderCompressionPolicy<1> { 60 typedef RAnsBitEncoder NumbersEncoder; 61 }; 62 63 template <> 64 struct IntegerPointsKdTreeEncoderCompressionPolicy<4> 65 : public IntegerPointsKdTreeEncoderCompressionPolicy<3> { 66 typedef FoldedBit32Encoder<RAnsBitEncoder> NumbersEncoder; 67 }; 68 69 template <> 70 struct IntegerPointsKdTreeEncoderCompressionPolicy<6> 71 : public IntegerPointsKdTreeEncoderCompressionPolicy<5> { 72 static constexpr bool select_axis = true; 73 }; 74 75 template <> 76 struct IntegerPointsKdTreeEncoderCompressionPolicy<8> 77 : public IntegerPointsKdTreeEncoderCompressionPolicy<7> { 78 typedef FoldedBit32Encoder<AdaptiveRAnsBitEncoder> NumbersEncoder; 79 template <class T> 80 using QueuingStrategy = Queue<T>; 81 }; 82 83 template <> 84 struct IntegerPointsKdTreeEncoderCompressionPolicy<10> 85 : public IntegerPointsKdTreeEncoderCompressionPolicy<9> { 86 template <class T> 87 using QueuingStrategy = PriorityQueue<T>; 88 }; 89 90 // This class encodes a given integer point cloud based on the point cloud 91 // compression algorithm in: 92 // Olivier Devillers and Pierre-Marie Gandoin 93 // "Geometric compression for interactive transmission" 94 // 95 // In principle the algorithm keeps on splitting the point cloud in the middle 96 // while alternating the axes. In 3D this results in an Octree like structure. 97 // In each step we encode the number of points in the first half. 98 // The algorithm does not preserve the order of points. 99 // 100 // However, the algorithm here differs from the original as follows: 101 // The algorithm keeps on splitting the point cloud in the middle of the axis 102 // that keeps the point cloud as clustered as possible, which gives a better 103 // compression rate. 104 // The number of points is encode by the deviation from the half of the points 105 // in the smaller half of the two. This results in a better compression rate as 106 // there are more leading zeros, which is then compressed better by the 107 // arithmetic encoding. 108 // 109 // |PointDiT| is a type representing a point with uint32_t coordinates. 110 // must provide construction from three uint32_t and operator[]. 111 template <class PointDiT, int compression_level_t> 112 class IntegerPointsKdTreeEncoder { 113 typedef IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t> 114 Policy; 115 typedef typename Policy::NumbersEncoder NumbersEncoder; 116 typedef typename Policy::AxisEncoder AxisEncoder; 117 typedef typename Policy::HalfEncoder HalfEncoder; 118 typedef typename Policy::RemainingBitsEncoder RemainingBitsEncoder; 119 120 public: 121 IntegerPointsKdTreeEncoder() : bit_length_(0) {} 122 123 // Encodes an integer point cloud given by [begin,end) into buffer. 124 // |bit_length| gives the highest bit used for all coordinates. 125 template <class RandomAccessIteratorT> 126 bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end, 127 const uint32_t &bit_length, EncoderBuffer *buffer); 128 129 // Encodes an integer point cloud given by [begin,end) into buffer. 130 template <class RandomAccessIteratorT> 131 bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end, 132 EncoderBuffer *buffer) { 133 return EncodePoints(begin, end, 32, buffer); 134 } 135 136 private: 137 // For the sack of readability of code, we decided to make this exception 138 // from the naming scheme. 139 static constexpr int D = PointTraits<PointDiT>::Dimension(); 140 template <class RandomAccessIteratorT> 141 uint32_t GetAxis(RandomAccessIteratorT begin, RandomAccessIteratorT end, 142 const PointDiT &old_base, std::array<uint32_t, D> levels, 143 uint32_t last_axis); 144 145 template <class RandomAccessIteratorT> 146 void EncodeInternal(RandomAccessIteratorT begin, RandomAccessIteratorT end, 147 PointDiT old_base, std::array<uint32_t, D> levels, 148 uint32_t last_axis); 149 150 class Splitter { 151 public: 152 Splitter(int axis, uint32_t value) : axis_(axis), value_(value) {} 153 bool operator()(const PointDiT &a) { return a[axis_] < value_; } 154 155 private: 156 int axis_; 157 uint32_t value_; 158 }; 159 160 void EncodeNumber(int nbits, uint32_t value) { 161 numbers_encoder_.EncodeLeastSignificantBits32(nbits, value); 162 } 163 164 template <class RandomAccessIteratorT> 165 struct EncodingStatus { 166 EncodingStatus( 167 RandomAccessIteratorT begin_, RandomAccessIteratorT end_, 168 const PointDiT &old_base_, 169 std::array<uint32_t, PointTraits<PointDiT>::Dimension()> levels_, 170 uint32_t last_axis_) 171 : begin(begin_), 172 end(end_), 173 old_base(old_base_), 174 levels(levels_), 175 last_axis(last_axis_) { 176 num_remaining_points = end - begin; 177 } 178 179 RandomAccessIteratorT begin; 180 RandomAccessIteratorT end; 181 PointDiT old_base; 182 std::array<uint32_t, D> levels; 183 uint32_t last_axis; 184 uint32_t num_remaining_points; 185 friend bool operator<(const EncodingStatus &l, const EncodingStatus &r) { 186 return l.num_remaining_points < r.num_remaining_points; 187 } 188 }; 189 190 uint32_t bit_length_; 191 uint32_t num_points_; 192 NumbersEncoder numbers_encoder_; 193 RemainingBitsEncoder remaining_bits_encoder_; 194 AxisEncoder axis_encoder_; 195 HalfEncoder half_encoder_; 196 }; 197 198 template <class PointDiT, int compression_level_t> 199 template <class RandomAccessIteratorT> 200 bool IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodePoints( 201 RandomAccessIteratorT begin, RandomAccessIteratorT end, 202 const uint32_t &bit_length, EncoderBuffer *buffer) { 203 bit_length_ = bit_length; 204 num_points_ = end - begin; 205 206 buffer->Encode(bit_length_); 207 buffer->Encode(num_points_); 208 if (num_points_ == 0) { 209 return true; 210 } 211 212 numbers_encoder_.StartEncoding(); 213 remaining_bits_encoder_.StartEncoding(); 214 axis_encoder_.StartEncoding(); 215 half_encoder_.StartEncoding(); 216 217 EncodeInternal(begin, end, PointTraits<PointDiT>::Origin(), 218 PointTraits<PointDiT>::ZeroArray(), 0); 219 220 numbers_encoder_.EndEncoding(buffer); 221 remaining_bits_encoder_.EndEncoding(buffer); 222 axis_encoder_.EndEncoding(buffer); 223 half_encoder_.EndEncoding(buffer); 224 225 return true; 226 } 227 template <class PointDiT, int compression_level_t> 228 template <class RandomAccessIteratorT> 229 uint32_t IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::GetAxis( 230 RandomAccessIteratorT begin, RandomAccessIteratorT end, 231 const PointDiT &old_base, std::array<uint32_t, D> levels, 232 uint32_t last_axis) { 233 if (!Policy::select_axis) { 234 return DRACO_INCREMENT_MOD(last_axis, D); 235 } 236 237 // For many points this function selects the axis that should be used 238 // for the split by keeping as many points as possible bundled. 239 // In the best case we do not split the point cloud at all. 240 // For lower number of points, we simply choose the axis that is refined the 241 // least so far. 242 243 DRACO_DCHECK_EQ(true, end - begin != 0); 244 245 uint32_t best_axis = 0; 246 if (end - begin < 64) { 247 for (uint32_t axis = 1; axis < D; ++axis) { 248 if (levels[best_axis] > levels[axis]) { 249 best_axis = axis; 250 } 251 } 252 } else { 253 const uint32_t size = (end - begin); 254 std::array<uint32_t, D> num_remaining_bits = 255 PointTraits<PointDiT>::ZeroArray(); 256 for (int i = 0; i < D; i++) { 257 num_remaining_bits[i] = bit_length_ - levels[i]; 258 } 259 PointDiT split(old_base); 260 261 for (int i = 0; i < D; i++) { 262 if (num_remaining_bits[i]) { 263 split[i] += 1 << (num_remaining_bits[i] - 1); 264 } 265 } 266 267 std::array<uint32_t, D> deviations = PointTraits<PointDiT>::ZeroArray(); 268 for (auto it = begin; it != end; ++it) { 269 for (int i = 0; i < D; i++) { 270 deviations[i] += ((*it)[i] < split[i]); 271 } 272 } 273 for (int i = 0; i < D; i++) { 274 deviations[i] = std::max(size - deviations[i], deviations[i]); 275 } 276 277 uint32_t max_value = 0; 278 best_axis = 0; 279 for (int i = 0; i < D; i++) { 280 // If axis can be subdivided. 281 if (num_remaining_bits[i]) { 282 // Check if this is the better axis. 283 if (max_value < deviations[i]) { 284 max_value = deviations[i]; 285 best_axis = i; 286 } 287 } 288 } 289 axis_encoder_.EncodeLeastSignificantBits32(4, best_axis); 290 } 291 292 return best_axis; 293 } 294 295 template <class PointDiT, int compression_level_t> 296 template <class RandomAccessIteratorT> 297 void IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodeInternal( 298 RandomAccessIteratorT begin, RandomAccessIteratorT end, PointDiT old_base, 299 std::array<uint32_t, D> levels, uint32_t last_axis) { 300 EncodingStatus<RandomAccessIteratorT> init_status(begin, end, old_base, 301 levels, last_axis); 302 typename Policy::template QueuingStrategy< 303 EncodingStatus<RandomAccessIteratorT>> 304 status_q; 305 306 status_q.push(init_status); 307 308 while (!status_q.empty()) { 309 EncodingStatus<RandomAccessIteratorT> status = status_q.front(); 310 status_q.pop(); 311 312 begin = status.begin; 313 end = status.end; 314 old_base = status.old_base; 315 levels = status.levels; 316 last_axis = status.last_axis; 317 318 const uint32_t axis = GetAxis(begin, end, old_base, levels, last_axis); 319 const uint32_t level = levels[axis]; 320 const uint32_t num_remaining_points = end - begin; 321 322 // If this happens all axis are subdivided to the end. 323 if ((bit_length_ - level) == 0) { 324 continue; 325 } 326 327 // Fast encoding of remaining bits if number of points is 1. 328 // Doing this also for 2 gives a slight additional speed up. 329 if (num_remaining_points <= 2) { 330 std::array<uint32_t, D> axes; 331 axes[0] = axis; 332 for (int i = 1; i < D; i++) { 333 axes[i] = DRACO_INCREMENT_MOD(axes[i - 1], D); 334 } 335 336 std::array<uint32_t, D> num_remaining_bits; 337 for (int i = 0; i < D; i++) { 338 num_remaining_bits[i] = bit_length_ - levels[axes[i]]; 339 } 340 341 for (uint32_t i = 0; i < num_remaining_points; ++i) { 342 const PointDiT &p = *(begin + i); 343 for (int j = 0; j < D; j++) { 344 if (num_remaining_bits[j]) { 345 remaining_bits_encoder_.EncodeLeastSignificantBits32( 346 num_remaining_bits[j], p[axes[j]]); 347 } 348 } 349 } 350 continue; 351 } 352 353 const uint32_t num_remaining_bits = bit_length_ - level; 354 const uint32_t modifier = 1 << (num_remaining_bits - 1); 355 PointDiT new_base(old_base); 356 new_base[axis] += modifier; 357 const RandomAccessIteratorT split = 358 std::partition(begin, end, Splitter(axis, new_base[axis])); 359 360 DRACO_DCHECK_EQ(true, (end - begin) > 0); 361 362 // Encode number of points in first and second half. 363 const int required_bits = MostSignificantBit(num_remaining_points); 364 365 const uint32_t first_half = split - begin; 366 const uint32_t second_half = end - split; 367 const bool left = first_half < second_half; 368 369 if (first_half != second_half) { 370 half_encoder_.EncodeBit(left); 371 } 372 373 if (left) { 374 EncodeNumber(required_bits, num_remaining_points / 2 - first_half); 375 } else { 376 EncodeNumber(required_bits, num_remaining_points / 2 - second_half); 377 } 378 379 levels[axis] += 1; 380 if (split != begin) { 381 status_q.push(EncodingStatus<RandomAccessIteratorT>( 382 begin, split, old_base, levels, axis)); 383 } 384 if (split != end) { 385 status_q.push(EncodingStatus<RandomAccessIteratorT>(split, end, new_base, 386 levels, axis)); 387 } 388 } 389 } 390 391 extern template class IntegerPointsKdTreeEncoder<Point3ui, 0>; 392 extern template class IntegerPointsKdTreeEncoder<Point3ui, 1>; 393 extern template class IntegerPointsKdTreeEncoder<Point3ui, 2>; 394 extern template class IntegerPointsKdTreeEncoder<Point3ui, 3>; 395 extern template class IntegerPointsKdTreeEncoder<Point3ui, 4>; 396 extern template class IntegerPointsKdTreeEncoder<Point3ui, 5>; 397 extern template class IntegerPointsKdTreeEncoder<Point3ui, 6>; 398 extern template class IntegerPointsKdTreeEncoder<Point3ui, 7>; 399 extern template class IntegerPointsKdTreeEncoder<Point3ui, 8>; 400 extern template class IntegerPointsKdTreeEncoder<Point3ui, 9>; 401 extern template class IntegerPointsKdTreeEncoder<Point3ui, 10>; 402 403 } // namespace draco 404 405 #endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ 406