AttributeEncoder.cpp 34.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* The copyright in this software is being made available under the BSD
 * Licence, included below.  This software may be subject to other third
 * party and contributor rights, including patent rights, and no such
 * rights are granted under this licence.
 *
 * Copyright (c) 2017-2018, ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of the ISO/IEC nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "AttributeEncoder.h"
37

David Flynn's avatar
David Flynn committed
38
#include "ArithmeticCodec.h"
39
#include "DualLutCoder.h"
40
#include "constants.h"
41
#include "entropy.h"
42
#include "quantization.h"
43
#include "RAHT.h"
44
#include "FixedPoint.h"
45

46
// todo(df): promote to per-attribute encoder parameter
47
48
static const double kAttrPredLambdaR = 0.01;
static const double kAttrPredLambdaC = 0.01;
49

50
51
52
53
54
namespace pcc {
//============================================================================
// An encapsulation of the entropy coding methods used in attribute coding

struct PCCResidualsEncoder {
55
56
57
58
  EntropyEncoder arithmeticEncoder;
  StaticBitModel binaryModel0;
  AdaptiveBitModel binaryModelDiff[7];
  AdaptiveBitModel binaryModelIsZero[7];
59
  AdaptiveBitModel ctxPredMode[2];
60
  DualLutCoder<false> symbolCoder[2];
61

62
  void start(int numPoints);
63
  int stop();
64
  void encodePredMode(int value, int max);
65
66
67
  void encodeSymbol(uint32_t value, int k1, int k2);
  void encode(uint32_t value0, uint32_t value1, uint32_t value2);
  void encode(uint32_t value);
68
69
70
71
};

//----------------------------------------------------------------------------

72
void
73
PCCResidualsEncoder::start(int pointCount)
74
{
75
76
  // todo(df): remove estimate when arithmetic codec is replaced
  int maxAcBufLen = pointCount * 3 * 2 + 1024;
77
78
  arithmeticEncoder.setBuffer(maxAcBufLen, nullptr);
  arithmeticEncoder.start();
79
80
81
82
}

//----------------------------------------------------------------------------

83
int
84
85
PCCResidualsEncoder::stop()
{
86
  return arithmeticEncoder.stop();
87
88
89
90
}

//----------------------------------------------------------------------------

91
void
92
PCCResidualsEncoder::encodePredMode(int mode, int maxMode)
93
{
94
95
96
97
98
99
100
101
102
103
104
105
106
  // max = 0 => no direct predictors are used
  if (maxMode == 0)
    return;

  int ctxIdx = 0;
  for (int i = 0; i < mode; i++) {
    arithmeticEncoder.encode(1, ctxPredMode[ctxIdx]);
    ctxIdx = 1;
  }

  // Truncated unary
  if (mode != maxMode)
    arithmeticEncoder.encode(0, ctxPredMode[ctxIdx]);
107
108
109
110
}

//----------------------------------------------------------------------------

111
112
void
PCCResidualsEncoder::encodeSymbol(uint32_t value, int k1, int k2)
113
{
114
115
116
117
118
119
  bool isZero = value == 0;
  arithmeticEncoder.encode(isZero, binaryModelIsZero[k1]);
  if (isZero) {
    return;
  }
  --value;
120
  if (value < kAttributeResidualAlphabetSize) {
121
    symbolCoder[k2].encode(value, &arithmeticEncoder);
122
  } else {
123
    int alphabetSize = kAttributeResidualAlphabetSize;
124
    symbolCoder[k2].encode(alphabetSize, &arithmeticEncoder);
125
    arithmeticEncoder.encodeExpGolomb(
126
      value - alphabetSize, 0, binaryModel0, binaryModelDiff[k1]);
127
128
129
  }
}

130
131
132
//----------------------------------------------------------------------------

void
133
PCCResidualsEncoder::encode(uint32_t value0, uint32_t value1, uint32_t value2)
134
{
135
136
137
138
139
140
141
142
143
144
145
146
147
  int b0 = value0 == 0;
  int b1 = value1 == 0;
  encodeSymbol(value0, 0, 0);
  encodeSymbol(value1, 1 + b0, 1);
  encodeSymbol(value2, 3 + (b0 << 1) + b1, 1);
}

//----------------------------------------------------------------------------

void
PCCResidualsEncoder::encode(uint32_t value)
{
  encodeSymbol(value, 0, 0);
148
149
}

150
//============================================================================
151
152
153
// An encapsulation of the entropy coding methods used in attribute coding

struct PCCResidualsEntropyEstimator {
154
155
  size_t freq0[kAttributeResidualAlphabetSize + 1];
  size_t freq1[kAttributeResidualAlphabetSize + 1];
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  size_t symbolCount0;
  size_t symbolCount1;
  size_t isZero0Count;
  size_t isZero1Count;
  PCCResidualsEntropyEstimator() { init(); }
  void init();
  double bitsDetail(
    const uint32_t detail,
    const size_t symbolCount,
    const size_t* const freq) const;
  double bits(const uint32_t value0) const;
  void update(const uint32_t value0);
  double bits(
    const uint32_t value0, const uint32_t value1, const uint32_t value2) const;
  void
  update(const uint32_t value0, const uint32_t value1, const uint32_t value2);
};

//----------------------------------------------------------------------------
175

176
void
177
PCCResidualsEntropyEstimator::init()
178
{
179
  for (size_t i = 0; i <= kAttributeResidualAlphabetSize; ++i) {
180
181
182
    freq0[i] = 1;
    freq1[i] = 1;
  }
183
184
  symbolCount0 = kAttributeResidualAlphabetSize + 1;
  symbolCount1 = kAttributeResidualAlphabetSize + 1;
185
186
  isZero1Count = isZero0Count = symbolCount0 / 2;
}
187

188
189
190
191
192
193
194
195
196
//----------------------------------------------------------------------------

double
PCCResidualsEntropyEstimator::bitsDetail(
  const uint32_t detail,
  const size_t symbolCount,
  const size_t* const freq) const
{
  const uint32_t detailClipped =
197
    std::min(detail, uint32_t(kAttributeResidualAlphabetSize));
198
199
200
  const double pDetail =
    PCCClip(double(freq[detailClipped]) / symbolCount, 0.001, 0.999);
  double bits = -log2(pDetail);
201
202
  if (detail >= kAttributeResidualAlphabetSize) {
    const double x = double(detail) - double(kAttributeResidualAlphabetSize);
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    bits += 2.0 * std::floor(log2(x + 1.0)) + 1.0;
  }
  return bits;
}

//----------------------------------------------------------------------------

double
PCCResidualsEntropyEstimator::bits(const uint32_t value0) const
{
  const bool isZero0 = value0 == 0;
  const double pIsZero0 = isZero0
    ? double(isZero0Count) / symbolCount0
    : double(symbolCount0 - isZero0Count) / symbolCount0;
  double bits = -log2(PCCClip(pIsZero0, 0.001, 0.999));
  if (!isZero0) {
    bits += bitsDetail(value0 - 1, symbolCount0, freq0);
220
  }
221
  return bits;
222
223
224
225
}

//----------------------------------------------------------------------------

226
227
228
229
230
231
void
PCCResidualsEntropyEstimator::update(const uint32_t value0)
{
  const bool isZero0 = value0 == 0;
  ++symbolCount0;
  if (!isZero0) {
232
    ++freq0[std::min(value0 - 1, uint32_t(kAttributeResidualAlphabetSize))];
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  } else {
    ++isZero0Count;
  }
}

//----------------------------------------------------------------------------

double
PCCResidualsEntropyEstimator::bits(
  const uint32_t value0, const uint32_t value1, const uint32_t value2) const
{
  const bool isZero0 = value0 == 0;
  const double pIsZero0 = isZero0
    ? double(isZero0Count) / symbolCount0
    : double(symbolCount0 - isZero0Count) / symbolCount0;
  double bits = -log2(PCCClip(pIsZero0, 0.001, 0.999));
  if (!isZero0) {
    bits += bitsDetail(value0 - 1, symbolCount0, freq0);
  }

  const bool isZero1 = value1 == 0 && value2 == 0;
  const double pIsZero1 = isZero1
    ? double(isZero1Count) / symbolCount0
    : double(symbolCount0 - isZero1Count) / symbolCount0;
  bits -= log2(PCCClip(pIsZero1, 0.001, 0.999));
  if (!isZero1) {
    bits += bitsDetail(value1, symbolCount1, freq1);
    bits += bitsDetail(value2, symbolCount1, freq1);
  }
  return bits;
}

//----------------------------------------------------------------------------

void
PCCResidualsEntropyEstimator::update(
  const uint32_t value0, const uint32_t value1, const uint32_t value2)
{
  const bool isZero0 = value0 == 0;
  ++symbolCount0;
  if (!isZero0) {
274
    ++freq0[std::min(value0 - 1, uint32_t(kAttributeResidualAlphabetSize))];
275
276
277
278
279
280
281
  } else {
    ++isZero0Count;
  }

  const bool isZero1 = value1 == 0 && value2 == 0;
  symbolCount1 += 2;
  if (!isZero1) {
282
283
    ++freq1[std::min(value1, uint32_t(kAttributeResidualAlphabetSize))];
    ++freq1[std::min(value2, uint32_t(kAttributeResidualAlphabetSize))];
284
285
286
287
288
289
290
291
  } else {
    ++isZero1Count;
  }
}

//============================================================================
// AttributeEncoder Members

292
void
293
AttributeEncoder::encode(
294
  const AttributeDescription& desc,
295
  const AttributeParameterSet& attr_aps,
296
  const AttributeBrickHeader& abh,
297
  PCCPointSet3& pointCloud,
298
  PayloadBuffer* payload)
299
{
300
  Quantizers qstep = deriveQuantSteps(attr_aps, abh);
301

302
  PCCResidualsEncoder encoder;
303
  encoder.start(int(pointCloud.getPointCount()));
304

305
  if (desc.attr_num_dimensions == 1) {
306
307
    switch (attr_aps.attr_encoding) {
    case AttributeEncoding::kRAHTransform:
308
309
      encodeReflectancesTransformRaht(
        desc, attr_aps, qstep, pointCloud, encoder);
310
      break;
311

312
    case AttributeEncoding::kPredictingTransform:
313
      encodeReflectancesPred(desc, attr_aps, qstep, pointCloud, encoder);
314
      break;
315

316
    case AttributeEncoding::kLiftingTransform:
317
      encodeReflectancesLift(desc, attr_aps, qstep, pointCloud, encoder);
318
319
      break;
    }
320
  } else if (desc.attr_num_dimensions == 3) {
321
322
    switch (attr_aps.attr_encoding) {
    case AttributeEncoding::kRAHTransform:
323
      encodeColorsTransformRaht(desc, attr_aps, qstep, pointCloud, encoder);
324
325
326
      break;

    case AttributeEncoding::kPredictingTransform:
327
      encodeColorsPred(desc, attr_aps, qstep, pointCloud, encoder);
328
329
330
      break;

    case AttributeEncoding::kLiftingTransform:
331
      encodeColorsLift(desc, attr_aps, qstep, pointCloud, encoder);
332
333
334
      break;
    }
  } else {
335
    assert(desc.attr_num_dimensions == 1 || desc.attr_num_dimensions == 3);
336
  }
337

338
339
340
341
  uint32_t acDataLen = encoder.stop();
  std::copy_n(
    encoder.arithmeticEncoder.buffer(), acDataLen,
    std::back_inserter(*payload));
342
343
344
345
}

//----------------------------------------------------------------------------

346
347
348
349
350
351
352
353
int64_t
AttributeEncoder::computeReflectanceResidual(
  const uint64_t reflectance,
  const uint64_t predictedReflectance,
  const int64_t qs)
{
  const int64_t quantAttValue = reflectance;
  const int64_t quantPredAttValue = predictedReflectance;
354
355
356
  const int64_t delta =
    PCCQuantization(quantAttValue - quantPredAttValue, qs, true);

357
  return IntToUInt(delta);
358
359
360
361
362
363
}

//----------------------------------------------------------------------------

void
AttributeEncoder::computeReflectancePredictionWeights(
364
  const AttributeParameterSet& aps,
365
  const PCCPointSet3& pointCloud,
366
367
  const std::vector<uint32_t>& indexesLOD,
  const uint32_t predictorIndex,
368
369
  PCCPredictor& predictor,
  PCCResidualsEncoder& encoder,
370
371
  PCCResidualsEntropyEstimator& context,
  const int64_t qs)
372
373
374
375
376
{
  predictor.computeWeights();
  if (predictor.neighborCount > 1) {
    int64_t minValue = 0;
    int64_t maxValue = 0;
377
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
378
      const uint64_t reflectanceNeighbor = pointCloud.getReflectance(
379
        indexesLOD[predictor.neighbors[i].predictorIndex]);
380
381
382
383
384
385
386
387
      if (i == 0 || reflectanceNeighbor < minValue) {
        minValue = reflectanceNeighbor;
      }
      if (i == 0 || reflectanceNeighbor > maxValue) {
        maxValue = reflectanceNeighbor;
      }
    }
    const int64_t maxDiff = maxValue - minValue;
388
    if (maxDiff > aps.adaptive_prediction_threshold) {
389
      uint64_t attrValue =
390
        pointCloud.getReflectance(indexesLOD[predictorIndex]);
391
392
393

      // base case: weighted average of n neighbours
      predictor.predMode = 0;
394
      uint64_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
395
396
397
      int64_t attrResidualQuant =
        computeReflectanceResidual(attrValue, attrPred, qs);

398
399
      double best_score = attrResidualQuant
        + kAttrPredLambdaR * (qs >> kFixedPointAttributeShift);
400
401
402
403
404

      for (int i = 0; i < predictor.neighborCount; i++) {
        if (i == aps.max_num_direct_predictors)
          break;

405
406
        attrPred = pointCloud.getReflectance(
          indexesLOD[predictor.neighbors[i].predictorIndex]);
407
408
409
410
        attrResidualQuant =
          computeReflectanceResidual(attrValue, attrPred, qs);

        double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
411
412
        double score = attrResidualQuant
          + idxBits * kAttrPredLambdaR * (qs >> kFixedPointAttributeShift);
413
414
415
416
417
418
419

        if (score < best_score) {
          best_score = score;
          predictor.predMode = i + 1;
          // NB: setting predictor.neighborCount = 1 will cause issues
          // with reconstruction.
        }
420
      }
421
422
423

      encoder.encodePredMode(
        predictor.predMode, aps.max_num_direct_predictors);
424
425
426
427
428
429
    }
  }
}

