PCCTMC3Common.h 26.8 KB
Newer Older
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
1
/* The copyright in this software is being made available under the BSD
David Flynn's avatar
David Flynn committed
2
3
4
 * 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.
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
5
 *
David Flynn's avatar
David Flynn committed
6
 * Copyright (c) 2017-2018, ISO/IEC
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
7
8
9
10
11
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
David Flynn's avatar
David Flynn committed
12
13
14
15
16
17
18
19
20
21
 * * 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.
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
22
23
24
25
 *
 * 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
David Flynn's avatar
David Flynn committed
26
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
28
29
30
31
 * 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)
David Flynn's avatar
David Flynn committed
32
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
34
35
36
37
 */

#ifndef PCCTMC3Common_h
#define PCCTMC3Common_h
38

Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
39
#include "PCCMath.h"
40
#include "PCCPointSet.h"
41
#include "constants.h"
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
42

43
44
#include "nanoflann.hpp"

45
#include <cstdint>
David Flynn's avatar
David Flynn committed
46
#include <cstddef>
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
47
48
49
50
#include <vector>

namespace pcc {

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Structure for sorting weights.
struct WeightWithIndex {
  float weight;
  int index;

  WeightWithIndex() = default;

  WeightWithIndex(const int index, const float weight)
    : weight(weight), index(index)
  {}

