AttributeEncoder.cpp 37.8 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
61
  AdaptiveBitModel ctxZeroCnt[3];
  AdaptiveBitModel binaryModelIsOne[7];
62
  DualLutCoder<false> symbolCoder[2];
63

64
  void start(int numPoints);
65
  int stop();
66
  void encodePredMode(int value, int max);
67
  void encodeZeroCnt(int value, int max);
68
69
70
  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);
71
72
73
74
};

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

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

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

86
int
87
88
PCCResidualsEncoder::stop()
{
89
  return arithmeticEncoder.stop();
90
91
92
93
}

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

94
void
95
PCCResidualsEncoder::encodePredMode(int mode, int maxMode)
96
{
97
98
99
100
101
102
103
104
105
106
107
108
109
  // 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]);
110
111
112
113
}

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

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
void
PCCResidualsEncoder::encodeZeroCnt(int mode, int maxMode)
{
  // max = 0 => no direct predictors are used
  if (maxMode == 0)
    return;

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

  // Truncated unary
  if (mode != maxMode)
    arithmeticEncoder.encode(0, ctxZeroCnt[ctxIdx]);
}

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

134
135
void
PCCResidualsEncoder::encodeSymbol(uint32_t value, int k1, int k2)
136
{
137
138
139
140
141
  bool isZero = value == 0;
  arithmeticEncoder.encode(isZero, binaryModelIsZero[k1]);
  if (isZero) {
    return;
  }
142
143
144
145
146
147
148
149

  bool isOne = value == 1;
  arithmeticEncoder.encode(isOne, binaryModelIsOne[k1]);
  if (isOne) {
    return;
  }
  value -= 2;

150
  if (value < kAttributeResidualAlphabetSize) {
151
    symbolCoder[k2].encode(value, &arithmeticEncoder);
152
  } else {
153
    int alphabetSize = kAttributeResidualAlphabetSize;
154
    symbolCoder[k2].encode(alphabetSize, &arithmeticEncoder);
155
    arithmeticEncoder.encodeExpGolomb(
156
      value - alphabetSize, 0, binaryModel0, binaryModelDiff[k1]);
157
158
159
  }
}

160
161
162
//----------------------------------------------------------------------------

void
163
PCCResidualsEncoder::encode(uint32_t value0, uint32_t value1, uint32_t value2)
164
{
165
166
167
168
169
170
  if (value0 == value1 && value0 == value2) {
    value0--;
    value1--;
    value2--;
  }

171
172
173
174
175
176
177
178
179
180
181
182
  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)
{
183
  encodeSymbol(value - 1, 0, 0);
184
185
}

186
//============================================================================
187
188
189
// An encapsulation of the entropy coding methods used in attribute coding

struct PCCResidualsEntropyEstimator {
190
191
  size_t freq0[kAttributeResidualAlphabetSize + 1];
  size_t freq1[kAttributeResidualAlphabetSize + 1];
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  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);
};

//----------------------------------------------------------------------------
211

212
void
213
PCCResidualsEntropyEstimator::init()
214
{
215
  for (size_t i = 0; i <= kAttributeResidualAlphabetSize; ++i) {
216
217
218
    freq0[i] = 1;
    freq1[i] = 1;
  }
219
220
  symbolCount0 = kAttributeResidualAlphabetSize + 1;
  symbolCount1 = kAttributeResidualAlphabetSize + 1;
221
222
  isZero1Count = isZero0Count = symbolCount0 / 2;
}
223

224
225
226
227
228
229
230
231
232
//----------------------------------------------------------------------------

double
PCCResidualsEntropyEstimator::bitsDetail(
  const uint32_t detail,
  const size_t symbolCount,
  const size_t* const freq) const
{
  const uint32_t detailClipped =
233
    std::min(detail, uint32_t(kAttributeResidualAlphabetSize));
234
235
236
  const double pDetail =
    PCCClip(double(freq[detailClipped]) / symbolCount, 0.001, 0.999);
  double bits = -log2(pDetail);
237
238
  if (detail >= kAttributeResidualAlphabetSize) {
    const double x = double(detail) - double(kAttributeResidualAlphabetSize);
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    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);
256
  }