//----------------------------------------------------------------------------

430
void
431
AttributeEncoder::encodeReflectancesPred(
432
  const AttributeDescription& desc,
433
  const AttributeParameterSet& aps,
434
  const Quantizers& qstep,
435
436
437
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
438
439
  const uint32_t pointCount = pointCloud.getPointCount();
  std::vector<PCCPredictor> predictors;
440
441
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
442
443
444
445
446
447
448
449
450
451
452
453

  if (!aps.lod_binary_tree_enabled_flag) {
    if (aps.num_detail_levels <= 1) {
      buildPredictorsFastNoLod(
        pointCloud, aps.num_pred_nearest_neighbours, aps.search_range,
        predictors, indexesLOD);
    } else {
      buildPredictorsFast(
        pointCloud, aps.dist2, aps.num_detail_levels,
        aps.num_pred_nearest_neighbours, aps.search_range, aps.search_range,
        predictors, numberOfPointsPerLOD, indexesLOD);
    }
454
  } else {
455
456
457
458
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
459
  }
460

461
  const int64_t clipMax = (1ll << desc.attr_bitdepth) - 1;
462
  PCCResidualsEntropyEstimator context;
463
464
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
465
    auto& predictor = predictors[predictorIndex];
466
    const int64_t qs = qstep[0];
467
    computeReflectancePredictionWeights(
468
469
470
      aps, pointCloud, indexesLOD, predictorIndex, predictor, encoder, context,
      qs);

471
    const uint32_t pointIndex = indexesLOD[predictorIndex];
472
    const uint64_t reflectance = pointCloud.getReflectance(pointIndex);
473
    const uint16_t predictedReflectance =
474
      predictor.predictReflectance(pointCloud, indexesLOD);
475
476
    const int64_t quantAttValue = reflectance;
    const int64_t quantPredAttValue = predictedReflectance;
477
    const int64_t delta =
478
      PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
479
    const uint32_t attValue0 = uint32_t(IntToUInt(long(delta)));
480
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs, true);
481
482
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
483
484
485
    const uint16_t reconstructedReflectance =
      uint16_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));