  // NB: this definition ranks larger weights before smaller values.
  bool operator<(const WeightWithIndex& rhs) const
  {
    // NB: index used to maintain stable sort
    if (weight == rhs.weight)
      return index < rhs.index;
    return weight > rhs.weight;
  }
};

72
73
74
//---------------------------------------------------------------------------

struct MortonCodeWithIndex {
75
  int64_t mortonCode;
76
77
78
79
80
81
82
83
84
85
86
87
  int32_t index;
  bool operator<(const MortonCodeWithIndex& rhs) const
  {
    // NB: index used to maintain stable sort
    if (mortonCode == rhs.mortonCode)
      return index < rhs.index;
    return mortonCode < rhs.mortonCode;
  }
};

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

Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
88
struct PCCNeighborInfo {
89
  uint64_t weight;
90
  uint32_t predictorIndex;
91
  uint32_t insertIndex;
92
93
  bool operator<(const PCCNeighborInfo& rhs) const
  {
94
    return (weight == rhs.weight) ? insertIndex < rhs.insertIndex
95
96
                                  : weight < rhs.weight;
  }
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
97
98
};

99
100
//---------------------------------------------------------------------------

Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
101
struct PCCPredictor {
102
  uint32_t neighborCount;
103
  PCCNeighborInfo neighbors[kAttributePredictionMaxNeighbourCount];
104
  int8_t predMode;
105
  int64_t maxDiff;
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
106

107
  Vec3<uint8_t> predictColor(
108
    const PCCPointSet3& pointCloud, const std::vector<uint32_t>& indexes) const
109
  {
110
    Vec3<int64_t> predicted(0);
111
112
113
    if (predMode > neighborCount) {
      /* nop */
    } else if (predMode > 0) {
114
      const Vec3<uint8_t> color =
115
        pointCloud.getColor(indexes[neighbors[predMode - 1].predictorIndex]);
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
116
      for (size_t k = 0; k < 3; ++k) {
117
118
119
120
        predicted[k] += color[k];
      }
    } else {
      for (size_t i = 0; i < neighborCount; ++i) {
121
        const Vec3<uint8_t> color =
122
          pointCloud.getColor(indexes[neighbors[i].predictorIndex]);
123
        const uint32_t w = neighbors[i].weight;
124
125
126
        for (size_t k = 0; k < 3; ++k) {
          predicted[k] += w * color[k];
        }
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
127
      }
128
129
130
131
      for (uint32_t k = 0; k < 3; ++k) {
        predicted[k] =
          divExp2RoundHalfInf(predicted[k], kFixedPointWeightShift);
      }
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
132
    }
133
    return Vec3<uint8_t>(predicted[0], predicted[1], predicted[2]);
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
134
  }
135

136
  int64_t predictReflectance(
137
    const PCCPointSet3& pointCloud, const std::vector<uint32_t>& indexes) const
138
  {
139
    int64_t predicted(0);
140
141
142
    if (predMode > neighborCount) {
      /* nop */
    } else if (predMode > 0) {
143
144
      predicted = pointCloud.getReflectance(
        indexes[neighbors[predMode - 1].predictorIndex]);
145
146
    } else {
      for (size_t i = 0; i < neighborCount; ++i) {
147
148
        predicted += neighbors[i].weight
          * pointCloud.getReflectance(indexes[neighbors[i].predictorIndex]);
149
      }
150
      predicted = divExp2RoundHalfInf(predicted, kFixedPointWeightShift);
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
151
    }
152
    return predicted;
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
153
154
  }

155
  void computeWeights()
156
  {
157
158
159
160
161
162
163
164
165
    const uint32_t shift = (1 << kFixedPointWeightShift);
    int32_t n = 0;
    while ((neighbors[0].weight >> n) >= shift) {
      ++n;
    }
    if (n > 0) {
      for (size_t i = 0; i < neighborCount; ++i) {
        neighbors[i].weight = (neighbors[i].weight + (1 << (n - 1))) >> n;
      }
166
    }
167
168
169
170
171
172
173
174
    while (neighborCount > 1) {
      if (
        neighbors[neighborCount - 1].weight
        >= (neighbors[neighborCount - 2].weight << kFixedPointWeightShift)) {
        --neighborCount;
      } else {
        break;
      }
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
175
    }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    if (neighborCount <= 1) {
      neighbors[0].weight = shift;
    } else if (neighborCount == 2) {
      const uint64_t d0 = neighbors[0].weight;
      const uint64_t d1 = neighbors[1].weight;
      const uint64_t sum = d1 + d0;
      const uint64_t w1 = (d0 << kFixedPointWeightShift) / sum;
      const uint64_t w0 = shift - w1;
      neighbors[0].weight = uint32_t(w0);
      neighbors[1].weight = uint32_t(w1);
    } else {
      neighborCount = 3;
      const uint64_t d0 = neighbors[0].weight;
      const uint64_t d1 = neighbors[1].weight;
      const uint64_t d2 = neighbors[2].weight;
      const uint64_t sum = d1 * d2 + d0 * d2 + d0 * d1;
      const uint64_t w2 = ((d0 * d1) << kFixedPointWeightShift) / sum;
      const uint64_t w1 = ((d0 * d2) << kFixedPointWeightShift) / sum;
      const uint64_t w0 = shift - (w1 + w2);
      neighbors[0].weight = uint32_t(w0);
      neighbors[1].weight = uint32_t(w1);
      neighbors[2].weight = uint32_t(w2);
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
198
199
200
    }
  }

201
  void init(const uint32_t predictorIndex)
202
  {
203
    neighborCount = (predictorIndex != PCC_UNDEFINED_INDEX) ? 1 : 0;
204
    neighbors[0].predictorIndex = predictorIndex;
205
    neighbors[0].weight = 1;
206
    predMode = 0;
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
207
  }
208
209
210
211
212

  void init() { neighborCount = 0; }

