AttributeEncoder.cpp 20.6 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
#include "RAHT.h"
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

namespace pcc {

//============================================================================
// An encapsulation of the entropy coding methods used in attribute coding

struct PCCResidualsEncoder {
  uint32_t alphabetSize;
  o3dgc::Arithmetic_Codec arithmeticEncoder;
  o3dgc::Static_Bit_Model binaryModel0;
  o3dgc::Adaptive_Bit_Model binaryModelDiff0;
  o3dgc::Adaptive_Data_Model multiSymbolModelDiff0;
  o3dgc::Adaptive_Bit_Model binaryModelDiff1;
  o3dgc::Adaptive_Data_Model multiSymbolModelDiff1;

  PCCResidualsEncoder() { alphabetSize = 0; }

55
  void start(PCCBitstream& bitstream, const uint32_t alphabetSize = 64);
56
57
58
59
60
61
62
  uint32_t stop();
  inline void encode0(const uint32_t value);
  inline void encode1(const uint32_t value);
};

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

63
64
65
66
void
PCCResidualsEncoder::start(
  PCCBitstream& bitstream, const uint32_t alphabetSize)
{
67
68
69
70
71
72
73
  this->alphabetSize = alphabetSize;
  multiSymbolModelDiff0.set_alphabet(alphabetSize + 1);
  binaryModelDiff0.reset();
  multiSymbolModelDiff1.set_alphabet(alphabetSize + 1);
  binaryModelDiff1.reset();
  arithmeticEncoder.set_buffer(
    static_cast<uint32_t>(bitstream.capacity - bitstream.size),
74
    bitstream.buffer + bitstream.size);
75
76
77
78
79
  arithmeticEncoder.start_encoder();
}

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

80
81
82
uint32_t
PCCResidualsEncoder::stop()
{
83
84
85
86
87
  return arithmeticEncoder.stop_encoder();
}

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

88
89
90
inline void
PCCResidualsEncoder::encode0(const uint32_t value)
{
91
92
93
94
95
96
97
98
99
100
101
  if (value < alphabetSize) {
    arithmeticEncoder.encode(value, multiSymbolModelDiff0);
  } else {
    arithmeticEncoder.encode(alphabetSize, multiSymbolModelDiff0);
    arithmeticEncoder.ExpGolombEncode(
      value - alphabetSize, 0, binaryModel0, binaryModelDiff0);
  }
}

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

102
103
104
inline void
PCCResidualsEncoder::encode1(const uint32_t value)
{
105
106
107
108
109
110
111
112
113
114
115
116
  if (value < alphabetSize) {
    arithmeticEncoder.encode(value, multiSymbolModelDiff1);
  } else {
    arithmeticEncoder.encode(alphabetSize, multiSymbolModelDiff1);
    arithmeticEncoder.ExpGolombEncode(
      value - alphabetSize, 0, binaryModel0, binaryModelDiff1);
  }
}

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

117
118
119
120
121
void
AttributeEncoder::buildPredictors(
  const PCCAttributeEncodeParamaters& attributeParams,
  const PCCPointSet3& pointCloud)
{
122
123
124
125
  // NB: predictors are only used by the TMC3 integer lifting scheme
  if (attributeParams.transformType != TransformType::kIntegerLift)
    return;

126
127
128
129
  std::vector<uint32_t> numberOfPointsPerLOD;
  std::vector<uint32_t> indexes;
  PCCBuildPredictors(
    pointCloud, attributeParams.numberOfNearestNeighborsInPrediction,
130
131
    attributeParams.levelOfDetailCount, attributeParams.dist2, predictors,
    numberOfPointsPerLOD, indexes);
132

133
  for (auto& predictor : predictors) {
134
135
136
137
138
139
140
    predictor.computeWeights(
      attributeParams.numberOfNearestNeighborsInPrediction);
  }
}

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

141
142
143
144
145
146
void
AttributeEncoder::encodeHeader(
  const PCCAttributeEncodeParamaters& attributeParams,
  const std::string& attributeName,
  PCCBitstream& bitstream) const
{
147
  PCCWriteToBuffer<uint8_t>(
148
    uint8_t(attributeParams.transformType), bitstream.buffer, bitstream.size);
149

150
151
152
153
154
155
  if (attributeParams.transformType == TransformType::kIntegerLift) {
    PCCWriteToBuffer<uint8_t>(
      uint8_t(attributeParams.numberOfNearestNeighborsInPrediction),
      bitstream.buffer, bitstream.size);

    PCCWriteToBuffer<uint8_t>(
156
157
      uint8_t(attributeParams.levelOfDetailCount), bitstream.buffer,
      bitstream.size);
158

159
160
    for (size_t lodIndex = 0; lodIndex < attributeParams.levelOfDetailCount;
         ++lodIndex) {
161
162
163
      const uint32_t d2 = uint32_t(attributeParams.dist2[lodIndex]);
      PCCWriteToBuffer<uint32_t>(d2, bitstream.buffer, bitstream.size);
    }
164
165
166
167
    for (size_t lodIndex = 0; lodIndex < attributeParams.levelOfDetailCount;
         ++lodIndex) {
      const uint32_t qs =
        uint32_t(attributeParams.quantizationSteps[lodIndex]);
168
169
      PCCWriteToBuffer<uint32_t>(qs, bitstream.buffer, bitstream.size);
    }
170
  }
171

172
173
174
  if (attributeParams.transformType == TransformType::kRAHT) {
    PCCWriteToBuffer<uint8_t>(
      uint8_t(attributeParams.depthRaht), bitstream.buffer, bitstream.size);
175

176
    PCCWriteToBuffer<uint8_t>(
177
178
      uint8_t(attributeParams.binaryLevelThresholdRaht), bitstream.buffer,
      bitstream.size);
179

180
    PCCWriteToBuffer<uint32_t>(
181
182
      uint32_t(attributeParams.quantizationStepRaht), bitstream.buffer,
      bitstream.size);
183
  }
184
185
186
187
}

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

188
189
190
191
192
193
void
AttributeEncoder::encodeReflectances(
  const PCCAttributeEncodeParamaters& reflectanceParams,
  PCCPointSet3& pointCloud,
  PCCBitstream& bitstream)
{
194
195
196
197
198
199
  uint64_t startSize = bitstream.size;
  bitstream.size += 4;  // placehoder for bitstream size
  PCCResidualsEncoder encoder;
  const uint32_t alphabetSize = 64;
  encoder.start(bitstream, alphabetSize);

200
201
202
203
204
205
206
207
208
  switch (reflectanceParams.transformType) {
  case TransformType::kRAHT:
    encodeReflectancesTransformRaht(reflectanceParams, pointCloud, encoder);
    break;

  case TransformType::kIntegerLift:
    encodeReflectancesIntegerLift(reflectanceParams, pointCloud, encoder);
    break;
  }
209
210
211

  uint32_t compressedBitstreamSize = encoder.stop();
  bitstream.size += compressedBitstreamSize;
212
213
  PCCWriteToBuffer<uint32_t>(
    compressedBitstreamSize, bitstream.buffer, startSize);
214
215
216
217
}

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

218
219
220
221
222
223
void
AttributeEncoder::encodeColors(
  const PCCAttributeEncodeParamaters& colorParams,
  PCCPointSet3& pointCloud,
  PCCBitstream& bitstream)
{
224
225
226
227
228
229
  uint64_t startSize = bitstream.size;
  bitstream.size += 4;  // placehoder for bitstream size
  PCCResidualsEncoder encoder;
  const uint32_t alphabetSize = 64;
  encoder.start(bitstream, alphabetSize);

230
231
232
233
234
235
236
237
238
  switch (colorParams.transformType) {
  case TransformType::kRAHT:
    encodeColorsTransformRaht(colorParams, pointCloud, encoder);
    break;

  case TransformType::kIntegerLift:
    encodeColorsIntegerLift(colorParams, pointCloud, encoder);
    break;
  }
239
240
241

  uint32_t compressedBitstreamSize = encoder.stop();
  bitstream.size += compressedBitstreamSize;
242
243
  PCCWriteToBuffer<uint32_t>(
    compressedBitstreamSize, bitstream.buffer, startSize);
244
245
246
247
}

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

248
249
250
251
252
253
void
AttributeEncoder::encodeReflectancesIntegerLift(
  const PCCAttributeEncodeParamaters& reflectanceParams,
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
254
  const size_t pointCount = predictors.size();
255
256
257
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    const auto& predictor = predictors[predictorIndex];
258
259
260
261
262
    const size_t lodIndex = predictor.levelOfDetailIndex;
    const int64_t qs = reflectanceParams.quantizationSteps[lodIndex];

    const int64_t quantAttValue = pointCloud.getReflectance(predictor.index);
    const int64_t quantPredAttValue = predictor.predictReflectance(pointCloud);
263
264
    const int64_t delta =
      PCCQuantization(quantAttValue - quantPredAttValue, qs);
265
266
    const uint32_t attValue0 = uint32_t(o3dgc::IntToUInt(long(delta)));
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
267
268
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
269
    const uint16_t reconstructedReflectance = uint16_t(PCCClip(
270
271
      reconstructedQuantAttValue, int64_t(0),
      int64_t(std::numeric_limits<uint16_t>::max())));
272
273
274
275
276
277
278
    encoder.encode0(attValue0);
    pointCloud.setReflectance(predictor.index, reconstructedReflectance);
  }
}

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