257
  return bits;
258
259
260
261
}

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

262
263
264
265
266
267
void
PCCResidualsEntropyEstimator::update(const uint32_t value0)
{
  const bool isZero0 = value0 == 0;
  ++symbolCount0;
  if (!isZero0) {
268
    ++freq0[std::min(value0 - 1, uint32_t(kAttributeResidualAlphabetSize))];
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  } 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) {
310
    ++freq0[std::min(value0 - 1, uint32_t(kAttributeResidualAlphabetSize))];
311
312
313
314
315
316
317
  } else {
    ++isZero0Count;
  }

  const bool isZero1 = value1 == 0 && value2 == 0;
  symbolCount1 += 2;
  if (!isZero1) {
318
319
    ++freq1[std::min(value1, uint32_t(kAttributeResidualAlphabetSize))];
    ++freq1[std::min(value2, uint32_t(kAttributeResidualAlphabetSize))];
320
321
322
323
324
325
326
327
  } else {
    ++isZero1Count;
  }
}

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

328
void
329
AttributeEncoder::encode(
330
  const AttributeDescription& desc,
331
  const AttributeParameterSet& attr_aps,
332
  const AttributeBrickHeader& abh,
333
  PCCPointSet3& pointCloud,
334
  PayloadBuffer* payload)
335
{
336
  Quantizers qstep = deriveQuantSteps(attr_aps, abh);
337

338
  PCCResidualsEncoder encoder;
339
  encoder.start(int(pointCloud.getPointCount()));
340

341
  if (desc.attr_num_dimensions == 1) {
342
343
    switch (attr_aps.attr_encoding) {
    case AttributeEncoding::kRAHTransform:
344
345
      encodeReflectancesTransformRaht(
        desc, attr_aps, qstep, pointCloud, encoder);
346
      break;
347

348
    case AttributeEncoding::kPredictingTransform:
349
      encodeReflectancesPred(desc, attr_aps, qstep, pointCloud, encoder);
350
      break;
351

352
    case AttributeEncoding::kLiftingTransform:
353
      encodeReflectancesLift(desc, attr_aps, qstep, pointCloud, encoder);
354
355
      break;
    }
356
  } else if (desc.attr_num_dimensions == 3) {
357
358
    switch (attr_aps.attr_encoding) {
    case AttributeEncoding::kRAHTransform:
359
      encodeColorsTransformRaht(desc, attr_aps, qstep, pointCloud, encoder);
360
361
362
      break;

    case AttributeEncoding::kPredictingTransform:
363
      encodeColorsPred(desc, attr_aps, qstep, pointCloud, encoder);
364
365
366
      break;

    case AttributeEncoding::kLiftingTransform:
367
      encodeColorsLift(desc, attr_aps, qstep, pointCloud, encoder);
368
369
370
      break;
    }
  } else {
371
    assert(desc.attr_num_dimensions == 1 || desc.attr_num_dimensions == 3);
372
  }
373

374
375
376
377
  uint32_t acDataLen = encoder.stop();
  std::copy_n(
    encoder.arithmeticEncoder.buffer(), acDataLen,
    std::back_inserter(*payload));
378
379
380
381
}

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

382
383
384
385
386
387
388
389
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;
390
391
392
  const int64_t delta =
    PCCQuantization(quantAttValue - quantPredAttValue, qs, true);

393
  return IntToUInt(delta);
394
395
396
397
398
399
}

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

void
AttributeEncoder::computeReflectancePredictionWeights(
400
  const AttributeParameterSet& aps,
401
  const PCCPointSet3& pointCloud,
402
403
  const std::vector<uint32_t>& indexesLOD,
  const uint32_t predictorIndex,
404
405
  PCCPredictor& predictor,
  PCCResidualsEncoder& encoder,
406
407
  PCCResidualsEntropyEstimator& context,
  const int64_t qs)