  void insertNeighbor(
    const uint32_t reference,
213
    const uint64_t weight,
214
215
    const uint32_t maxNeighborCount,
    const uint32_t insertIndex)
216
217
218
219
220
221
222
223
224
  {
    bool sort = false;
    assert(
      maxNeighborCount > 0
      && maxNeighborCount <= kAttributePredictionMaxNeighbourCount);
    if (neighborCount < maxNeighborCount) {
      PCCNeighborInfo& neighborInfo = neighbors[neighborCount];
      neighborInfo.weight = weight;
      neighborInfo.predictorIndex = reference;
225
      neighborInfo.insertIndex = insertIndex;
226
227
228
229
      ++neighborCount;
      sort = true;
    } else {
      PCCNeighborInfo& neighborInfo = neighbors[maxNeighborCount - 1];
230
231
232
233
      if (
        weight < neighborInfo.weight
        || (weight == neighborInfo.weight
            && insertIndex < neighborInfo.insertIndex)) {
234
235
        neighborInfo.weight = weight;
        neighborInfo.predictorIndex = reference;
236
        neighborInfo.insertIndex = insertIndex;
237
238
239
240
241
242
243
244
245
246
        sort = true;
      }
    }
    for (int32_t k = neighborCount - 1; k > 0 && sort; --k) {
      if (neighbors[k] < neighbors[k - 1])
        std::swap(neighbors[k], neighbors[k - 1]);
      else
        return;
    }
  }
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
247
};
248
249
250

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

251
252
253
254
255
256
257
inline void
PCCLiftPredict(
  const std::vector<PCCPredictor>& predictors,
  const size_t startIndex,
  const size_t endIndex,
  const bool direct,
  std::vector<Vec3<int64_t>>& attributes)
258
{
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  const size_t predictorCount = endIndex - startIndex;
  for (size_t index = 0; index < predictorCount; ++index) {
    const size_t predictorIndex = predictorCount - index - 1 + startIndex;
    const auto& predictor = predictors[predictorIndex];
    auto& attribute = attributes[predictorIndex];
    Vec3<int64_t> predicted(int64_t(0));
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
      const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
      const uint32_t weight = predictor.neighbors[i].weight;
      assert(neighborPredIndex < startIndex);
      predicted += weight * attributes[neighborPredIndex];
    }
    predicted = divExp2RoundHalfInf(predicted, kFixedPointWeightShift);
    if (direct) {
      attribute -= predicted;
    } else {
      attribute += predicted;
    }
  }
278
279
}

280
inline void
281
282
283
284
285
PCCLiftPredict(
  const std::vector<PCCPredictor>& predictors,
  const size_t startIndex,
  const size_t endIndex,
  const bool direct,
286
  std::vector<int64_t>& attributes)
287
288
289
290
291
{
  const size_t predictorCount = endIndex - startIndex;
  for (size_t index = 0; index < predictorCount; ++index) {
    const size_t predictorIndex = predictorCount - index - 1 + startIndex;
    const auto& predictor = predictors[predictorIndex];
292
293
    auto& attribute = attributes[predictorIndex];
    int64_t predicted(int64_t(0));
294
295
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
      const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
296
      const uint32_t weight = predictor.neighbors[i].weight;
297
298
299
      assert(neighborPredIndex < startIndex);
      predicted += weight * attributes[neighborPredIndex];
    }
300
    predicted = divExp2RoundHalfInf(predicted, kFixedPointWeightShift);
301
    if (direct) {
302
      attribute -= predicted;
303
    } else {
304
      attribute += predicted;
305
306
307
308
309
310
    }
  }
}

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

311
inline void
312
313
PCCLiftUpdate(
  const std::vector<PCCPredictor>& predictors,
314
  const std::vector<uint64_t>& quantizationWeights,
315
316
317
  const size_t startIndex,
  const size_t endIndex,
  const bool direct,
318
  std::vector<Vec3<int64_t>>& attributes)