279
280
281
282
283
284
void
AttributeEncoder::encodeColorsIntegerLift(
  const PCCAttributeEncodeParamaters& colorParams,
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
285
  const size_t pointCount = predictors.size();
286
287
288
  for (size_t predictorIndex = 0; predictorIndex < pointCount;
       ++predictorIndex) {
    const auto& predictor = predictors[predictorIndex];
289
290
291
292
293
294
    const PCCColor3B color = pointCloud.getColor(predictor.index);
    const PCCColor3B predictedColor = predictor.predictColor(pointCloud);
    const size_t lodIndex = predictor.levelOfDetailIndex;
    const int64_t qs = colorParams.quantizationSteps[lodIndex];
    const int64_t quantAttValue = color[0];
    const int64_t quantPredAttValue = predictedColor[0];
295
296
    const int64_t delta =
      PCCQuantization(quantAttValue - quantPredAttValue, qs);
297
298
    const uint32_t attValue0 = uint32_t(o3dgc::IntToUInt(long(delta)));
    const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
299
300
    const int64_t reconstructedQuantAttValue =
      quantPredAttValue + reconstructedDelta;
301
302
303
    encoder.encode0(attValue0);
    PCCColor3B reconstructedColor;
    reconstructedColor[0] =
304
      uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), int64_t(255)));