486
    encoder.encode(attValue0);
487
    pointCloud.setReflectance(pointIndex, reconstructedReflectance);
488
489
490
491
492
  }
}

//----------------------------------------------------------------------------

493
Vec3<int64_t>
494
AttributeEncoder::computeColorResiduals(
495
496
  const Vec3<uint8_t> color,
  const Vec3<uint8_t> predictedColor,
497
498
499
  const int64_t qs,
  const int64_t qs2)
{
500
  Vec3<int64_t> residuals;
501
502
  const int64_t quantAttValue = color[0];
  const int64_t quantPredAttValue = predictedColor[0];
503
504
  const int64_t delta =
    PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
505
  residuals[0] = IntToUInt(delta);
506
507
508
509
  for (size_t k = 1; k < 3; ++k) {
    const int64_t quantAttValue = color[k];
    const int64_t quantPredAttValue = predictedColor[k];
    const int64_t delta =
510
      PCCQuantization(quantAttValue - quantPredAttValue, qs2, true);
511
    residuals[k] = IntToUInt(delta);
512
513
514
515
516
517
518
519
  }
  return residuals;
}

//----------------------------------------------------------------------------

void
AttributeEncoder::computeColorPredictionWeights(
520
  const AttributeParameterSet& aps,
521
  const PCCPointSet3& pointCloud,
522
523
  const std::vector<uint32_t>& indexesLOD,
  const uint32_t predictorIndex,
524
525
  PCCPredictor& predictor,
  PCCResidualsEncoder& encoder,
526
527
528
  PCCResidualsEntropyEstimator& context,
  const int64_t qs,
  const int64_t qs2)