408
409
{
  predictor.computeWeights();
410
  predictor.maxDiff = -1;
411
412
413
  if (predictor.neighborCount > 1) {
    int64_t minValue = 0;
    int64_t maxValue = 0;
414
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
415
      const uint64_t reflectanceNeighbor = pointCloud.getReflectance(
416
        indexesLOD[predictor.neighbors[i].predictorIndex]);
417
418
419
420
421
422
423
424
      if (i == 0 || reflectanceNeighbor < minValue) {
        minValue = reflectanceNeighbor;
      }
      if (i == 0 || reflectanceNeighbor > maxValue) {
        maxValue = reflectanceNeighbor;
      }
    }
    const int64_t maxDiff = maxValue - minValue;
425
    predictor.maxDiff = maxDiff;
426
    if (maxDiff > aps.adaptive_prediction_threshold) {
427
      uint64_t attrValue =
428
        pointCloud.getReflectance(indexesLOD[predictorIndex]);
429
430
431

      // base case: weighted average of n neighbours
      predictor.predMode = 0;
432
      uint64_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
433
434
435
      int64_t attrResidualQuant =
        computeReflectanceResidual(attrValue, attrPred, qs);

436
437
      double best_score = attrResidualQuant
        + kAttrPredLambdaR * (qs >> kFixedPointAttributeShift);
438
439
440
441
442

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

443
444
        attrPred = pointCloud.getReflectance(
          indexesLOD[predictor.neighbors[i].predictorIndex]);
445
446
447
448
        attrResidualQuant =
          computeReflectanceResidual(attrValue, attrPred, qs);

        double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
449
450
        double score = attrResidualQuant
          + idxBits * kAttrPredLambdaR * (qs >> kFixedPointAttributeShift);
451
452
453
454
455
456
457

        if (score < best_score) {
          best_score = score;
          predictor.predMode = i + 1;
          // NB: setting predictor.neighborCount = 1 will cause issues
          // with reconstruction.
        }
458
459
460
461
462
463
464
      }
    }
  }
}

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

465
void
466
AttributeEncoder::encodeReflectancesPred(
467
  const AttributeDescription& desc,
468
  const AttributeParameterSet& aps,
469
  const Quantizers& qstep,
470
471
472
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
473
474
  const uint32_t pointCount = pointCloud.getPointCount();
  std::vector<PCCPredictor> predictors;
475
476
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
477
478
479
480
481
482
483
484

  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(
485
486
487
488
        pointCloud, aps.lod_decimation_enabled_flag, aps.dist2,
        aps.num_detail_levels, aps.num_pred_nearest_neighbours,
        aps.search_range, aps.search_range, predictors, numberOfPointsPerLOD,
        indexesLOD);
489
    }
490
  } else {
491
492
493
494
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
495
  }
496

497
  const int64_t clipMax = (1ll << desc.attr_bitdepth) - 1;
498
  PCCResidualsEntropyEstimator context;
499
500
501
502
503
504
  int zero_cnt = 0;
  std::vector<int> zerorun;
  zerorun.reserve(pointCount);
  std::vector<uint32_t> residual;
  residual.resize(pointCount);

505
506
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
507
    auto& predictor = predictors[predictorIndex];
508
    const int64_t qs = qstep[0];
509
    computeReflectancePredictionWeights(
510
511
512
      aps, pointCloud, indexesLOD, predictorIndex, predictor, encoder, context,
      qs);

513
    const uint32_t pointIndex = indexesLOD[predictorIndex];
514
    const uint64_t reflectance = pointCloud.getReflectance(pointIndex);
515
    const uint16_t predictedReflectance =
516
      predictor.predictReflectance(pointCloud, indexesLOD);
517
518
    const int64_t quantAttValue = reflectance;
    const int64_t quantPredAttValue = predictedReflectance;
519
    const int64_t delta =
520
      PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
521
    const uint32_t attValue0 = uint32_t(IntToUInt(long(delta)));
522
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs, true);
523
524
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
525
526
527
    const uint16_t reconstructedReflectance =
      uint16_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));