305
306
307
    for (size_t k = 1; k < 3; ++k) {
      const int64_t quantAttValue = color[k];
      const int64_t quantPredAttValue = predictedColor[k];
308
309
      const int64_t delta =
        PCCQuantization(quantAttValue - quantPredAttValue, qs);
310
      const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
311
312
      const int64_t reconstructedQuantAttValue =
        quantPredAttValue + reconstructedDelta;
313
314
315
      const uint32_t attValue1 = uint32_t(o3dgc::IntToUInt(long(delta)));
      encoder.encode1(attValue1);
      reconstructedColor[k] =
316
        uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), int64_t(255)));
317
318
319
320
321
    }
    pointCloud.setColor(predictor.index, reconstructedColor);
  }
}

322
323
//----------------------------------------------------------------------------

324
325
326
327
328
329
void
AttributeEncoder::encodeReflectancesTransformRaht(
  const PCCAttributeEncodeParamaters& reflectanceParams,
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  const int voxelCount = int(pointCloud.getPointCount());
  // Pack voxel into int64, sort in Morton order, and unpack.
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
    const auto position = pointCloud[n];
    const int x = int(position[0]);
    const int y = int(position[1]);
    const int z = int(position[2]);
    long long mortonCode = 0;
    for (int b = 0; b < reflectanceParams.depthRaht; b++) {
      mortonCode |= (long long)((x >> b) & 1) << (3 * b + 2);
      mortonCode |= (long long)((y >> b) & 1) << (3 * b + 1);
      mortonCode |= (long long)((z >> b) & 1) << (3 * b);
    }
    packedVoxel[n].mortonCode = mortonCode;
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
350
351
352
353
354
355
  long long* mortonCode = new long long[voxelCount];
  float* attributes = new float[voxelCount];
  int* integerizedAttributes = new int[voxelCount];
  int* sortedIntegerizedAttributes = new int[voxelCount];
  float* weight = new float[voxelCount];
  int* binaryLayer = new int[voxelCount];
356
357
358
359
360
361
362
363
364

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
    mortonCode[n] = packedVoxel[n].mortonCode;
    attributes[n] = pointCloud.getReflectance(packedVoxel[n].index);
  }

  // Transform.
  regionAdaptiveHierarchicalTransform(
365
366
    mortonCode, attributes, weight, binaryLayer, 1, voxelCount,
    reflectanceParams.depthRaht);
367