319
{
320
321
322
  std::vector<uint64_t> updateWeights;
  updateWeights.resize(startIndex, uint64_t(0));
  std::vector<Vec3<int64_t>> updates;
323
324
  updates.resize(startIndex);
  for (size_t index = 0; index < startIndex; ++index) {
325
    updates[index] = int64_t(0);
326
327
328
329
330
  }
  const size_t predictorCount = endIndex - startIndex;
  for (size_t index = 0; index < predictorCount; ++index) {
    const size_t predictorIndex = predictorCount - index - 1 + startIndex;
    const auto& predictor = predictors[predictorIndex];
331
    const auto currentQuantWeight = quantizationWeights[predictorIndex];
332
333
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
      const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
334
335
      const uint64_t weight =
        predictor.neighbors[i].weight * currentQuantWeight;
336
337
338
339
340
341
342
      assert(neighborPredIndex < startIndex);
      updateWeights[neighborPredIndex] += weight;
      updates[neighborPredIndex] += weight * attributes[predictorIndex];
    }
  }
  for (size_t predictorIndex = 0; predictorIndex < startIndex;
       ++predictorIndex) {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
    const uint32_t sumWeights = updateWeights[predictorIndex];
    if (sumWeights) {
      auto& update = updates[predictorIndex];
      update = (update + sumWeights / 2) / sumWeights;
      auto& attribute = attributes[predictorIndex];
      if (direct) {
        attribute += update;
      } else {
        attribute -= update;
      }
    }
  }
}

inline void
PCCLiftUpdate(
  const std::vector<PCCPredictor>& predictors,
  const std::vector<uint64_t>& quantizationWeights,
  const size_t startIndex,
  const size_t endIndex,
  const bool direct,
  std::vector<int64_t>& attributes)
{
  std::vector<uint64_t> updateWeights;
  updateWeights.resize(startIndex, uint64_t(0));
  std::vector<int64_t> updates;
  updates.resize(startIndex);
  for (size_t index = 0; index < startIndex; ++index) {
    updates[index] = int64_t(0);
  }
  const size_t predictorCount = endIndex - startIndex;
  for (size_t index = 0; index < predictorCount; ++index) {
    const size_t predictorIndex = predictorCount - index - 1 + startIndex;
    const auto& predictor = predictors[predictorIndex];
    const auto currentQuantWeight = quantizationWeights[predictorIndex];
    for (size_t i = 0; i < predictor.neighborCount; ++i) {
      const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
      const uint64_t weight =
        predictor.neighbors[i].weight * currentQuantWeight;
      assert(neighborPredIndex < startIndex);
      updateWeights[neighborPredIndex] += weight;
      updates[neighborPredIndex] += weight * attributes[predictorIndex];
    }
  }
  for (size_t predictorIndex = 0; predictorIndex < startIndex;
       ++predictorIndex) {
    const uint32_t sumWeights = updateWeights[predictorIndex];
390
    if (sumWeights > 0.0) {
391
392
393
      auto& update = updates[predictorIndex];
      update = (update + sumWeights / 2) / sumWeights;
      auto& attribute = attributes[predictorIndex];
394
      if (direct) {
395
        attribute += update;
396
      } else {
397
        attribute -= update;
398
399
400
401
402
403
404
405
406
407
      }
    }
  }
}

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

inline void
PCCComputeQuantizationWeights(
  const std::vector<PCCPredictor>& predictors,
408
  std::vector<uint64_t>& quantizationWeights)
409
410
411
412
{
  const size_t pointCount = predictors.size();
  quantizationWeights.resize(pointCount);
  for (size_t i = 0; i < pointCount; ++i) {
413
    quantizationWeights[i] = (1 << kFixedPointWeightShift);
414
415
416
417
  }
  for (size_t i = 0; i < pointCount; ++i) {
    const size_t predictorIndex = pointCount - i - 1;
    const auto& predictor = predictors[predictorIndex];
418
419
420
421
422
423
424
    const auto currentQuantWeight = quantizationWeights[predictorIndex];
    for (size_t j = 0; j < predictor.neighborCount; ++j) {
      const size_t neighborPredIndex = predictor.neighbors[j].predictorIndex;
      const auto weight = predictor.neighbors[j].weight;
      auto& neighborQuantWeight = quantizationWeights[neighborPredIndex];
      neighborQuantWeight += divExp2RoundHalfInf(
        weight * currentQuantWeight, kFixedPointWeightShift);
425
426
    }
  }
427
428
429
  for (auto& w : quantizationWeights) {
    w = isqrt(w);
  }
430
431
432
433
}

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