528
529
530
531
532
533
534
    if (!attValue0)
      ++zero_cnt;
    else {
      zerorun.push_back(zero_cnt);
      zero_cnt = 0;
    }
    residual[predictorIndex] = attValue0;
535
    pointCloud.setReflectance(pointIndex, reconstructedReflectance);
536
  }
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

  zerorun.push_back(zero_cnt);
  int run_index = 0;
  encoder.encodeZeroCnt(zerorun[run_index], pointCount);
  zero_cnt = zerorun[run_index++];

  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    auto& predictor = predictors[predictorIndex];
    if (predictor.maxDiff > aps.adaptive_prediction_threshold) {
      encoder.encodePredMode(
        predictor.predMode, aps.max_num_direct_predictors);
    }
    if (zero_cnt > 0)
      zero_cnt--;
    else {
      encoder.encode(residual[predictorIndex]);
      if (predictorIndex != pointCount - 1)
        encoder.encodeZeroCnt(zerorun[run_index], pointCount);
      zero_cnt = zerorun[run_index++];
    }
  }
559
560
561
562
}

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

563
Vec3<int64_t>
564
AttributeEncoder::computeColorResiduals(
565
566
  const Vec3<uint8_t> color,
  const Vec3<uint8_t> predictedColor,
567
568
569
  const int64_t qs,
  const int64_t qs2)
{
570
  Vec3<int64_t> residuals;
571
572
  const int64_t quantAttValue = color[0];
  const int64_t quantPredAttValue = predictedColor[0];
573
574
  const int64_t delta =
    PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
575
  residuals[0] = IntToUInt(delta);
576
577
578
579
  for (size_t k = 1; k < 3; ++k) {
    const int64_t quantAttValue = color[k];
    const int64_t quantPredAttValue = predictedColor[k];
    const int64_t delta =
580
      PCCQuantization(quantAttValue - quantPredAttValue, qs2, true);
581
    residuals[k] = IntToUInt(delta);
582
583
584
585
586
587
588
589
  }
  return residuals;
}

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

void
AttributeEncoder::computeColorPredictionWeights(
590
  const AttributeParameterSet& aps,
591
  const PCCPointSet3& pointCloud,
592
593
  const std::vector<uint32_t>& indexesLOD,
  const uint32_t predictorIndex,
594
595
  PCCPredictor& predictor,
  PCCResidualsEncoder& encoder,
596
597
598
  PCCResidualsEntropyEstimator& context,
  const int64_t qs,
  const int64_t qs2)
599
600
{
  predictor.computeWeights();
601
  predictor.maxDiff = -1;
602
603
604
  if (predictor.neighborCount > 1) {
    int64_t minValue[3] = {0, 0, 0};
    int64_t maxValue[3] = {0, 0, 0};
605
    for (int i = 0; i < predictor.neighborCount; ++i) {
606
      const Vec3<uint8_t> colorNeighbor =
607
        pointCloud.getColor(indexesLOD[predictor.neighbors[i].predictorIndex]);
608
609
610
611
612
613
614
615
616
617
618
619
      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]));
620
621
    predictor.maxDiff = maxDiff;

622
    if (maxDiff > aps.adaptive_prediction_threshold) {
623
624
      Vec3<uint8_t> attrValue =
        pointCloud.getColor(indexesLOD[predictorIndex]);
625
626
627

      // base case: weighted average of n neighbours
      predictor.predMode = 0;
628
      Vec3<uint8_t> attrPred = predictor.predictColor(pointCloud, indexesLOD);
629
      Vec3<int64_t> attrResidualQuant =
630
631
632
        computeColorResiduals(attrValue, attrPred, qs, qs2);

      double best_score = attrResidualQuant[0] + attrResidualQuant[1]
633
634
        + attrResidualQuant[2]
        + kAttrPredLambdaC * (double)(qs >> kFixedPointAttributeShift);
635
636
637
638
639

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

640
641
        attrPred = pointCloud.getColor(
          indexesLOD[predictor.neighbors[i].predictorIndex]);
642
643
644
645
646
        attrResidualQuant =
          computeColorResiduals(attrValue, attrPred, qs, qs2);

        double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
        double score = attrResidualQuant[0] + attrResidualQuant[1]
647
648
          + attrResidualQuant[2]
          + idxBits * kAttrPredLambdaC * (qs >> kFixedPointAttributeShift);
649
650
651
652
653
654
655

        if (score < best_score) {
          best_score = score;
          predictor.predMode = i + 1;
          // NB: setting predictor.neighborCount = 1 will cause issues
          // with reconstruction.
        }
656
657
658
659
660
661
662
      }
    }
  }
}

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