368
369
370
  // Quantize.
  for (int n = 0; n < voxelCount; n++) {
    integerizedAttributes[n] =
371
      int(round(attributes[n] / reflectanceParams.quantizationStepRaht));
372
373
374
375
376
377
378
379
380
381
382
  }

  // Sort integerized attributes by weight.
  std::vector<WeightWithIndex> sortedWeight(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
    sortedWeight[n].weight = weight[n];
    sortedWeight[n].index = n;
  }
  sort(sortedWeight.begin(), sortedWeight.end());
  for (int n = 0; n < voxelCount; n++) {
    // Put sorted integerized attributes into column-major order.
383
384
    sortedIntegerizedAttributes[n] =
      integerizedAttributes[sortedWeight[n].index];
385
386
387
388
389
390
391
392
393
394
  }
  // Entropy encode.
  for (int n = 0; n < voxelCount; ++n) {
    const int64_t detail = o3dgc::IntToUInt(sortedIntegerizedAttributes[n]);
    assert(detail < std::numeric_limits<uint32_t>::max());
    const uint32_t attValue0 = uint32_t(detail);
    encoder.encode0(attValue0);
  }
  // Re-obtain weights at the decoder by calling Raht without any attributes.
  regionAdaptiveHierarchicalTransform(
395
396
    mortonCode, nullptr, weight, binaryLayer, 0, voxelCount,
    reflectanceParams.depthRaht);
397

398
399
400
401
402
403
404
405
406
  // Sort integerized attributes by weight.
  for (int n = 0; n < voxelCount; n++) {
    sortedWeight[n].weight = weight[n];
    sortedWeight[n].index = n;
  }
  sort(sortedWeight.begin(), sortedWeight.end());
  // Unsort integerized attributes by weight.
  for (int n = 0; n < voxelCount; n++) {
    // Pull sorted integerized attributes out of column-major order.
407
408
    integerizedAttributes[sortedWeight[n].index] =
      sortedIntegerizedAttributes[n];
409
410
411
  }
  // Inverse Quantize.
  for (int n = 0; n < voxelCount; n++) {
412
413
    attributes[n] =
      integerizedAttributes[n] * reflectanceParams.quantizationStepRaht;
414
415
  }
  regionAdaptiveHierarchicalInverseTransform(
416
    mortonCode, attributes, 1, voxelCount, reflectanceParams.depthRaht);
417
418
419
420

  const int maxReflectance = std::numeric_limits<uint16_t>::max();
  const int minReflectance = 0;
  for (int n = 0; n < voxelCount; n++) {
421
422
    const int reflectance =
      PCCClip((int)round(attributes[n]), minReflectance, maxReflectance);
423
424
425
426
    pointCloud.setReflectance(packedVoxel[n].index, uint16_t(reflectance));
  }

  // De-allocate arrays.
427
  delete[] binaryLayer;
428
429
430
431
432
433
434
435
436
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] sortedIntegerizedAttributes;
  delete[] weight;
}

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