434
435
inline uint32_t
FindNeighborWithinDistance(
436
  const PCCPointSet3& pointCloud,
437
  const std::vector<MortonCodeWithIndex>& packedVoxel,
438
439
  const int32_t index,
  const double radius2,
440
441
  const int32_t searchRange,
  std::vector<uint32_t>& retained)
442
{
443
444
445
446
447
448
449
450
451
452
453
454
455
  const auto& point = pointCloud[packedVoxel[index].index];
  const int32_t retainedSize = retained.size();
  int32_t j = retainedSize - 2;
  int32_t k = 0;
  while (j >= 0 && ++k < searchRange) {
    const int32_t index1 = retained[j];
    const int32_t pointIndex1 = packedVoxel[index1].index;
    const auto& point1 = pointCloud[pointIndex1];
    const auto d2 = (point1 - point).getNorm2();
    if (d2 <= radius2) {
      return index1;
    }
    --j;
456
  }
457
458
  return PCC_UNDEFINED_INDEX;
}
459

460
461
//---------------------------------------------------------------------------

462
463
464
465
466
467
468
469
inline int
indexTieBreaker(int a, int b)
{
  return a > b ? ((a - b) << 1) - 1 : ((b - a) << 1);
}

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

470
471
472
473
474
475
476
477
478
479
480
481
482
inline void
computeNearestNeighbors(
  const PCCPointSet3& pointCloud,
  const std::vector<MortonCodeWithIndex>& packedVoxel,
  const std::vector<uint32_t>& retained,
  const int32_t startIndex,
  const int32_t endIndex,
  const int32_t searchRange,
  const int32_t numberOfNearestNeighborsInPrediction,
  std::vector<uint32_t>& indexes,
  std::vector<PCCPredictor>& predictors,
  std::vector<uint32_t>& pointIndexToPredictorIndex,
  int32_t& predIndex,
483
484
  std::vector<Box3<double>>& bBoxes,
  const bool intraLodPredictionEnabled)