663
void
664
AttributeEncoder::encodeColorsPred(
665
  const AttributeDescription& desc,
666
  const AttributeParameterSet& aps,
667
  const Quantizers& qstep,
668
669
670
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
671
  const size_t pointCount = pointCloud.getPointCount();
672
  std::vector<PCCPredictor> predictors;
673
674
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
675
676
677
678
679
680
681
682

  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(
683
684
685
686
        pointCloud, aps.lod_decimation_enabled_flag, aps.dist2,
        aps.num_detail_levels, aps.num_pred_nearest_neighbours,
        aps.search_range, aps.search_range, predictors, numberOfPointsPerLOD,
        indexesLOD);
687
    }
688
  } else {
689
690
691
692
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
693
  }
694

695
  const int64_t clipMax = (1ll << desc.attr_bitdepth) - 1;
696
697
  uint32_t values[3];
  PCCResidualsEntropyEstimator context;
698
699
700
701
702
703
704
705

  int zero_cnt = 0;
  std::vector<int> zerorun;
  std::vector<uint32_t> residual[3];
  for (int i = 0; i < 3; i++) {
    residual[i].resize(pointCount);
  }

706
707
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
708
    auto& predictor = predictors[predictorIndex];
709
710
    const int64_t qs = qstep[0];
    const int64_t qs2 = qstep[1];
711
    computeColorPredictionWeights(
712
713
      aps, pointCloud, indexesLOD, predictorIndex, predictor, encoder, context,
      qs, qs2);
714
    const auto pointIndex = indexesLOD[predictorIndex];
715
716
    const Vec3<uint8_t> color = pointCloud.getColor(pointIndex);
    const Vec3<uint8_t> predictedColor =
717
      predictor.predictColor(pointCloud, indexesLOD);
718
719
    const int64_t quantAttValue = color[0];
    const int64_t quantPredAttValue = predictedColor[0];
720
    const int64_t delta =
721
722
      PCCQuantization(quantAttValue - quantPredAttValue, qs, true);
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs, true);
723
724
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
725
    values[0] = uint32_t(IntToUInt(long(delta)));
726
    Vec3<uint8_t> reconstructedColor;
727
    reconstructedColor[0] =
728
      uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));
729
730
731
    for (size_t k = 1; k < 3; ++k) {
      const int64_t quantAttValue = color[k];
      const int64_t quantPredAttValue = predictedColor[k];
732
      const int64_t delta =
733
734
735
        PCCQuantization(quantAttValue - quantPredAttValue, qs2, true);
      const int64_t reconstructedDelta =
        PCCInverseQuantization(delta, qs2, true);
736
737
      const int64_t reconstructedQuantAttValue =
        quantPredAttValue + reconstructedDelta;
738
      values[k] = uint32_t(IntToUInt(long(delta)));
739
      reconstructedColor[k] =
740
        uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), clipMax));
741
    }
742
    pointCloud.setColor(pointIndex, reconstructedColor);
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777

    if (!values[0] && !values[1] && !values[2]) {
      ++zero_cnt;
    } else {
      zerorun.push_back(zero_cnt);
      zero_cnt = 0;
    }

    for (int i = 0; i < 3; i++) {
      residual[i][predictorIndex] = values[i];
    }
  }

  zerorun.push_back(zero_cnt);
  int run_index = 0;
  encoder.encodeZeroCnt(zerorun[run_index], pointCount);
  zero_cnt = zerorun[run_index++];
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    auto& predictor = predictors[predictorIndex];
    if (predictor.maxDiff > aps.adaptive_prediction_threshold) {
      encoder.encodePredMode(
        predictor.predMode, aps.max_num_direct_predictors);
    }
    if (zero_cnt > 0)
      zero_cnt--;
    else {
      for (size_t k = 0; k < 3; k++)
        values[k] = residual[k][predictorIndex];

      encoder.encode(values[0], values[1], values[2]);
      if (predictorIndex != pointCount - 1)
        encoder.encodeZeroCnt(zerorun[run_index], pointCount);
      zero_cnt = zerorun[run_index++];
    }
