Differential D4501 Diff 14677 extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h
Changeset View
Changeset View
Standalone View
Standalone View
extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h
- This file was added.
| // Copyright 2016 The Draco Authors. | |||||
| // | |||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| // you may not use this file except in compliance with the License. | |||||
| // You may obtain a copy of the License at | |||||
| // | |||||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||||
| // | |||||
| // Unless required by applicable law or agreed to in writing, software | |||||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| // See the License for the specific language governing permissions and | |||||
| // limitations under the License. | |||||
| // | |||||
| // TODO(hemmer): Make this a wrapper using DynamicIntegerPointsKdTreeEncoder. | |||||
| #ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ | |||||
| #define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ | |||||
| #include <algorithm> | |||||
| #include <array> | |||||
| #include <memory> | |||||
| #include <vector> | |||||
| #include "draco/compression/bit_coders/adaptive_rans_bit_encoder.h" | |||||
| #include "draco/compression/bit_coders/direct_bit_encoder.h" | |||||
| #include "draco/compression/bit_coders/folded_integer_bit_encoder.h" | |||||
| #include "draco/compression/bit_coders/rans_bit_encoder.h" | |||||
| #include "draco/compression/point_cloud/algorithms/point_cloud_types.h" | |||||
| #include "draco/compression/point_cloud/algorithms/queuing_policy.h" | |||||
| #include "draco/core/bit_utils.h" | |||||
| #include "draco/core/encoder_buffer.h" | |||||
| #include "draco/core/math_utils.h" | |||||
| namespace draco { | |||||
| // This policy class provides several configurations for the encoder that allow | |||||
| // to trade speed vs compression rate. Level 0 is fastest while 10 is the best | |||||
| // compression rate. The decoder must select the same level. | |||||
| template <int compression_level_t> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t - | |||||
| 1> {}; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<0> { | |||||
| typedef DirectBitEncoder NumbersEncoder; | |||||
| typedef DirectBitEncoder AxisEncoder; | |||||
| typedef DirectBitEncoder HalfEncoder; | |||||
| typedef DirectBitEncoder RemainingBitsEncoder; | |||||
| static constexpr bool select_axis = false; | |||||
| template <class T> | |||||
| using QueuingStrategy = Stack<T>; | |||||
| }; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<2> | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<1> { | |||||
| typedef RAnsBitEncoder NumbersEncoder; | |||||
| }; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<4> | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<3> { | |||||
| typedef FoldedBit32Encoder<RAnsBitEncoder> NumbersEncoder; | |||||
| }; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<6> | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<5> { | |||||
| static constexpr bool select_axis = true; | |||||
| }; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<8> | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<7> { | |||||
| typedef FoldedBit32Encoder<AdaptiveRAnsBitEncoder> NumbersEncoder; | |||||
| template <class T> | |||||
| using QueuingStrategy = Queue<T>; | |||||
| }; | |||||
| template <> | |||||
| struct IntegerPointsKdTreeEncoderCompressionPolicy<10> | |||||
| : public IntegerPointsKdTreeEncoderCompressionPolicy<9> { | |||||
| template <class T> | |||||
| using QueuingStrategy = PriorityQueue<T>; | |||||
| }; | |||||
| // This class encodes a given integer point cloud based on the point cloud | |||||
| // compression algorithm in: | |||||
| // Olivier Devillers and Pierre-Marie Gandoin | |||||
| // "Geometric compression for interactive transmission" | |||||
| // | |||||
| // In principle the algorithm keeps on splitting the point cloud in the middle | |||||
| // while alternating the axes. In 3D this results in an Octree like structure. | |||||
| // In each step we encode the number of points in the first half. | |||||
| // The algorithm does not preserve the order of points. | |||||
| // | |||||
| // However, the algorithm here differs from the original as follows: | |||||
| // The algorithm keeps on splitting the point cloud in the middle of the axis | |||||
| // that keeps the point cloud as clustered as possible, which gives a better | |||||
| // compression rate. | |||||
| // The number of points is encode by the deviation from the half of the points | |||||
| // in the smaller half of the two. This results in a better compression rate as | |||||
| // there are more leading zeros, which is then compressed better by the | |||||
| // arithmetic encoding. | |||||
| // | |||||
| // |PointDiT| is a type representing a point with uint32_t coordinates. | |||||
| // must provide construction from three uint32_t and operator[]. | |||||
| template <class PointDiT, int compression_level_t> | |||||
| class IntegerPointsKdTreeEncoder { | |||||
| typedef IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t> | |||||
| Policy; | |||||
| typedef typename Policy::NumbersEncoder NumbersEncoder; | |||||
| typedef typename Policy::AxisEncoder AxisEncoder; | |||||
| typedef typename Policy::HalfEncoder HalfEncoder; | |||||
| typedef typename Policy::RemainingBitsEncoder RemainingBitsEncoder; | |||||
| public: | |||||
| IntegerPointsKdTreeEncoder() : bit_length_(0) {} | |||||
| // Encodes an integer point cloud given by [begin,end) into buffer. | |||||
| // |bit_length| gives the highest bit used for all coordinates. | |||||
| template <class RandomAccessIteratorT> | |||||
| bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| const uint32_t &bit_length, EncoderBuffer *buffer); | |||||
| // Encodes an integer point cloud given by [begin,end) into buffer. | |||||
| template <class RandomAccessIteratorT> | |||||
| bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| EncoderBuffer *buffer) { | |||||
| return EncodePoints(begin, end, 32, buffer); | |||||
| } | |||||
| private: | |||||
| // For the sack of readability of code, we decided to make this exception | |||||
| // from the naming scheme. | |||||
| static constexpr int D = PointTraits<PointDiT>::Dimension(); | |||||
| template <class RandomAccessIteratorT> | |||||
| uint32_t GetAxis(RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| const PointDiT &old_base, std::array<uint32_t, D> levels, | |||||
| uint32_t last_axis); | |||||
| template <class RandomAccessIteratorT> | |||||
| void EncodeInternal(RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| PointDiT old_base, std::array<uint32_t, D> levels, | |||||
| uint32_t last_axis); | |||||
| class Splitter { | |||||
| public: | |||||
| Splitter(int axis, uint32_t value) : axis_(axis), value_(value) {} | |||||
| bool operator()(const PointDiT &a) { return a[axis_] < value_; } | |||||
| private: | |||||
| int axis_; | |||||
| uint32_t value_; | |||||
| }; | |||||
| void EncodeNumber(int nbits, uint32_t value) { | |||||
| numbers_encoder_.EncodeLeastSignificantBits32(nbits, value); | |||||
| } | |||||
| template <class RandomAccessIteratorT> | |||||
| struct EncodingStatus { | |||||
| EncodingStatus( | |||||
| RandomAccessIteratorT begin_, RandomAccessIteratorT end_, | |||||
| const PointDiT &old_base_, | |||||
| std::array<uint32_t, PointTraits<PointDiT>::Dimension()> levels_, | |||||
| uint32_t last_axis_) | |||||
| : begin(begin_), | |||||
| end(end_), | |||||
| old_base(old_base_), | |||||
| levels(levels_), | |||||
| last_axis(last_axis_) { | |||||
| num_remaining_points = end - begin; | |||||
| } | |||||
| RandomAccessIteratorT begin; | |||||
| RandomAccessIteratorT end; | |||||
| PointDiT old_base; | |||||
| std::array<uint32_t, D> levels; | |||||
| uint32_t last_axis; | |||||
| uint32_t num_remaining_points; | |||||
| friend bool operator<(const EncodingStatus &l, const EncodingStatus &r) { | |||||
| return l.num_remaining_points < r.num_remaining_points; | |||||
| } | |||||
| }; | |||||
| uint32_t bit_length_; | |||||
| uint32_t num_points_; | |||||
| NumbersEncoder numbers_encoder_; | |||||
| RemainingBitsEncoder remaining_bits_encoder_; | |||||
| AxisEncoder axis_encoder_; | |||||
| HalfEncoder half_encoder_; | |||||
| }; | |||||
| template <class PointDiT, int compression_level_t> | |||||
| template <class RandomAccessIteratorT> | |||||
| bool IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodePoints( | |||||
| RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| const uint32_t &bit_length, EncoderBuffer *buffer) { | |||||
| bit_length_ = bit_length; | |||||
| num_points_ = end - begin; | |||||
| buffer->Encode(bit_length_); | |||||
| buffer->Encode(num_points_); | |||||
| if (num_points_ == 0) | |||||
| return true; | |||||
| numbers_encoder_.StartEncoding(); | |||||
| remaining_bits_encoder_.StartEncoding(); | |||||
| axis_encoder_.StartEncoding(); | |||||
| half_encoder_.StartEncoding(); | |||||
| EncodeInternal(begin, end, PointTraits<PointDiT>::Origin(), | |||||
| PointTraits<PointDiT>::ZeroArray(), 0); | |||||
| numbers_encoder_.EndEncoding(buffer); | |||||
| remaining_bits_encoder_.EndEncoding(buffer); | |||||
| axis_encoder_.EndEncoding(buffer); | |||||
| half_encoder_.EndEncoding(buffer); | |||||
| return true; | |||||
| } | |||||
| template <class PointDiT, int compression_level_t> | |||||
| template <class RandomAccessIteratorT> | |||||
| uint32_t IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::GetAxis( | |||||
| RandomAccessIteratorT begin, RandomAccessIteratorT end, | |||||
| const PointDiT &old_base, std::array<uint32_t, D> levels, | |||||
| uint32_t last_axis) { | |||||
| if (!Policy::select_axis) | |||||
| return DRACO_INCREMENT_MOD(last_axis, D); | |||||
| // For many points this function selects the axis that should be used | |||||
| // for the split by keeping as many points as possible bundled. | |||||
| // In the best case we do not split the point cloud at all. | |||||
| // For lower number of points, we simply choose the axis that is refined the | |||||
| // least so far. | |||||
| DRACO_DCHECK_EQ(true, end - begin != 0); | |||||
| uint32_t best_axis = 0; | |||||
| if (end - begin < 64) { | |||||
| for (uint32_t axis = 1; axis < D; ++axis) { | |||||
| if (levels[best_axis] > levels[axis]) { | |||||
| best_axis = axis; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| const uint32_t size = (end - begin); | |||||
| std::array<uint32_t, D> num_remaining_bits = | |||||
| PointTraits<PointDiT>::ZeroArray(); | |||||
| for (int i = 0; i < D; i++) { | |||||
| num_remaining_bits[i] = bit_length_ - levels[i]; | |||||
| } | |||||
| PointDiT split(old_base); | |||||
| for (int i = 0; i < D; i++) { | |||||
| if (num_remaining_bits[i]) | |||||
| split[i] += 1 << (num_remaining_bits[i] - 1); | |||||
| } | |||||
| std::array<uint32_t, D> deviations = PointTraits<PointDiT>::ZeroArray(); | |||||
| for (auto it = begin; it != end; ++it) { | |||||
| for (int i = 0; i < D; i++) { | |||||
| deviations[i] += ((*it)[i] < split[i]); | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < D; i++) { | |||||
| deviations[i] = std::max(size - deviations[i], deviations[i]); | |||||
| } | |||||
| uint32_t max_value = 0; | |||||
| best_axis = 0; | |||||
| for (int i = 0; i < D; i++) { | |||||
| // If axis can be subdivided. | |||||
| if (num_remaining_bits[i]) { | |||||
| // Check if this is the better axis. | |||||
| if (max_value < deviations[i]) { | |||||
| max_value = deviations[i]; | |||||
| best_axis = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| axis_encoder_.EncodeLeastSignificantBits32(4, best_axis); | |||||
| } | |||||
| return best_axis; | |||||
| } | |||||
| template <class PointDiT, int compression_level_t> | |||||
| template <class RandomAccessIteratorT> | |||||
| void IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodeInternal( | |||||
| RandomAccessIteratorT begin, RandomAccessIteratorT end, PointDiT old_base, | |||||
| std::array<uint32_t, D> levels, uint32_t last_axis) { | |||||
| EncodingStatus<RandomAccessIteratorT> init_status(begin, end, old_base, | |||||
| levels, last_axis); | |||||
| typename Policy::template QueuingStrategy< | |||||
| EncodingStatus<RandomAccessIteratorT>> | |||||
| status_q; | |||||
| status_q.push(init_status); | |||||
| while (!status_q.empty()) { | |||||
| EncodingStatus<RandomAccessIteratorT> status = status_q.front(); | |||||
| status_q.pop(); | |||||
| begin = status.begin; | |||||
| end = status.end; | |||||
| old_base = status.old_base; | |||||
| levels = status.levels; | |||||
| last_axis = status.last_axis; | |||||
| const uint32_t axis = GetAxis(begin, end, old_base, levels, last_axis); | |||||
| const uint32_t level = levels[axis]; | |||||
| const uint32_t num_remaining_points = end - begin; | |||||
| // If this happens all axis are subdivided to the end. | |||||
| if ((bit_length_ - level) == 0) | |||||
| continue; | |||||
| // Fast encoding of remaining bits if number of points is 1. | |||||
| // Doing this also for 2 gives a slight additional speed up. | |||||
| if (num_remaining_points <= 2) { | |||||
| std::array<uint32_t, D> axes; | |||||
| axes[0] = axis; | |||||
| for (int i = 1; i < D; i++) { | |||||
| axes[i] = DRACO_INCREMENT_MOD(axes[i - 1], D); | |||||
| } | |||||
| std::array<uint32_t, D> num_remaining_bits; | |||||
| for (int i = 0; i < D; i++) { | |||||
| num_remaining_bits[i] = bit_length_ - levels[axes[i]]; | |||||
| } | |||||
| for (uint32_t i = 0; i < num_remaining_points; ++i) { | |||||
| const PointDiT &p = *(begin + i); | |||||
| for (int j = 0; j < D; j++) { | |||||
| if (num_remaining_bits[j]) { | |||||
| remaining_bits_encoder_.EncodeLeastSignificantBits32( | |||||
| num_remaining_bits[j], p[axes[j]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| continue; | |||||
| } | |||||
| const uint32_t num_remaining_bits = bit_length_ - level; | |||||
| const uint32_t modifier = 1 << (num_remaining_bits - 1); | |||||
| PointDiT new_base(old_base); | |||||
| new_base[axis] += modifier; | |||||
| const RandomAccessIteratorT split = | |||||
| std::partition(begin, end, Splitter(axis, new_base[axis])); | |||||
| DRACO_DCHECK_EQ(true, (end - begin) > 0); | |||||
| // Encode number of points in first and second half. | |||||
| const int required_bits = MostSignificantBit(num_remaining_points); | |||||
| const uint32_t first_half = split - begin; | |||||
| const uint32_t second_half = end - split; | |||||
| const bool left = first_half < second_half; | |||||
| if (first_half != second_half) | |||||
| half_encoder_.EncodeBit(left); | |||||
| if (left) { | |||||
| EncodeNumber(required_bits, num_remaining_points / 2 - first_half); | |||||
| } else { | |||||
| EncodeNumber(required_bits, num_remaining_points / 2 - second_half); | |||||
| } | |||||
| levels[axis] += 1; | |||||
| if (split != begin) | |||||
| status_q.push(EncodingStatus<RandomAccessIteratorT>( | |||||
| begin, split, old_base, levels, axis)); | |||||
| if (split != end) | |||||
| status_q.push(EncodingStatus<RandomAccessIteratorT>(split, end, new_base, | |||||
| levels, axis)); | |||||
| } | |||||
| } | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 0>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 1>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 2>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 3>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 4>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 5>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 6>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 7>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 8>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 9>; | |||||
| extern template class IntegerPointsKdTreeEncoder<Point3ui, 10>; | |||||
| } // namespace draco | |||||
| #endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_ | |||||