485
486
487
488
489
490
491
492
493
494
495
496
497
{
  const int32_t retainedSize = retained.size();
  const int32_t bucketSize = 8;
  bBoxes.resize((retainedSize + bucketSize - 1) / bucketSize);
  for (int32_t i = 0, b = 0; i < retainedSize; ++b) {
    auto& bBox = bBoxes[b];
    bBox.min = bBox.max = pointCloud[packedVoxel[retained[i++]].index];
    for (int32_t k = 1; k < bucketSize && i < retainedSize; ++k, ++i) {
      const int32_t pointIndex = packedVoxel[retained[i]].index;
      const auto& point = pointCloud[pointIndex];
      for (int32_t p = 0; p < 3; ++p) {
        bBox.min[p] = std::min(bBox.min[p], point[p]);
        bBox.max[p] = std::max(bBox.max[p], point[p]);
498
499
500
      }
    }
  }
501

502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
  std::vector<Box3<double>> bBoxesI;
  const int32_t indexesSize = endIndex - startIndex;
  if (intraLodPredictionEnabled) {
    bBoxesI.resize((indexesSize + bucketSize - 1) / bucketSize);
    for (int32_t i = startIndex, b = 0; i < endIndex; ++b) {
      auto& bBox = bBoxesI[b];
      bBox.min = bBox.max = pointCloud[packedVoxel[indexes[i++]].index];
      for (int32_t k = 1; k < bucketSize && i < endIndex; ++k, ++i) {
        const int32_t pointIndex = packedVoxel[indexes[i]].index;
        const auto& point = pointCloud[pointIndex];
        for (int32_t p = 0; p < 3; ++p) {
          bBox.min[p] = std::min(bBox.min[p], point[p]);
          bBox.max[p] = std::max(bBox.max[p], point[p]);
        }
      }
    }
  }

520
521
522
523
  const int32_t index0 = numberOfNearestNeighborsInPrediction - 1;

  for (int32_t i = startIndex, j = 0; i < endIndex; ++i) {
    const int32_t index = indexes[i];
524
    const int64_t mortonCode = packedVoxel[index].mortonCode;
525
526
527
    const int32_t pointIndex = packedVoxel[index].index;
    const auto& point = pointCloud[pointIndex];
    indexes[i] = pointIndex;
528
    while (j < retainedSize - 1
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
           && mortonCode >= packedVoxel[retained[j]].mortonCode)
      ++j;
    auto& predictor = predictors[--predIndex];
    pointIndexToPredictorIndex[pointIndex] = predIndex;

    predictor.init();

    const int32_t j0 = std::max(0, j - searchRange);
    const int32_t j1 = std::min(retainedSize, j + searchRange + 1);

    const int32_t bucketIndex0 = j / bucketSize;
    int32_t k0 = std::max(bucketIndex0 * bucketSize, j0);
    int32_t k1 = std::min((bucketIndex0 + 1) * bucketSize, j1);

    for (int32_t k = k0; k < k1; ++k) {
      const int32_t pointIndex1 = packedVoxel[retained[k]].index;
      const auto& point1 = pointCloud[pointIndex1];
      predictor.insertNeighbor(
        pointIndex1, (point - point1).getNorm2(),
548
        numberOfNearestNeighborsInPrediction, indexTieBreaker(k, j));
549
550
551
552
553
554
555
556
557
558
559
560
    }

    for (int32_t s0 = 1, sr = (1 + searchRange / bucketSize); s0 < sr; ++s0) {
      for (int32_t s1 = 0; s1 < 2; ++s1) {
        const int32_t bucketIndex1 =
          s1 == 0 ? bucketIndex0 + s0 : bucketIndex0 - s0;
        if (bucketIndex1 < 0 || bucketIndex1 >= bBoxes.size()) {
          continue;
        }
        if (
          predictor.neighborCount < numberOfNearestNeighborsInPrediction
          || bBoxes[bucketIndex1].getDist2(point)
561
            <= predictor.neighbors[index0].weight) {
562
563
564
565
566
567
568
          const int32_t k0 = std::max(bucketIndex1 * bucketSize, j0);
          const int32_t k1 = std::min((bucketIndex1 + 1) * bucketSize, j1);
          for (int32_t k = k0; k < k1; ++k) {
            const int32_t pointIndex1 = packedVoxel[retained[k]].index;
            const auto& point1 = pointCloud[pointIndex1];
            predictor.insertNeighbor(
              pointIndex1, (point - point1).getNorm2(),
569
              numberOfNearestNeighborsInPrediction, indexTieBreaker(k, j));
570
571
572
573
          }
        }
      }
    }
574
575
576
577
578
579
580
581
582
583
584
585
586

    if (intraLodPredictionEnabled) {
      const int32_t i0 = i - startIndex;
      const int32_t j1 = std::min(indexesSize, i0 + searchRange + 1);
      const int32_t bucketIndex0 = i0 / bucketSize;
      int32_t k0 = i0 + 1;
      int32_t k1 = std::min((bucketIndex0 + 1) * bucketSize, j1);

      for (int32_t k = k0; k < k1; ++k) {
        const int32_t pointIndex1 = packedVoxel[indexes[startIndex + k]].index;
        const auto& point1 = pointCloud[pointIndex1];
        predictor.insertNeighbor(
          pointIndex1, (point - point1).getNorm2(),
587
          numberOfNearestNeighborsInPrediction,
588
          startIndex + k - i + 2 * searchRange);
589
590
591
592
593
594
595
596
597
598
599
      }

      for (int32_t s0 = 1, sr = (1 + searchRange / bucketSize); s0 < sr;
           ++s0) {
        const int32_t bucketIndex1 = bucketIndex0 + s0;
        if (bucketIndex1 >= bBoxesI.size())
          continue;

        if (
          predictor.neighborCount < numberOfNearestNeighborsInPrediction
          || bBoxesI[bucketIndex1].getDist2(point)
600
            <= predictor.neighbors[index0].weight) {
601
602
603
604
605
606
607
608
          const int32_t k0 = bucketIndex1 * bucketSize;
          const int32_t k1 = std::min((bucketIndex1 + 1) * bucketSize, j1);
          for (int32_t k = k0; k < k1; ++k) {
            const int32_t pointIndex1 =
              packedVoxel[indexes[startIndex + k]].index;
            const auto& point1 = pointCloud[pointIndex1];
            predictor.insertNeighbor(
              pointIndex1, (point - point1).getNorm2(),
609
              numberOfNearestNeighborsInPrediction,
610
              startIndex + k - i + 2 * searchRange);
611
612
613
614
          }
        }
      }
    }
615
    assert(predictor.neighborCount <= numberOfNearestNeighborsInPrediction);
616
617
618
619
620
621
  }
}

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