529
530
531
532
533
{
  predictor.computeWeights();
  if (predictor.neighborCount > 1) {
    int64_t minValue[3] = {0, 0, 0};
    int64_t maxValue[3] = {0, 0, 0};
534
    for (int i = 0; i < predictor.neighborCount; ++i) {
535
      const Vec3<uint8_t> colorNeighbor =
536
        pointCloud.getColor(indexesLOD[predictor.neighbors[i].predictorIndex]);
537
538
539
540
541
542
543
544
545
546
547
548
      for (size_t k = 0; k < 3; ++k) {
        if (i == 0 || colorNeighbor[k] < minValue[k]) {
          minValue[k] = colorNeighbor[k];
        }
        if (i == 0 || colorNeighbor[k] > maxValue[k]) {
          maxValue[k] = colorNeighbor[k];
        }
      }
    }
    const int64_t maxDiff = (std::max)(
      maxValue[2] - minValue[2],
      (std::max)(maxValue[0] - minValue[0], maxValue[1] - minValue[1]));
549
    if (maxDiff > aps.adaptive_prediction_threshold) {
550
551
      Vec3<uint8_t> attrValue =
        pointCloud.getColor(indexesLOD[predictorIndex]);
552
553
554

      // base case: weighted average of n neighbours
      predictor.predMode = 0;
555
      Vec3<uint8_t> attrPred = predictor.predictColor(pointCloud, indexesLOD);
556
      Vec3<int64_t> attrResidualQuant =
557
558
559
        computeColorResiduals(attrValue, attrPred, qs, qs2);

      double best_score = attrResidualQuant[0] + attrResidualQuant[1]
560
561
        + attrResidualQuant[2]
        + kAttrPredLambdaC * (double)(qs >> kFixedPointAttributeShift);
562
563
564
565
566

      for (int i = 0; i < predictor.neighborCount; i++) {
        if (i == aps.max_num_direct_predictors)
          break;

567
568
        attrPred = pointCloud.getColor(
          indexesLOD[predictor.neighbors[i].predictorIndex]);
569
570
571
572
573
        attrResidualQuant =
          computeColorResiduals(attrValue, attrPred, qs, qs2);

        double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
        double score = attrResidualQuant[0] + attrResidualQuant[1]
574
575
          + attrResidualQuant[2]
          + idxBits * kAttrPredLambdaC * (qs >> kFixedPointAttributeShift);
576
577
578
579
580
581
582

        if (score < best_score) {
          best_score = score;
          predictor.predMode = i + 1;
          // NB: setting predictor.neighborCount = 1 will cause issues
          // with reconstruction.
        }
583
      }
584
585
586

      encoder.encodePredMode(
        predictor.predMode, aps.max_num_direct_predictors);
587
588
589
590
591
592
    }
  }
}

