Commit 85474147 authored by Khaled Mammou's avatar Khaled Mammou Committed by David Flynn
Browse files

m42642/attr: adaptive distance-based prediction

Adaptively pick the number of neighbours considered during the
prediction based on analysis of the reconstructed attribute values of
the neighbours.

From m42642, this is performed by computing the maximum difference
between any two reconstructed attribute values of a points neighbours.
If the variability is higher than a threshold, then apply a
rate-distortion optimization procedure to choose between using either
a single neighbour or all neighbours. The index of the best prediction
strategy is arithmetically encoded.

This commit:
 - implements the m42642 method
 - moves buildPredictors into transform-specific attribute encoding/decoding
 - adds quantizationStepsChroma
 - renames quantizationSteps -> quantizationStepsLuma

df: fixed incorrect application of quantizationStepsLuma|Chroma
df: updated cat3 cfg to specify quantizationStepsChroma
df: fixed header encoding if quantizationStepsChroma missing
parent 817c61d1
......@@ -32,7 +32,8 @@ categories:
- numberOfNearestNeighborsInPrediction: 4
- levelOfDetailCount: 9
- dist2: 134217728 33554432 8388608 2097152 524288 131072 32768 8192 0
- quantizationSteps: 0 0 0 0 0 0 0 0 0
- quantizationStepsLuma: 0 0 0 0 0 0 0 0 0
- quantizationStepsChroma: 0 0 0 0 0 0 0 0 0
- attribute: color
overpass_q1mm:
......@@ -86,7 +87,7 @@ categories:
- numberOfNearestNeighborsInPrediction: 4
- levelOfDetailCount: 6
- dist2: 4194301 1048582 262149 65534 16383 0
- quantizationSteps: 0 0 0 0 0 0
- quantizationStepsLuma: 0 0 0 0 0 0
- attribute: reflectance
ford_02_q1mm:
......
......@@ -33,7 +33,14 @@ categories:
- numberOfNearestNeighborsInPrediction: 4
- levelOfDetailCount: 9
- dist2: 134217728 33554432 8388608 2097152 524288 131072 32768 8192 0
- quantizationSteps:
- quantizationStepsLuma:
r01: 1 2 4 8 16 32 64 128 128
r02: 1 2 4 8 16 32 64 64 66
r03: 1 2 4 8 16 32 32 32 34
r04: 1 2 4 8 8 8 16 16 16
r05: 1 2 4 4 4 4 4 8 8
r06: 0 1 1 1 2 2 2 2 2
- quantizationStepsChroma:
r01: 1 2 4 8 16 32 64 128 128
r02: 1 2 4 8 16 32 64 64 66
r03: 1 2 4 8 16 32 32 32 34
......@@ -45,7 +52,14 @@ categories:
tollbooth_q1mm:
encflags:
- *commonAttr
- quantizationSteps:
- quantizationStepsLuma:
r01: 1 2 4 8 16 32 64 128 128
r02: 1 2 4 8 16 32 32 64 64
r03: 1 2 4 8 16 16 16 32 32
r04: 1 2 4 8 8 8 8 10 16
r05: 1 2 4 4 4 4 4 4 6
r06: 0 0 0 0 0 0 1 1 2
- quantizationStepsChroma:
r01: 1 2 4 8 16 32 64 128 128
r02: 1 2 4 8 16 32 32 64 64
r03: 1 2 4 8 16 16 16 32 32
......@@ -78,7 +92,7 @@ categories:
citytunnel_q1mm:
encflags:
- *commonAttr
- quantizationSteps:
- quantizationStepsLuma:
r01: 286 571 1143 2286 4571 9143 18286 36571 65536
r02: 77 154 308 615 1231 2462 4923 9846 19692
r03: 29 57 114 229 457 914 1829 3657 7314
......@@ -90,7 +104,7 @@ categories:
tollbooth_q1mm:
encflags:
- *commonAttr
- quantizationSteps:
- quantizationStepsLuma:
r01: 286 571 1143 2286 4571 9143 18286 36571 65536
r02: 100 200 400 800 1600 3200 6400 12800 25600
r03: 50 100 200 400 800 1600 3200 6400 12800
......
......@@ -33,7 +33,13 @@ categories:
- numberOfNearestNeighborsInPrediction: 4
- levelOfDetailCount: 9
- dist2: 134217728 33554432 8388608 2097152 524288 131072 32768 8192 0
- quantizationSteps:
- quantizationStepsLuma:
r01: 2 2 2 2 2 2 2 2 2
r02: 4 4 4 4 4 4 4 4 4
r03: 8 8 8 8 8 8 8 8 8
r04: 16 16 16 16 16 16 16 16 16
r05: 32 32 32 32 32 32 32 32 32
- quantizationStepsChroma:
r01: 2 2 2 2 2 2 2 2 2
r02: 4 4 4 4 4 4 4 4 4
r03: 8 8 8 8 8 8 8 8 8
......@@ -93,7 +99,7 @@ categories:
- numberOfNearestNeighborsInPrediction: 4
- levelOfDetailCount: 6
- dist2: 4194301 1048582 262149 65534 16383 0
- quantizationSteps:
- quantizationStepsLuma:
r01: 2 2 2 2 2 2
r02: 4 4 4 4 4 4
r03: 8 8 8 8 8 8
......
......@@ -40,7 +40,7 @@ categories:
r04: 508 127 32 8 2 0
r05: 10486 2621 655 164 41 0
r06: 317194 79299 19825 4956 1239 0
- quantizationSteps:
- quantizationStepsLuma:
r01: 2 4
r02: 1 2 4 4
r03: 0 1 2 4 4
......
......@@ -45,15 +45,18 @@ struct PCCResidualsDecoder {
uint32_t alphabetSize;
o3dgc::Arithmetic_Codec arithmeticDecoder;
o3dgc::Static_Bit_Model binaryModel0;
o3dgc::Adaptive_Bit_Model binaryModelPred;
o3dgc::Adaptive_Bit_Model binaryModelDiff0;
o3dgc::Adaptive_Data_Model multiSymbolModelDiff0;
o3dgc::Adaptive_Bit_Model binaryModelDiff1;
o3dgc::Adaptive_Data_Model multiSymbolModelDiff1;
PCCResidualsDecoder() { alphabetSize = 0; }
PCCResidualsDecoder() { alphabetSize = PCCTMC3SymbolCount; }
void start(PCCBitstream& bitstream, const uint32_t alphabetSize = 64);
void start(
PCCBitstream& bitstream, const uint32_t alphabetSize = PCCTMC3SymbolCount);
void stop();
bool decodePred();
uint32_t decode0();
uint32_t decode1();
};
......@@ -66,9 +69,7 @@ PCCResidualsDecoder::start(
{
this->alphabetSize = alphabetSize;
multiSymbolModelDiff0.set_alphabet(alphabetSize + 1);
binaryModelDiff0.reset();
multiSymbolModelDiff1.set_alphabet(alphabetSize + 1);
binaryModelDiff1.reset();
arithmeticDecoder.set_buffer(
static_cast<uint32_t>(bitstream.capacity - bitstream.size),
bitstream.buffer + bitstream.size);
......@@ -85,6 +86,14 @@ PCCResidualsDecoder::stop()
//----------------------------------------------------------------------------
bool
PCCResidualsDecoder::decodePred()
{
return arithmeticDecoder.decode(binaryModelPred);
}
//----------------------------------------------------------------------------
uint32_t
PCCResidualsDecoder::decode0()
{
......@@ -112,22 +121,6 @@ PCCResidualsDecoder::decode1()
//============================================================================
// AttributeDecoder Members
void
AttributeDecoder::buildPredictors(const PCCPointSet3& pointCloud)
{
std::vector<uint32_t> numberOfPointsPerLOD;
std::vector<uint32_t> indexes;
PCCBuildPredictors(
pointCloud, numberOfNearestNeighborsInPrediction, levelOfDetailCount,
dist2, predictors, numberOfPointsPerLOD, indexes);
for (auto& predictor : predictors) {
predictor.computeWeights(numberOfNearestNeighborsInPrediction);
}
}
//----------------------------------------------------------------------------
void
AttributeDecoder::decodeHeader(
const std::string& attributeName, PCCBitstream& bitstream)
......@@ -146,18 +139,27 @@ AttributeDecoder::decodeHeader(
PCCReadFromBuffer<uint8_t>(bitstream.buffer, lodCount, bitstream.size);
levelOfDetailCount = lodCount;
dist2.resize(levelOfDetailCount);
for (size_t lodIndex = 0; lodIndex < levelOfDetailCount; ++lodIndex) {
assert(levelOfDetailCount);
dist2.resize(levelOfDetailCount, 0);
for (size_t lodIndex = 0; lodIndex < (levelOfDetailCount - 1);
++lodIndex) {
uint32_t d2 = 0;
PCCReadFromBuffer<uint32_t>(bitstream.buffer, d2, bitstream.size);
dist2[lodIndex] = d2;
}
quantizationSteps.resize(levelOfDetailCount);
quantizationStepsLuma.resize(levelOfDetailCount);
for (size_t lodIndex = 0; lodIndex < levelOfDetailCount; ++lodIndex) {
uint32_t qs = 0;
PCCReadFromBuffer<uint32_t>(bitstream.buffer, qs, bitstream.size);
quantizationSteps[lodIndex] = qs;
quantizationStepsLuma[lodIndex] = qs;
}
quantizationStepsChroma.resize(levelOfDetailCount);
for (size_t lodIndex = 0; lodIndex < levelOfDetailCount; ++lodIndex) {
uint32_t qs = 0;
PCCReadFromBuffer<uint32_t>(bitstream.buffer, qs, bitstream.size);
quantizationStepsChroma[lodIndex] = qs;
}
}
......@@ -224,25 +226,113 @@ AttributeDecoder::decodeColors(
//----------------------------------------------------------------------------
void
computeReflectancePredictionWeights(
const PCCPointSet3& pointCloud,
const size_t numberOfNearestNeighborsInPrediction,
const int64_t threshold,
const int64_t qs,
PCCPredictor& predictor,
PCCResidualsDecoder& decoder)
{
predictor.computeWeights();
if (predictor.neighborCount > 1) {
int64_t minValue = 0;
int64_t maxValue = 0;
for (size_t i = 0; i < numberOfNearestNeighborsInPrediction; ++i) {
const uint16_t reflectanceNeighbor =
pointCloud.getReflectance(predictor.neighbors[i].index);
if (i == 0 || reflectanceNeighbor < minValue) {
minValue = reflectanceNeighbor;
}
if (i == 0 || reflectanceNeighbor > maxValue) {
maxValue = reflectanceNeighbor;
}
}
const int64_t maxDiff = maxValue - minValue;
if (maxDiff > threshold) {
const bool predIndex = decoder.decodePred();
if (predIndex == 0) {
predictor.neighborCount = 1;
predictor.neighbors[0].weight = 1.0;
}
}
}
}
//----------------------------------------------------------------------------
void
AttributeDecoder::decodeReflectancesIntegerLift(
PCCResidualsDecoder& decoder, PCCPointSet3& pointCloud)
{
const size_t pointCount = predictors.size();
std::vector<PCCPredictor> predictors;
std::vector<uint32_t> numberOfPointsPerLOD;
std::vector<uint32_t> indexesLOD;
PCCBuildLevelOfDetail2(
pointCloud, levelOfDetailCount, dist2, numberOfPointsPerLOD, indexesLOD);
PCCComputePredictors2(
pointCloud, numberOfPointsPerLOD, indexesLOD,
numberOfNearestNeighborsInPrediction, predictors);
const size_t pointCount = pointCloud.getPointCount();
const int64_t threshold = 16384;
const int64_t maxReflectance = std::numeric_limits<uint16_t>::max();
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
auto& predictor = predictors[predictorIndex];
uint16_t& reflectance = pointCloud.getReflectance(predictor.index);
const size_t lodIndex = predictor.levelOfDetailIndex;
const int64_t qs = quantizationSteps[lodIndex];
const int64_t qs = quantizationStepsLuma[lodIndex];
computeReflectancePredictionWeights(
pointCloud, numberOfNearestNeighborsInPrediction, threshold, qs,
predictor, decoder);
uint16_t& reflectance = pointCloud.getReflectance(predictor.index);
const uint32_t attValue0 = decoder.decode0();
const int64_t quantPredAttValue = predictor.predictReflectance(pointCloud);
const int64_t delta =
PCCInverseQuantization(o3dgc::UIntToInt(attValue0), qs);
const int64_t reconstructedQuantAttValue = quantPredAttValue + delta;
reflectance = uint16_t(PCCClip(
reconstructedQuantAttValue, int64_t(0),
int64_t(std::numeric_limits<uint16_t>::max())));
reflectance = uint16_t(
PCCClip(reconstructedQuantAttValue, int64_t(0), maxReflectance));
}
}
//----------------------------------------------------------------------------
void
AttributeDecoder::computeColorPredictionWeights(
const PCCPointSet3& pointCloud,
const size_t numberOfNearestNeighborsInPrediction,
const int64_t threshold,
PCCPredictor& predictor,
PCCResidualsDecoder& decoder)
{
predictor.computeWeights();
if (predictor.neighborCount > 1) {
int64_t minValue[3] = {0, 0, 0};
int64_t maxValue[3] = {0, 0, 0};
for (size_t i = 0; i < numberOfNearestNeighborsInPrediction; ++i) {
const PCCColor3B colorNeighbor =
pointCloud.getColor(predictor.neighbors[i].index);
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]));
if (maxDiff > threshold) {
const bool predIndex = decoder.decodePred();
if (predIndex == 0) {
predictor.neighborCount = 1;
predictor.neighbors[0].weight = 1.0;
}
}
}
}
......@@ -252,27 +342,41 @@ void
AttributeDecoder::decodeColorsIntegerLift(
PCCResidualsDecoder& decoder, PCCPointSet3& pointCloud)
{
const size_t pointCount = predictors.size();
std::vector<PCCPredictor> predictors;
std::vector<uint32_t> numberOfPointsPerLOD;
std::vector<uint32_t> indexesLOD;
PCCBuildLevelOfDetail2(
pointCloud, levelOfDetailCount, dist2, numberOfPointsPerLOD, indexesLOD);
PCCComputePredictors2(
pointCloud, numberOfPointsPerLOD, indexesLOD,
numberOfNearestNeighborsInPrediction, predictors);
const int64_t threshold = 64;
const size_t pointCount = pointCloud.getPointCount();
uint32_t values[3];
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
auto& predictor = predictors[predictorIndex];
const size_t lodIndex = predictor.levelOfDetailIndex;
const int64_t qs = quantizationStepsLuma[lodIndex];
const int64_t qs2 = quantizationStepsChroma[lodIndex];
computeColorPredictionWeights(
pointCloud, numberOfNearestNeighborsInPrediction, threshold, predictor,
decoder);
values[0] = decoder.decode0();
values[1] = decoder.decode1();
values[2] = decoder.decode1();
PCCColor3B& color = pointCloud.getColor(predictor.index);
const PCCColor3B predictedColor = predictor.predictColor(pointCloud);
const size_t lodIndex = predictor.levelOfDetailIndex;
const int64_t qs = quantizationSteps[lodIndex];
const uint32_t attValue0 = decoder.decode0();
const int64_t quantPredAttValue = predictedColor[0];
const int64_t delta =
PCCInverseQuantization(o3dgc::UIntToInt(attValue0), qs);
PCCInverseQuantization(o3dgc::UIntToInt(values[0]), qs);
const int64_t reconstructedQuantAttValue = quantPredAttValue + delta;
color[0] =
uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), int64_t(255)));
for (size_t k = 1; k < 3; ++k) {
const uint32_t attValue = decoder.decode1();
const int64_t quantPredAttValue = predictedColor[k];
const int64_t delta =
PCCInverseQuantization(o3dgc::UIntToInt(attValue), qs);
PCCInverseQuantization(o3dgc::UIntToInt(values[k]), qs2);
const int64_t reconstructedQuantAttValue = quantPredAttValue + delta;
color[k] =
uint8_t(PCCClip(reconstructedQuantAttValue, int64_t(0), int64_t(255)));
......
......@@ -54,8 +54,6 @@ struct PCCResidualsDecoder;
class AttributeDecoder {
public:
void buildPredictors(const PCCPointSet3& pointCloud);
void decodeHeader(const std::string& attributeName, PCCBitstream& bitstream);
void decodeReflectances(PCCBitstream& bitstream, PCCPointSet3& pointCloud);
......@@ -77,16 +75,23 @@ protected:
void
decodeColorsRaht(PCCResidualsDecoder& decoder, PCCPointSet3& pointCloud);
static void computeColorPredictionWeights(
const PCCPointSet3& pointCloud,
const size_t numberOfNearestNeighborsInPrediction,
const int64_t threshold,
PCCPredictor& predictor,
PCCResidualsDecoder& decoder);
private:
std::vector<PCCPredictor> predictors;
std::vector<int64_t> quantizationSteps;
std::vector<int64_t> quantizationStepsLuma;
std::vector<int64_t> quantizationStepsChroma;
std::vector<size_t> dist2;
size_t numberOfNearestNeighborsInPrediction;
size_t levelOfDetailCount;
uint32_t quantizationStepRaht;
uint8_t depthRaht;
uint8_t binaryLevelThresholdRaht;
TransformType transformType;
uint32_t quantizationStepRaht = 0;
uint8_t depthRaht = 0;
uint8_t binaryLevelThresholdRaht = 0;
TransformType transformType = TransformType::kIntegerLift;
};
//============================================================================
......
......@@ -45,17 +45,20 @@ struct PCCResidualsEncoder {
uint32_t alphabetSize;
o3dgc::Arithmetic_Codec arithmeticEncoder;
o3dgc::Static_Bit_Model binaryModel0;
o3dgc::Adaptive_Bit_Model binaryModelPred;
o3dgc::Adaptive_Bit_Model binaryModelDiff0;
o3dgc::Adaptive_Data_Model multiSymbolModelDiff0;
o3dgc::Adaptive_Bit_Model binaryModelDiff1;
o3dgc::Adaptive_Data_Model multiSymbolModelDiff1;
PCCResidualsEncoder() { alphabetSize = 0; }
PCCResidualsEncoder() { alphabetSize = PCCTMC3SymbolCount; }
void start(PCCBitstream& bitstream, const uint32_t alphabetSize = 64);
void start(
PCCBitstream& bitstream, const uint32_t alphabetSize = PCCTMC3SymbolCount);
uint32_t stop();
inline void encode0(const uint32_t value);
inline void encode1(const uint32_t value);
void encode0(const uint32_t value);
void encode1(const uint32_t value);
void encodePred(const bool value);
};
//----------------------------------------------------------------------------
......@@ -66,9 +69,7 @@ PCCResidualsEncoder::start(
{
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),
bitstream.buffer + bitstream.size);
......@@ -111,33 +112,156 @@ PCCResidualsEncoder::encode1(const uint32_t value)
}
}
//----------------------------------------------------------------------------
void
PCCResidualsEncoder::encodePred(const bool value)
{
arithmeticEncoder.encode(value, binaryModelPred);
}
//============================================================================
// AttributeEncoder Members
// An encapsulation of the entropy coding methods used in attribute coding
struct PCCResidualsEntropyEstimator {
size_t freq0[PCCTMC3SymbolCount + 1];
size_t freq1[PCCTMC3SymbolCount + 1];
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);
};
//----------------------------------------------------------------------------
void
AttributeEncoder::buildPredictors(
const PCCAttributeEncodeParamaters& attributeParams,
const PCCPointSet3& pointCloud)
PCCResidualsEntropyEstimator::init()
{
// NB: predictors are only used by the TMC3 integer lifting scheme
if (attributeParams.transformType != TransformType::kIntegerLift)
return;
for (size_t i = 0; i <= PCCTMC3SymbolCount; ++i) {
freq0[i] = 1;
freq1[i] = 1;
}
symbolCount0 = PCCTMC3SymbolCount + 1;
symbolCount1 = PCCTMC3SymbolCount + 1;
isZero1Count = isZero0Count = symbolCount0 / 2;
}
std::vector<uint32_t> numberOfPointsPerLOD;
std::vector<uint32_t> indexes;
PCCBuildPredictors(
pointCloud, attributeParams.numberOfNearestNeighborsInPrediction,
attributeParams.levelOfDetailCount, attributeParams.dist2, predictors,
numberOfPointsPerLOD, indexes);
//----------------------------------------------------------------------------
for (auto& predictor : predictors) {
predictor.computeWeights(
attributeParams.numberOfNearestNeighborsInPrediction);
double
PCCResidualsEntropyEstimator::bitsDetail(
const uint32_t detail,
const size_t symbolCount,
const size_t* const freq) const
{
const uint32_t detailClipped =
std::min(detail, uint32_t(PCCTMC3SymbolCount));
const double pDetail =
PCCClip(double(freq[detailClipped]) / symbolCount, 0.001, 0.999);
double bits = -log2(pDetail);
if (detail >= PCCTMC3SymbolCount) {
const double x = double(detail) - double(PCCTMC3SymbolCount);
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);
}
return bits;
}
//----------------------------------------------------------------------------
void
PCCResidualsEntropyEstimator::update(const uint32_t value0)
{
const bool isZero0 = value0 == 0;
++symbolCount0;
if (!isZero0) {
++freq0[std::min(value0 - 1, uint32_t(64))];
} 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) {
++freq0[std::min(value0 - 1, uint32_t(64))];
} else {
++isZero0Count;
}