inline void
622
subsampleByDistance(
623
  const PCCPointSet3& pointCloud,
624
625
626
627
628
  const std::vector<MortonCodeWithIndex>& packedVoxel,
  const std::vector<uint32_t>& input,
  const double radius2,
  const int32_t searchRange,
  std::vector<uint32_t>& retained,
629
630
  std::vector<uint32_t>& indexes)
{
631
632
633
634
635
636
637
  if (input.size() == 1) {
    indexes.push_back(input[0]);
  } else {
    for (const auto index : input) {
      if (retained.empty()) {
        retained.push_back(index);
        continue;
638
      }
639
640
641
642
643
644
645
646
647
648
      const auto& point = pointCloud[packedVoxel[index].index];
      if (
        (pointCloud[packedVoxel[retained.back()].index] - point).getNorm2()
          <= radius2
        || FindNeighborWithinDistance(
             pointCloud, packedVoxel, index, radius2, searchRange, retained)
          != PCC_UNDEFINED_INDEX) {
        indexes.push_back(index);
      } else {
        retained.push_back(index);
649
650
651
652
653
      }
    }
  }
}

654
//---------------------------------------------------------------------------
655

656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
inline void
subsampleByDecimation(
  const std::vector<uint32_t>& input,
  std::vector<uint32_t>& retained,
  std::vector<uint32_t>& indexes)
{
  static const int kLodUniformQuant = 4;
  const int indexCount = int(input.size());
  for (int i = 0; i < indexCount; ++i) {
    if (i % kLodUniformQuant == 0)
      retained.push_back(input[i]);
    else
      indexes.push_back(input[i]);
  }
}

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

inline void
subsample(
  bool useDecimation,
  const PCCPointSet3& pointCloud,
  const std::vector<MortonCodeWithIndex>& packedVoxel,
  const std::vector<uint32_t>& input,
  const double radius2,
  const int32_t searchRange,
  std::vector<uint32_t>& retained,
  std::vector<uint32_t>& indexes)
{
  if (useDecimation)
    subsampleByDecimation(input, retained, indexes);
  else
    subsampleByDistance(
      pointCloud, packedVoxel, input, radius2, searchRange, retained, indexes);
}

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

694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
inline void
computeMortonCodes(
  const PCCPointSet3& pointCloud,
  std::vector<MortonCodeWithIndex>& packedVoxel)
{
  const int32_t pointCount = int32_t(pointCloud.getPointCount());
  packedVoxel.resize(pointCount);
  for (int n = 0; n < pointCount; n++) {
    const auto& position = pointCloud[n];
    packedVoxel[n].mortonCode = mortonAddr(
      int32_t(position[0]), int32_t(position[1]), int32_t(position[2]));
    packedVoxel[n].index = n;
  }
  sort(packedVoxel.begin(), packedVoxel.end());
}
709
710
711

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