//----------------------------------------------------------------------------

593
void
594
AttributeEncoder::encodeColorsPred(
595
  const AttributeDescription& desc,
596
  const AttributeParameterSet& aps,
597
  const Quantizers& qstep,
598
599
600
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
601
  const size_t pointCount = pointCloud.getPointCount();
602
  std::vector<PCCPredictor> predictors;
603
604
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
605
606
607
608
609
610
611
612
613
614
615
616

  if (!aps.lod_binary_tree_enabled_flag) {
    if (aps.num_detail_levels <= 1) {
      buildPredictorsFastNoLod(
        pointCloud, aps.num_pred_nearest_neighbours, aps.search_range,
        predictors, indexesLOD);
    } else {
      buildPredictorsFast(
        pointCloud, aps.dist2, aps.num_detail_levels,
        aps.num_pred_nearest_neighbours, aps.search_range, aps.search_range,
        predictors, numberOfPointsPerLOD, indexesLOD);
    }
617
  } else {
618
619
620
621
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
622
  }
623

624
  const int64_t clipMax = (1ll << desc.attr_bitdepth) - 1;
625
626
  uint32_t values[3];
  PCCResidualsEntropyEstimator context;
627
628
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
629
    auto& predictor = predictors[predictorIndex];
630
631
    const int64_t qs = qstep[0];
    const int64_t qs2 = qstep[1];
632
    computeColorPredictionWeights(
633
634
      aps, pointCloud, indexesLOD, predictorIndex, predictor, encoder, context,
      qs, qs2);
635
    const auto pointIndex = indexesLOD[predictorIndex];
636
637
    const Vec3<uint8_t> color = pointCloud.getColor(pointIndex);
    const Vec3<uint8_t> predictedColor =
638
      predictor.predictColor(pointCloud, indexesLOD);
639
640
    const int64_t quantAttValue = color[0];
    const int64_t quantPredAttValue = predictedColor[0];
641
    const int64_t delta =
642
643
      PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs, true);