437
438
439
440
441
442
void
AttributeEncoder::encodeColorsTransformRaht(
  const PCCAttributeEncodeParamaters& colorParams,
  PCCPointSet3& pointCloud,
  PCCResidualsEncoder& encoder)
{
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  const int voxelCount = int(pointCloud.getPointCount());
  std::vector<MortonCodeWithIndex> packedVoxel(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
    const auto position = pointCloud[n];
    int x = int(position[0]);
    int y = int(position[1]);
    int z = int(position[2]);
    long long mortonCode = 0;
    for (int b = 0; b < colorParams.depthRaht; b++) {
      mortonCode |= (long long)((x >> b) & 1) << (3 * b + 2);
      mortonCode |= (long long)((y >> b) & 1) << (3 * b + 1);
      mortonCode |= (long long)((z >> b) & 1) << (3 * b);
    }
    packedVoxel[n].mortonCode = mortonCode;
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());

  // Allocate arrays.
462
  long long* mortonCode = new long long[voxelCount];
463
  const int attribCount = 3;
464
465
466
467
468
  float* attributes = new float[attribCount * voxelCount];
  int* integerizedAttributes = new int[attribCount * voxelCount];
  int* sortedIntegerizedAttributes = new int[attribCount * voxelCount];
  float* weight = new float[voxelCount];
  int* binaryLayer = new int[voxelCount];
469
470
471
472
473
474
475
476
477
478
479
480

  // Populate input arrays.
  for (int n = 0; n < voxelCount; n++) {
    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(
481
482
    mortonCode, attributes, weight, binaryLayer, attribCount, voxelCount,
    colorParams.depthRaht);
483

484
485
486
  // Quantize.
  for (int n = 0; n < voxelCount; n++) {
    for (int k = 0; k < attribCount; k++) {
487
488
      integerizedAttributes[attribCount * n + k] = int(round(
        attributes[attribCount * n + k] / colorParams.quantizationStepRaht));
489
490
491
492
493
494
495
496
497
498
499
500
501
502
    }
  }

  // Sort integerized attributes by weight.
  std::vector<WeightWithIndex> sortedWeight(voxelCount);
  for (int n = 0; n < voxelCount; n++) {
    sortedWeight[n].weight = weight[n];
    sortedWeight[n].index = n;
  }
  sort(sortedWeight.begin(), sortedWeight.end());
  for (int n = 0; n < voxelCount; n++) {
    for (int k = 0; k < attribCount; k++) {
      // Put sorted integerized attributes into column-major order.
      sortedIntegerizedAttributes[voxelCount * k + n] =
503
        integerizedAttributes[attribCount * sortedWeight[n].index + k];
504
505
506
507
508
509
510
511
512
    }
  }

  // Entropy encode.
  for (int n = 0; n < voxelCount; ++n) {
    const int64_t detail = o3dgc::IntToUInt(sortedIntegerizedAttributes[n]);
    assert(detail < std::numeric_limits<uint32_t>::max());
    const uint32_t attValue0 = uint32_t(detail);
    encoder.encode0(attValue0);
513
514
515
    if (
      binaryLayer[sortedWeight[n].index]
      >= colorParams.binaryLevelThresholdRaht) {
516
      for (int d = 1; d < 3; ++d) {
517
518
        const int64_t detail =
          o3dgc::IntToUInt(sortedIntegerizedAttributes[voxelCount * d + n]);
519
520
521
522
523
524
525
526
        assert(detail < std::numeric_limits<uint32_t>::max());
        const uint32_t attValue1 = uint32_t(detail);
        encoder.encode1(attValue1);
      }
    } else {
      for (int d = 1; d < 3; d++) {
        sortedIntegerizedAttributes[voxelCount * d + n] = 0;
      }
527
528
529
530
531
    }
  }

  // Re-obtain weights at the decoder by calling RAHT without any attributes.
  regionAdaptiveHierarchicalTransform(
532
533
    mortonCode, nullptr, weight, binaryLayer, 0, voxelCount,
    colorParams.depthRaht);
534

535
536
537
538
539
540
541
542
543
544
545
  // Sort integerized attributes by weight.
  for (int n = 0; n < voxelCount; n++) {
    sortedWeight[n].weight = weight[n];
    sortedWeight[n].index = n;
  }
  sort(sortedWeight.begin(), sortedWeight.end());
  // Unsort integerized attributes by weight.
  for (int n = 0; n < voxelCount; n++) {
    for (int k = 0; k < attribCount; k++) {
      // Pull sorted integerized attributes out of column-major order.
      integerizedAttributes[attribCount * sortedWeight[n].index + k] =
546
        sortedIntegerizedAttributes[voxelCount * k + n];
547
548
549
550
551
552
    }
  }
  // Inverse Quantize.
  for (int n = 0; n < voxelCount; n++) {
    for (int k = 0; k < attribCount; k++) {
      attributes[attribCount * n + k] =
553
554
        integerizedAttributes[attribCount * n + k]
        * colorParams.quantizationStepRaht;
555
556
557
558
    }
  }

  regionAdaptiveHierarchicalInverseTransform(
559
    mortonCode, attributes, attribCount, voxelCount, colorParams.depthRaht);
560
561
562
563
564
565
566
567
568
569
570
571
  for (size_t n = 0; n < voxelCount; n++) {
    const int r = (int)round(attributes[attribCount * n]);
    const int g = (int)round(attributes[attribCount * n + 1]);
    const int b = (int)round(attributes[attribCount * n + 2]);
    PCCColor3B color;
    color[0] = uint8_t(PCCClip(r, 0, 255));
    color[1] = uint8_t(PCCClip(g, 0, 255));
    color[2] = uint8_t(PCCClip(b, 0, 255));
    pointCloud.setColor(packedVoxel[n].index, color);
  }

  // De-allocate arrays.
572
  delete[] binaryLayer;
573
574
575
576
577
578
579
  delete[] mortonCode;
  delete[] attributes;
  delete[] integerizedAttributes;
  delete[] sortedIntegerizedAttributes;
  delete[] weight;
}

580
581
582
//============================================================================

} /* namespace pcc */