778
779
780
  }
}

781
782
//----------------------------------------------------------------------------

783
784
void
AttributeEncoder::encodeReflectancesTransformRaht(
785
  const AttributeDescription& desc,
786
  const AttributeParameterSet& aps,
787
  const Quantizers& qstep,
788
789
790
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
791
792
793
  const int voxelCount = int(pointCloud.getPointCount());
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
794
    packedVoxel[n].mortonCode = mortonAddr(pointCloud[n], 0);
795
796
797
798
799
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
800
  uint64_t* mortonCode = new uint64_t[voxelCount];
801
802
803
804
  const int attribCount = 1;
  FixedPoint* attributes = new FixedPoint[attribCount * voxelCount];
  int* integerizedAttributes = new int[attribCount * voxelCount];
  uint64_t* weight = new uint64_t[voxelCount];
805
  int* binaryLayer = new int[voxelCount];
806
807
808

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
809
    weight[n] = 1;
810
    mortonCode[n] = packedVoxel[n].mortonCode;
811
812
    const auto reflectance = pointCloud.getReflectance(packedVoxel[n].index);
    attributes[attribCount * n] = reflectance;
813
814
815
816
  }

  // Transform.
  regionAdaptiveHierarchicalTransform(
817
818
    FixedPoint(qstep[0]), mortonCode, attributes, weight, binaryLayer,
    attribCount, voxelCount, integerizedAttributes);
819

820
  // Entropy encode.
821
  int zero_cnt = 0;
822
  uint32_t value;
823
  for (int n = 0; n < voxelCount; ++n) {
824
    const int64_t detail = IntToUInt(integerizedAttributes[n]);
825
    assert(detail < std::numeric_limits<uint32_t>::max());
826
    value = uint32_t(detail);
827
828
829
830
831
832
833
    if (!value)
      ++zero_cnt;
    else {
      encoder.encodeZeroCnt(zero_cnt, voxelCount);
      encoder.encode(value);
      zero_cnt = 0;
    }
834
  }
835
  encoder.encodeZeroCnt(zero_cnt, voxelCount);
836

837
838
  // local decode
  std::fill_n(attributes, attribCount * voxelCount, FixedPoint(0));
839
  for (int n = 0; n < voxelCount; n++) {
840
841
    mortonCode[n] = packedVoxel[n].mortonCode;
    weight[n] = 1;
842
  }
843

844
  regionAdaptiveHierarchicalInverseTransform(
845
846
    FixedPoint(qstep[0]), mortonCode, attributes, weight, attribCount,
    voxelCount, integerizedAttributes);
847

848
849
  const int64_t maxReflectance = (1 << desc.attr_bitdepth) - 1;
  const int64_t minReflectance = 0;
850
  for (int n = 0; n < voxelCount; n++) {
851
852
853
854
    int64_t val = attributes[attribCount * n].round();
    const uint16_t reflectance =
      (uint16_t)PCCClip(val, minReflectance, maxReflectance);
    pointCloud.setReflectance(packedVoxel[n].index, reflectance);
855
856
857
  }

  // De-allocate arrays.
858
  delete[] binaryLayer;
859
860
861
862
863
864
865
866
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] weight;
}

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