644
645
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
646
    values[0] = uint32_t(IntToUInt(long(delta)));
647
    Vec3<uint8_t> reconstructedColor;
648
    reconstructedColor[0] =
649
      uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));
650
651
652
    for (size_t k = 1; k < 3; ++k) {
      const int64_t quantAttValue = color[k];
      const int64_t quantPredAttValue = predictedColor[k];
653
      const int64_t delta =
654
655
656
        PCCQuantization(quantAttValue - quantPredAttValue, qs2, true);
      const int64_t reconstructedDelta =
        PCCInverseQuantization(delta, qs2, true);
657
658
      const int64_t reconstructedQuantAttValue =
        quantPredAttValue + reconstructedDelta;
659
      values[k] = uint32_t(IntToUInt(long(delta)));
660
      reconstructedColor[k] =
661
        uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));
662
    }
663
    pointCloud.setColor(pointIndex, reconstructedColor);
664
    encoder.encode(values[0], values[1], values[2]);
665
666
667
  }
}

668
669
//----------------------------------------------------------------------------

670
671
void
AttributeEncoder::encodeReflectancesTransformRaht(
672
  const AttributeDescription& desc,
673
  const AttributeParameterSet& aps,
674
  const Quantizers& qstep,
675
676
677
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
678
679
680
  const int voxelCount = int(pointCloud.getPointCount());
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
681
    packedVoxel[n].mortonCode = mortonAddr(pointCloud[n], 0);
682
683
684
685
686
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
687
  uint64_t* mortonCode = new uint64_t[voxelCount];
688
689
690
691
  const int attribCount = 1;
  FixedPoint* attributes = new FixedPoint[attribCount * voxelCount];
  int* integerizedAttributes = new int[attribCount * voxelCount];
  uint64_t* weight = new uint64_t[voxelCount];
692
  int* binaryLayer = new int[voxelCount];
693
694
695

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
696
    weight[n] = 1;
697
    mortonCode[n] = packedVoxel[n].mortonCode;
698
699
    const auto reflectance = pointCloud.getReflectance(packedVoxel[n].index);
    attributes[attribCount * n] = reflectance;
700
701
702
703
  }

  // Transform.
  regionAdaptiveHierarchicalTransform(
704
705
    FixedPoint(qstep[0]), mortonCode, attributes, weight, binaryLayer,
    attribCount, voxelCount, integerizedAttributes);
706

707
  // Entropy encode.
708
  uint32_t value;
709
  for (int n = 0; n < voxelCount; ++n) {
710
    const int64_t detail = IntToUInt(integerizedAttributes[n]);
711
    assert(detail < std::numeric_limits<uint32_t>::max());
712
713
    value = uint32_t(detail);
    encoder.encode(value);
714
  }
715

716
717
  // local decode
  std::fill_n(attributes, attribCount * voxelCount, FixedPoint(0));
718
  for (int n = 0; n < voxelCount; n++) {
719
720
    mortonCode[n] = packedVoxel[n].mortonCode;
    weight[n] = 1;
721
  }
722

723
  regionAdaptiveHierarchicalInverseTransform(
724
725
    FixedPoint(qstep[0]), mortonCode, attributes, weight, attribCount,
    voxelCount, integerizedAttributes);
726

727
728
  const int64_t maxReflectance = (1 << desc.attr_bitdepth) - 1;
  const int64_t minReflectance = 0;
729
  for (int n = 0; n < voxelCount; n++) {
730
731
732
733
    int64_t val = attributes[attribCount * n].round();
    const uint16_t reflectance =
      (uint16_t)PCCClip(val, minReflectance, maxReflectance);
    pointCloud.setReflectance(packedVoxel[n].index, reflectance);
734
735
736
  }

  // De-allocate arrays.
737
  delete[] binaryLayer;
738
739
740
741
742
743
744
745
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] weight;
}

//----------------------------------------------------------------------------

