GameApi 28
Library used by GameApi Builder tool
Loading...
Searching...
No Matches
mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h
Go to the documentation of this file.
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#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
16#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
17
18#include <algorithm>
19#include <cmath>
20
27
28namespace draco {
29
30// Compared to standard multi-parallelogram, constrained multi-parallelogram can
31// explicitly select which of the available parallelograms are going to be used
32// for the prediction by marking crease edges between two triangles. This
33// requires storing extra data, but it allows the predictor to avoid using
34// parallelograms that would lead to poor predictions. For improved efficiency,
35// our current implementation limits the maximum number of used parallelograms
36// to four, which covers >95% of the cases (on average, there are only two
37// parallelograms available for any given vertex).
38// All bits of the explicitly chosen configuration are stored together in a
39// single context chosen by the total number of parallelograms available to
40// choose from.
41template <typename DataTypeT, class TransformT, class MeshDataT>
43 : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
44 public:
45 using CorrType =
47 using CornerTable = typename MeshDataT::CornerTable;
48
51 : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
52 attribute),
53 selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
55 const PointAttribute *attribute, const TransformT &transform,
56 const MeshDataT &mesh_data)
57 : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
59 selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
60
62 const DataTypeT *in_data, CorrType *out_corr, int size,
63 int num_components, const PointIndex *entry_to_point_id_map) override;
64
65 bool EncodePredictionData(EncoderBuffer *buffer) override;
66
70
71 bool IsInitialized() const override {
72 return this->mesh_data().IsInitialized();
73 }
74
75 private:
76 // Function used to compute number of bits needed to store overhead of the
77 // predictor. In this case, we consider overhead to be all bits that mark
78 // whether a parallelogram should be used for prediction or not. The input
79 // to this method is the total number of parallelograms that were evaluated so
80 // far(total_parallelogram), and the number of parallelograms we decided to
81 // use for prediction (total_used_parallelograms).
82 // Returns number of bits required to store the overhead.
83 int64_t ComputeOverheadBits(int64_t total_used_parallelograms,
84 int64_t total_parallelogram) const {
85 // For now we assume RAns coding for the bits where the total required size
86 // is directly correlated to the binary entropy of the input stream.
87 // TODO(ostava): This should be generalized in case we use other binary
88 // coding scheme.
89 const double entropy = ComputeBinaryShannonEntropy(
90 static_cast<uint32_t>(total_parallelogram),
91 static_cast<uint32_t>(total_used_parallelograms));
92
93 // Round up to the nearest full bit.
94 return static_cast<int64_t>(
95 ceil(static_cast<double>(total_parallelogram) * entropy));
96 }
97
98 // Struct that contains data used for measuring the error of each available
99 // parallelogram configuration.
100 struct Error {
101 Error() : num_bits(0), residual_error(0) {}
102
103 // Primary metric: number of bits required to store the data as a result of
104 // the selected prediction configuration.
105 int num_bits;
106 // Secondary metric: absolute difference of residuals for the given
107 // configuration.
108 int residual_error;
109
110 bool operator<(const Error &e) const {
111 if (num_bits < e.num_bits) {
112 return true;
113 }
114 if (num_bits > e.num_bits) {
115 return false;
116 }
117 return residual_error < e.residual_error;
118 }
119 };
120
121 // Computes error for predicting |predicted_val| instead of |actual_val|.
122 // Error is computed as the number of bits needed to encode the difference
123 // between the values.
124 Error ComputeError(const DataTypeT *predicted_val,
125 const DataTypeT *actual_val, int *out_residuals,
126 int num_components) {
127 Error error;
128
129 for (int i = 0; i < num_components; ++i) {
130 const int dif = (predicted_val[i] - actual_val[i]);
131 error.residual_error += std::abs(dif);
132 out_residuals[i] = dif;
133 // Entropy needs unsigned symbols, so convert the signed difference to an
134 // unsigned symbol.
135 entropy_symbols_[i] = ConvertSignedIntToSymbol(dif);
136 }
137
138 // Generate entropy data for case that this configuration was used.
139 // Note that the entropy stream is NOT updated in this case.
140 const auto entropy_data =
141 entropy_tracker_.Peek(entropy_symbols_.data(), num_components);
142
143 error.num_bits = entropy_tracker_.GetNumberOfDataBits(entropy_data) +
144 entropy_tracker_.GetNumberOfRAnsTableBits(entropy_data);
145 return error;
146 }
147
149 static constexpr int kMaxNumParallelograms =
150 constrained_multi_parallelogram::kMaxNumParallelograms;
151 // Crease edges are used to store whether any given edge should be used for
152 // parallelogram prediction or not. New values are added in the order in which
153 // the edges are processed. For better compression, the flags are stored in
154 // in separate contexts based on the number of available parallelograms at a
155 // given vertex.
156 // TODO(draco-eng) reconsider std::vector<bool> (performance/space).
157 std::vector<bool> is_crease_edge_[kMaxNumParallelograms];
158 Mode selected_mode_;
159
160 ShannonEntropyTracker entropy_tracker_;
161
162 // Temporary storage for symbols that are fed into the |entropy_stream|.
163 // Always contains only |num_components| entries.
164 std::vector<uint32_t> entropy_symbols_;
165};
166
167template <typename DataTypeT, class TransformT, class MeshDataT>
169 DataTypeT, TransformT, MeshDataT>::
170 ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
171 int size, int num_components,
172 const PointIndex * /* entry_to_point_id_map */) {
173 this->transform().Init(in_data, size, num_components);
174 const CornerTable *const table = this->mesh_data().corner_table();
175 const std::vector<int32_t> *const vertex_to_data_map =
176 this->mesh_data().vertex_to_data_map();
177
178 // Predicted values for all simple parallelograms encountered at any given
179 // vertex.
180 std::vector<DataTypeT> pred_vals[kMaxNumParallelograms];
181 for (int i = 0; i < kMaxNumParallelograms; ++i) {
182 pred_vals[i].resize(num_components);
183 }
184 // Used to store predicted value for various multi-parallelogram predictions
185 // (combinations of simple parallelogram predictions).
186 std::vector<DataTypeT> multi_pred_vals(num_components);
187 entropy_symbols_.resize(num_components);
188
189 // Struct for holding data about prediction configuration for different sets
190 // of used parallelograms.
191 struct PredictionConfiguration {
192 PredictionConfiguration()
193 : error(), configuration(0), num_used_parallelograms(0) {}
194 Error error;
195 uint8_t configuration; // Bitfield, 1 use parallelogram, 0 don't use it.
196 int num_used_parallelograms;
197 std::vector<DataTypeT> predicted_value;
198 std::vector<int32_t> residuals;
199 };
200
201 // Bit-field used for computing permutations of excluded edges
202 // (parallelograms).
203 bool exluded_parallelograms[kMaxNumParallelograms];
204
205 // Data about the number of used parallelogram and total number of available
206 // parallelogram for each context. Used to compute overhead needed for storing
207 // the parallelogram choices made by the encoder.
208 int64_t total_used_parallelograms[kMaxNumParallelograms] = {0};
209 int64_t total_parallelograms[kMaxNumParallelograms] = {0};
210
211 std::vector<int> current_residuals(num_components);
212
213 // We start processing the vertices from the end because this prediction uses
214 // data from previous entries that could be overwritten when an entry is
215 // processed.
216 for (int p =
217 static_cast<int>(this->mesh_data().data_to_corner_map()->size()) - 1;
218 p > 0; --p) {
219 const CornerIndex start_corner_id =
220 this->mesh_data().data_to_corner_map()->at(p);
221
222 // Go over all corners attached to the vertex and compute the predicted
223 // value from the parallelograms defined by their opposite faces.
224 CornerIndex corner_id(start_corner_id);
225 int num_parallelograms = 0;
226 bool first_pass = true;
227 while (corner_id != kInvalidCornerIndex) {
229 p, corner_id, table, *vertex_to_data_map, in_data, num_components,
230 &(pred_vals[num_parallelograms][0]))) {
231 // Parallelogram prediction applied and stored in
232 // |pred_vals[num_parallelograms]|
233 ++num_parallelograms;
234 // Stop processing when we reach the maximum number of allowed
235 // parallelograms.
236 if (num_parallelograms == kMaxNumParallelograms) {
237 break;
238 }
239 }
240
241 // Proceed to the next corner attached to the vertex. First swing left
242 // and if we reach a boundary, swing right from the start corner.
243 if (first_pass) {
244 corner_id = table->SwingLeft(corner_id);
245 } else {
246 corner_id = table->SwingRight(corner_id);
247 }
248 if (corner_id == start_corner_id) {
249 break;
250 }
251 if (corner_id == kInvalidCornerIndex && first_pass) {
252 first_pass = false;
253 corner_id = table->SwingRight(start_corner_id);
254 }
255 }
256
257 // Offset to the target (destination) vertex.
258 const int dst_offset = p * num_components;
259 Error error;
260
261 // Compute all prediction errors for all possible configurations of
262 // available parallelograms.
263
264 // Variable for holding the best configuration that has been found so far.
265 PredictionConfiguration best_prediction;
266
267 // Compute delta coding error (configuration when no parallelogram is
268 // selected).
269 const int src_offset = (p - 1) * num_components;
270 error = ComputeError(in_data + src_offset, in_data + dst_offset,
271 &current_residuals[0], num_components);
272
273 if (num_parallelograms > 0) {
274 total_parallelograms[num_parallelograms - 1] += num_parallelograms;
275 const int64_t new_overhead_bits =
276 ComputeOverheadBits(total_used_parallelograms[num_parallelograms - 1],
277 total_parallelograms[num_parallelograms - 1]);
278 error.num_bits += new_overhead_bits;
279 }
280
281 best_prediction.error = error;
282 best_prediction.configuration = 0;
283 best_prediction.num_used_parallelograms = 0;
284 best_prediction.predicted_value.assign(
285 in_data + src_offset, in_data + src_offset + num_components);
286 best_prediction.residuals.assign(current_residuals.begin(),
287 current_residuals.end());
288
289 // Compute prediction error for different cases of used parallelograms.
290 for (int num_used_parallelograms = 1;
291 num_used_parallelograms <= num_parallelograms;
292 ++num_used_parallelograms) {
293 // Mark all parallelograms as excluded.
294 std::fill(exluded_parallelograms,
295 exluded_parallelograms + num_parallelograms, true);
296 // TODO(draco-eng) maybe this should be another std::fill.
297 // Mark the first |num_used_parallelograms| as not excluded.
298 for (int j = 0; j < num_used_parallelograms; ++j) {
299 exluded_parallelograms[j] = false;
300 }
301 // Permute over the excluded edges and compute error for each
302 // configuration (permutation of excluded parallelograms).
303 do {
304 // Reset the multi-parallelogram predicted values.
305 for (int j = 0; j < num_components; ++j) {
306 multi_pred_vals[j] = 0;
307 }
308 uint8_t configuration = 0;
309 for (int j = 0; j < num_parallelograms; ++j) {
310 if (exluded_parallelograms[j]) {
311 continue;
312 }
313 for (int c = 0; c < num_components; ++c) {
314 multi_pred_vals[c] += pred_vals[j][c];
315 }
316 // Set jth bit of the configuration.
317 configuration |= (1 << j);
318 }
319
320 for (int j = 0; j < num_components; ++j) {
321 multi_pred_vals[j] /= num_used_parallelograms;
322 }
323 error = ComputeError(multi_pred_vals.data(), in_data + dst_offset,
324 &current_residuals[0], num_components);
325 if (num_parallelograms > 0) {
326 const int64_t new_overhead_bits = ComputeOverheadBits(
327 total_used_parallelograms[num_parallelograms - 1] +
328 num_used_parallelograms,
329 total_parallelograms[num_parallelograms - 1]);
330
331 // Add overhead bits to the total error.
332 error.num_bits += new_overhead_bits;
333 }
334 if (error < best_prediction.error) {
335 best_prediction.error = error;
336 best_prediction.configuration = configuration;
337 best_prediction.num_used_parallelograms = num_used_parallelograms;
338 best_prediction.predicted_value.assign(multi_pred_vals.begin(),
339 multi_pred_vals.end());
340 best_prediction.residuals.assign(current_residuals.begin(),
341 current_residuals.end());
342 }
343 } while (std::next_permutation(
344 exluded_parallelograms, exluded_parallelograms + num_parallelograms));
345 }
346 if (num_parallelograms > 0) {
347 total_used_parallelograms[num_parallelograms - 1] +=
348 best_prediction.num_used_parallelograms;
349 }
350
351 // Update the entropy stream by adding selected residuals as symbols to the
352 // stream.
353 for (int i = 0; i < num_components; ++i) {
354 entropy_symbols_[i] =
355 ConvertSignedIntToSymbol(best_prediction.residuals[i]);
356 }
357 entropy_tracker_.Push(entropy_symbols_.data(), num_components);
358
359 for (int i = 0; i < num_parallelograms; ++i) {
360 if ((best_prediction.configuration & (1 << i)) == 0) {
361 // Parallelogram not used, mark the edge as crease.
362 is_crease_edge_[num_parallelograms - 1].push_back(true);
363 } else {
364 // Parallelogram used. Add it to the predicted value and mark the
365 // edge as not a crease.
366 is_crease_edge_[num_parallelograms - 1].push_back(false);
367 }
368 }
369 this->transform().ComputeCorrection(in_data + dst_offset,
370 best_prediction.predicted_value.data(),
371 out_corr + dst_offset);
372 }
373 // First element is always fixed because it cannot be predicted.
374 for (int i = 0; i < num_components; ++i) {
375 pred_vals[0][i] = static_cast<DataTypeT>(0);
376 }
377 this->transform().ComputeCorrection(in_data, pred_vals[0].data(), out_corr);
378 return true;
379}
380
381template <typename DataTypeT, class TransformT, class MeshDataT>
383 DataTypeT, TransformT, MeshDataT>::EncodePredictionData(EncoderBuffer
384 *buffer) {
385 // Encode selected edges using separate rans bit coder for each context.
386 for (int i = 0; i < kMaxNumParallelograms; ++i) {
387 // |i| is the context based on the number of available parallelograms, which
388 // is always equal to |i + 1|.
389 const int num_used_parallelograms = i + 1;
390 EncodeVarint<uint32_t>(is_crease_edge_[i].size(), buffer);
391 if (is_crease_edge_[i].size()) {
392 RAnsBitEncoder encoder;
393 encoder.StartEncoding();
394 // Encode the crease edge flags in the reverse vertex order that is needed
395 // by the decoder. Note that for the currently supported mode, each vertex
396 // has exactly |num_used_parallelograms| edges that need to be encoded.
397 for (int j = static_cast<int>(is_crease_edge_[i].size()) -
398 num_used_parallelograms;
399 j >= 0; j -= num_used_parallelograms) {
400 // Go over all edges of the current vertex.
401 for (int k = 0; k < num_used_parallelograms; ++k) {
402 encoder.EncodeBit(is_crease_edge_[i][j + k]);
403 }
404 }
405 encoder.EndEncoding(buffer);
406 }
407 }
408 return MeshPredictionSchemeEncoder<DataTypeT, TransformT,
409 MeshDataT>::EncodePredictionData(buffer);
410}
411
412} // namespace draco
413
414#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
Env e
Definition Common.cc:2
void error(std::string err, std::string fail_reason)
Definition check.cc:18
Definition encoder_buffer.h:29
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:43
bool ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr, int size, int num_components, const PointIndex *entry_to_point_id_map) override
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:170
MeshPredictionSchemeConstrainedMultiParallelogramEncoder(const PointAttribute *attribute, const TransformT &transform, const MeshDataT &mesh_data)
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:54
bool IsInitialized() const override
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:71
MeshPredictionSchemeConstrainedMultiParallelogramEncoder(const PointAttribute *attribute)
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:49
typename MeshDataT::CornerTable CornerTable
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:47
bool EncodePredictionData(EncoderBuffer *buffer) override
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:383
typename PredictionSchemeEncoder< DataTypeT, TransformT >::CorrType CorrType
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:45
PredictionSchemeMethod GetPredictionMethod() const override
Definition mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h:67
MeshPredictionSchemeEncoder(const PointAttribute *attribute, const TransformT &transform, const MeshDataT &mesh_data)
Definition mesh_prediction_scheme_encoder.h:31
const MeshData & mesh_data() const
Definition mesh_prediction_scheme_encoder.h:38
Definition point_attribute.h:33
const PointAttribute * attribute() const
Definition prediction_scheme_encoder.h:79
Transform::CorrType CorrType
Definition prediction_scheme_encoder.h:41
const Transform & transform() const
Definition prediction_scheme_encoder.h:80
Definition rans_bit_encoder.h:28
void EndEncoding(EncoderBuffer *target_buffer)
Definition rans_bit_encoder.cc:75
void EncodeBit(bool bit)
Definition rans_bit_encoder.cc:31
void StartEncoding()
Definition rans_bit_encoder.cc:29
lazy data
Definition meshpage.php:1583
n< br > font size
Definition meshpage.php:3536
Mode
Definition mesh_prediction_scheme_constrained_multi_parallelogram_shared.h:23
Definition attribute_octahedron_transform.cc:21
std::make_unsigned< IntTypeT >::type ConvertSignedIntToSymbol(IntTypeT val)
Definition bit_utils.h:90
bool ComputeParallelogramPrediction(int data_entry_id, const CornerIndex ci, const CornerTableT *table, const std::vector< int32_t > &vertex_to_data_map, const DataTypeT *in_data, int num_components, DataTypeT *out_prediction)
Definition mesh_prediction_scheme_parallelogram_shared.h:45
double ComputeBinaryShannonEntropy(uint32_t num_values, uint32_t num_true_values)
Definition shannon_entropy.cc:39
CornerIndex CornerIndex
Definition mesh_edgebreaker_encoder_impl.cc:35
bool EncodeVarint(IntTypeT val, EncoderBuffer *out_buffer)
Definition varint_encoding.h:28
PredictionSchemeMethod
Definition compression_shared.h:80
@ MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM
Definition compression_shared.h:89
Definition GameApi_pl.cc:29382