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