712
inline void
713
714
updatePredictors(
  const std::vector<uint32_t>& pointIndexToPredictorIndex,
715
716
  std::vector<PCCPredictor>& predictors)
{
717
718
  for (auto& predictor : predictors) {
    if (predictor.neighborCount < 2) {
719
720
      predictor.neighbors[0].weight = 1;
    } else if (predictor.neighbors[0].weight == 0) {
721
      predictor.neighborCount = 1;
722
      predictor.neighbors[0].weight = 1;
723
    }
724
725
726
727
728
    for (int32_t k = 0; k < predictor.neighborCount; ++k) {
      auto& neighbor = predictor.neighbors[k];
      neighbor.predictorIndex =
        pointIndexToPredictorIndex[neighbor.predictorIndex];
    }
729
730
731
  }
}

732
733
734
//---------------------------------------------------------------------------

inline void
735
buildPredictorsFast(
736
  const PCCPointSet3& pointCloud,
737
  bool lod_decimation_enabled_flag,
738
  bool intraLodPredictionEnabled,
739
740
741
742
743
744
745
746
  const std::vector<int64_t>& dist2,
  const int32_t levelOfDetailCount,
  const int32_t numberOfNearestNeighborsInPrediction,
  const int32_t searchRange1,
  const int32_t searchRange2,
  std::vector<PCCPredictor>& predictors,
  std::vector<uint32_t>& numberOfPointsPerLevelOfDetail,
  std::vector<uint32_t>& indexes)
747
{
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  const int32_t pointCount = int32_t(pointCloud.getPointCount());
  assert(pointCount);

  std::vector<MortonCodeWithIndex> packedVoxel;
  computeMortonCodes(pointCloud, packedVoxel);

  std::vector<uint32_t> retained, input, pointIndexToPredictorIndex;
  pointIndexToPredictorIndex.resize(pointCount);
  retained.reserve(pointCount);
  input.resize(pointCount);
  for (uint32_t i = 0; i < pointCount; ++i) {
    input[i] = i;
  }

  // prepare output buffers
763
  predictors.resize(pointCount);
764
765
766
767
768
  numberOfPointsPerLevelOfDetail.resize(0);
  indexes.resize(0);
  indexes.reserve(pointCount);
  numberOfPointsPerLevelOfDetail.reserve(21);
  numberOfPointsPerLevelOfDetail.push_back(pointCount);
769

770
  std::vector<Box3<double>> bBoxes;
771
772
773
774
775
776
777
778
  int32_t predIndex = int32_t(pointCount);
  for (uint32_t lodIndex = 0; !input.empty() && lodIndex <= levelOfDetailCount;
       ++lodIndex) {
    const int32_t startIndex = indexes.size();
    if (lodIndex == levelOfDetailCount) {
      for (const auto index : input) {
        indexes.push_back(index);
      }
779
    } else {
780
781
      const double radius2 = dist2[lodIndex];
      subsample(
782
783
        lod_decimation_enabled_flag, pointCloud, packedVoxel, input, radius2,
        searchRange1, retained, indexes);
784
    }
785
786
    const int32_t endIndex = indexes.size();

787
788
789
790
791
792
793
794
    computeNearestNeighbors(
      pointCloud, packedVoxel, retained, startIndex, endIndex, searchRange2,
      numberOfNearestNeighborsInPrediction, indexes, predictors,
      pointIndexToPredictorIndex, predIndex, bBoxes,
      intraLodPredictionEnabled);

    if (!retained.empty()) {
      numberOfPointsPerLevelOfDetail.push_back(retained.size());
795
    }
796
797
    input.resize(0);
    std::swap(retained, input);
798
  }
799
800
801
802
803
  std::reverse(indexes.begin(), indexes.end());
  updatePredictors(pointIndexToPredictorIndex, predictors);
  std::reverse(
    numberOfPointsPerLevelOfDetail.begin(),
    numberOfPointsPerLevelOfDetail.end());
804
805
}

806
807
//---------------------------------------------------------------------------

808
}  // namespace pcc
Khaled Mammou's avatar
TMC3v0  
Khaled Mammou committed
809
810

#endif /* PCCTMC3Common_h */