746
747
void
AttributeEncoder::encodeColorsTransformRaht(
748
  const AttributeDescription& desc,
749
  const AttributeParameterSet& aps,
750
  const Quantizers& qstep,
751
752
753
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
754
755
756
  const int voxelCount = int(pointCloud.getPointCount());
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
757
    packedVoxel[n].mortonCode = mortonAddr(pointCloud[n], 0);
758
759
760
761
762
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
763
  uint64_t* mortonCode = new uint64_t[voxelCount];
764
  const int attribCount = 3;
765
  FixedPoint* attributes = new FixedPoint[attribCount * voxelCount];
766
  int* integerizedAttributes = new int[attribCount * voxelCount];
767
  uint64_t* weight = new uint64_t[voxelCount];
768
  int* binaryLayer = new int[voxelCount];
769
770
771

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
772
    weight[n] = 1;
773
774
775
776
777
778
779
780
781
    mortonCode[n] = packedVoxel[n].mortonCode;
    const auto color = pointCloud.getColor(packedVoxel[n].index);
    attributes[attribCount * n] = color[0];
    attributes[attribCount * n + 1] = color[1];
    attributes[attribCount * n + 2] = color[2];
  }

  // Transform.
  regionAdaptiveHierarchicalTransform(
782
783
    FixedPoint(qstep[0]), mortonCode, attributes, weight, binaryLayer,
    attribCount, voxelCount, integerizedAttributes);
784
785

  // Entropy encode.
786
  uint32_t values[attribCount];
787
  for (int n = 0; n < voxelCount; ++n) {
788
789
790
791
792
    for (int d = 0; d < attribCount; ++d) {
      const int64_t detail =
        IntToUInt(integerizedAttributes[voxelCount * d + n]);
      assert(detail < std::numeric_limits<uint32_t>::max());
      values[d] = uint32_t(detail);
793
    }
794
    encoder.encode(values[0], values[1], values[2]);
795
796
  }

797
798
  // local decode
  std::fill_n(attributes, attribCount * voxelCount, FixedPoint(0));
799
  for (int n = 0; n < voxelCount; n++) {
800
801
    weight[n] = 1;
    mortonCode[n] = packedVoxel[n].mortonCode;
802
803
804
  }

  regionAdaptiveHierarchicalInverseTransform(
805
806
    FixedPoint(qstep[0]), mortonCode, attributes, weight, attribCount,
    voxelCount, integerizedAttributes);
807
808

  const int clipMax = (1 << desc.attr_bitdepth) - 1;
809
810
811
812
  for (int n = 0; n < voxelCount; n++) {
    const int r = attributes[attribCount * n].round();
    const int g = attributes[attribCount * n + 1].round();
    const int b = attributes[attribCount * n + 2].round();
813
    Vec3<uint8_t> color;
814
815
816
    color[0] = uint8_t(PCCClip(r, 0, clipMax));
    color[1] = uint8_t(PCCClip(g, 0, clipMax));
    color[2] = uint8_t(PCCClip(b, 0, clipMax));
817
818
819
820
    pointCloud.setColor(packedVoxel[n].index, color);
  }

  // De-allocate arrays.
821
  delete[] binaryLayer;
822
823
824
825
826
827
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] weight;
}

828
829
830
831
//----------------------------------------------------------------------------

void
AttributeEncoder::encodeColorsLift(
832
  const AttributeDescription& desc,
833
  const AttributeParameterSet& aps,
834
  const Quantizers& qstep,
835
836
837
838
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
  const size_t pointCount = pointCloud.getPointCount();
839
  std::vector<PCCPredictor> predictors;
840
841
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
842
843
844
845
846
847
848
849
850
851
852
853
854

  if (!aps.lod_binary_tree_enabled_flag) {
    buildPredictorsFast(
      pointCloud, aps.dist2, aps.num_detail_levels,
      aps.num_pred_nearest_neighbours, aps.search_range, aps.search_range,
      predictors, numberOfPointsPerLOD, indexesLOD);
  } else {
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
  }

855
856
857
858
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    predictors[predictorIndex].computeWeights();
  }
859
  std::vector<uint64_t> weights;
860
  PCCComputeQuantizationWeights(predictors, weights);
861
  const size_t lodCount = numberOfPointsPerLOD.size();
862
  std::vector<Vec3<int64_t>> colors;
863
864
865
866
867
  colors.resize(pointCount);

  for (size_t index = 0; index < pointCount; ++index) {
    const auto& color = pointCloud.getColor(indexesLOD[index]);
    for (size_t d = 0; d < 3; ++d) {
868
      colors[index][d] = int32_t(color[d]) << kFixedPointAttributeShift;
869
870
871
872
873
874
875
876
877
878
879
880
    }
  }

  for (size_t i = 0; (i + 1) < lodCount; ++i) {
    const size_t lodIndex = lodCount - i - 1;
    const size_t startIndex = numberOfPointsPerLOD[lodIndex - 1];
    const size_t endIndex = numberOfPointsPerLOD[lodIndex];
    PCCLiftPredict(predictors, startIndex, endIndex, true, colors);
    PCCLiftUpdate(predictors, weights, startIndex, endIndex, true, colors);
  }

  // compress