867
868
void
AttributeEncoder::encodeColorsTransformRaht(
869
  const AttributeDescription& desc,
870
  const AttributeParameterSet& aps,
871
  const Quantizers& qstep,
872
873
874
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
875
876
877
  const int voxelCount = int(pointCloud.getPointCount());
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
878
    packedVoxel[n].mortonCode = mortonAddr(pointCloud[n], 0);
879
880
881
882
883
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
884
  uint64_t* mortonCode = new uint64_t[voxelCount];
885
  const int attribCount = 3;
886
  FixedPoint* attributes = new FixedPoint[attribCount * voxelCount];
887
  int* integerizedAttributes = new int[attribCount * voxelCount];
888
  uint64_t* weight = new uint64_t[voxelCount];
889
  int* binaryLayer = new int[voxelCount];
890
891
892

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
893
    weight[n] = 1;
894
895
896
897
898
899
900
901
902
    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(
903
904
    FixedPoint(qstep[0]), mortonCode, attributes, weight, binaryLayer,
    attribCount, voxelCount, integerizedAttributes);
905
906

  // Entropy encode.
907
  uint32_t values[attribCount];
908
  int zero_cnt = 0;
909
  for (int n = 0; n < voxelCount; ++n) {
910
911
912
913
914
    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);
915
    }
916
917
918
919
920
921
922
    if (!values[0] && !values[1] && !values[2])
      ++zero_cnt;
    else {
      encoder.encodeZeroCnt(zero_cnt, voxelCount);
      encoder.encode(values[0], values[1], values[2]);
      zero_cnt = 0;
    }
923
924
  }

925
926
  encoder.encodeZeroCnt(zero_cnt, voxelCount);

927
928
  // local decode
  std::fill_n(attributes, attribCount * voxelCount, FixedPoint(0));
929
  for (int n = 0; n < voxelCount; n++) {
930
931
    weight[n] = 1;
    mortonCode[n] = packedVoxel[n].mortonCode;
932
933
934
  }

  regionAdaptiveHierarchicalInverseTransform(
935
936
    FixedPoint(qstep[0]), mortonCode, attributes, weight, attribCount,
    voxelCount, integerizedAttributes);
937
938

  const int clipMax = (1 << desc.attr_bitdepth) - 1;
939
940
941
942
  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();
943
    Vec3<uint8_t> color;
944
945
946
    color[0] = uint8_t(PCCClip(r, 0, clipMax));
    color[1] = uint8_t(PCCClip(g, 0, clipMax));
    color[2] = uint8_t(PCCClip(b, 0, clipMax));
947
948
949
950
    pointCloud.setColor(packedVoxel[n].index, color);
  }

  // De-allocate arrays.
951
  delete[] binaryLayer;
952
953
954
955
956
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] weight;
}
957
958
959
960
//----------------------------------------------------------------------------

void
AttributeEncoder::encodeColorsLift(
961
  const AttributeDescription& desc,
962
  const AttributeParameterSet& aps,
963
  const Quantizers& qstep,
964
965
966
967
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
  const size_t pointCount = pointCloud.getPointCount();
968
  std::vector<PCCPredictor> predictors;
969
970
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexesLOD;
971
972
973

  if (!aps.lod_binary_tree_enabled_flag) {
    buildPredictorsFast(
974
975
976
      pointCloud, aps.lod_decimation_enabled_flag, aps.dist2,
      aps.num_detail_levels, aps.num_pred_nearest_neighbours, aps.search_range,
      aps.search_range, predictors, numberOfPointsPerLOD, indexesLOD);
977
978
979
980
981
982
983
  } else {
    buildLevelOfDetailBinaryTree(pointCloud, numberOfPointsPerLOD, indexesLOD);
    computePredictors(
      pointCloud, numberOfPointsPerLOD, indexesLOD,
      aps.num_pred_nearest_neighbours, predictors);
  }

984
985
986
987
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    predictors[predictorIndex].computeWeights();
  }
988
  std::vector<uint64_t> weights;
989
  PCCComputeQuantizationWeights(predictors, weights);
990
  const size_t lodCount = numberOfPointsPerLOD.size();
991
  std::vector<Vec3<int64_t>> colors;
992
993
994
995
996
  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) {
997
      colors[index][d] = int32_t(color[d]) << kFixedPointAttributeShift;
998
999
1000
    }
  }

For faster browsing, not all history is shown. View entire blame