881
882
  const int64_t qs = qstep[0] << (kFixedPointWeightShift / 2);
  const size_t qs2 = qstep[1] << (kFixedPointWeightShift / 2);
883
884
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
885
    const int64_t quantWeight = weights[predictorIndex];
886
887
    auto& color = colors[predictorIndex];
    const int64_t delta = PCCQuantization(color[0] * quantWeight, qs);
888
    const int64_t detail = IntToUInt(delta);
889
    assert(detail < std::numeric_limits<uint32_t>::max());
890
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
891
892
893
894
895
    color[0] = reconstructedDelta / quantWeight;
    uint32_t values[3];
    values[0] = uint32_t(detail);
    for (size_t d = 1; d < 3; ++d) {
      const int64_t delta = PCCQuantization(color[d] * quantWeight, qs2);
896
      const int64_t detail = IntToUInt(delta);
897
      assert(detail < std::numeric_limits<uint32_t>::max());
898
      const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs2);
899
900
901
      color[d] = reconstructedDelta / quantWeight;
      values[d] = uint32_t(detail);
    }
902
    encoder.encode(values[0], values[1], values[2]);
903
904
905
906
907
908
909
910
911
  }

  // reconstruct
  for (size_t lodIndex = 1; lodIndex < lodCount; ++lodIndex) {
    const size_t startIndex = numberOfPointsPerLOD[lodIndex - 1];
    const size_t endIndex = numberOfPointsPerLOD[lodIndex];
    PCCLiftUpdate(predictors, weights, startIndex, endIndex, false, colors);
    PCCLiftPredict(predictors, startIndex, endIndex, false, colors);
  }
912

913
  const int64_t clipMax = (1 << desc.attr_bitdepth) - 1;
914
  for (size_t f = 0; f < pointCount; ++f) {
915
916
    const auto color0 =
      divExp2RoundHalfInf(colors[f], kFixedPointAttributeShift);
917
    Vec3<uint8_t> color;
918
    for (size_t d = 0; d < 3; ++d) {
919
      color[d] = uint8_t(PCCClip(color0[d], 0.0, clipMax));
920
    }
921
    pointCloud.setColor(indexesLOD[f], color);
922
923
924
925
926
927
928
  }
}

//----------------------------------------------------------------------------

void
AttributeEncoder::encodeReflectancesLift(
929
  const AttributeDescription& desc,
930
  const AttributeParameterSet& aps,
931
  const Quantizers& qstep,
932
933
934
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
935
936
  const size_t pointCount = pointCloud.getPointCount();
  std::vector<PCCPredictor> predictors;
937
938
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
939
940
941
942
943
944
945
946
947
948
949
950
951

  if (!aps.lod_binary_tree_enabled_flag) {
    buildPredictorsFast(
      pointCloud, aps.dist2, aps.num_detail_levels,
      aps.num_pred_nearest_neighbours, aps.search_range, aps.search_range,
      predictors, numberOfPointsPerLOD, indexesLOD);
  } else {
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
  }

952
953
954
955
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    predictors[predictorIndex].computeWeights();
  }
956
  std::vector<uint64_t> weights;
957
958
  PCCComputeQuantizationWeights(predictors, weights);

959
  const size_t lodCount = numberOfPointsPerLOD.size();
960
  std::vector<int64_t> reflectances;
961
962
963
  reflectances.resize(pointCount);

  for (size_t index = 0; index < pointCount; ++index) {
964
965
    reflectances[index] = int32_t(pointCloud.getReflectance(indexesLOD[index]))
      << kFixedPointAttributeShift;
966
967
968
969
970
971
972
973
974
975
976
977
978
979
  }

  for (size_t i = 0; (i + 1) < lodCount; ++i) {
    const size_t lodIndex = lodCount - i - 1;
    const size_t startIndex = numberOfPointsPerLOD[lodIndex - 1];
    const size_t endIndex = numberOfPointsPerLOD[lodIndex];
    PCCLiftPredict(predictors, startIndex, endIndex, true, reflectances);
    PCCLiftUpdate(
      predictors, weights, startIndex, endIndex, true, reflectances);
  }

  // compress
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
980
    const int64_t qs = qstep[0] << (kFixedPointWeightShift / 2);
981
    const int64_t quantWeight = weights[predictorIndex];
982
983
    auto& reflectance = reflectances[predictorIndex];
    const int64_t delta = PCCQuantization(reflectance * quantWeight, qs);
984
    const int64_t detail = IntToUInt(delta);