Commit d85098de authored by Valery Valentin's avatar Valery Valentin Committed by David Flynn
Browse files

attr/m44900: convert pred/lift to fixed-point

parent 1d95e5fa
......@@ -525,26 +525,28 @@ AttributeDecoder::decodeColorsLift(
++predictorIndex) {
predictors[predictorIndex].computeWeights();
}
std::vector<double> weights;
std::vector<uint64_t> weights;
PCCComputeQuantizationWeights(predictors, weights);
const size_t lodCount = numberOfPointsPerLOD.size();
std::vector<Vec3<double>> colors;
std::vector<Vec3<int64_t>> colors;
colors.resize(pointCount);
// decompress
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
uint32_t values[3];
decoder.decode(values);
const int64_t qs = aps.quant_step_size_luma;
const int64_t qs2 = aps.quant_step_size_chroma;
const double quantWeight = sqrt(weights[predictorIndex]);
const int64_t qs = aps.quant_step_size_luma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
const int64_t qs2 = aps.quant_step_size_chroma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
const int64_t quantWeight = weights[predictorIndex];
auto& color = colors[predictorIndex];
const int64_t delta = UIntToInt(values[0]);
const double reconstructedDelta = PCCInverseQuantization(delta, qs);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
color[0] = reconstructedDelta / quantWeight;
for (size_t d = 1; d < 3; ++d) {
const int64_t delta = UIntToInt(values[d]);
const double reconstructedDelta = PCCInverseQuantization(delta, qs2);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs2);
color[d] = reconstructedDelta / quantWeight;
}
}
......@@ -557,11 +559,13 @@ AttributeDecoder::decodeColorsLift(
PCCLiftPredict(predictors, startIndex, endIndex, false, colors);
}
const double clipMax = (1 << desc.attr_bitdepth) - 1;
const int64_t clipMax = (1 << desc.attr_bitdepth) - 1;
for (size_t f = 0; f < pointCount; ++f) {
const auto color0 =
divExp2RoundHalfInf(colors[f], kFixedPointAttributeShift);
Vec3<uint8_t> color;
for (size_t d = 0; d < 3; ++d) {
color[d] = uint8_t(PCCClip(std::round(colors[f][d]), 0.0, clipMax));
color[d] = uint8_t(PCCClip(color0[d], int64_t(0), clipMax));
}
pointCloud.setColor(indexesLOD[f], color);
}
......@@ -597,21 +601,22 @@ AttributeDecoder::decodeReflectancesLift(
++predictorIndex) {
predictors[predictorIndex].computeWeights();
}
std::vector<double> weights;
std::vector<uint64_t> weights;
PCCComputeQuantizationWeights(predictors, weights);
const size_t lodCount = numberOfPointsPerLOD.size();
std::vector<double> reflectances;
std::vector<int64_t> reflectances;
reflectances.resize(pointCount);
// decompress
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
const int64_t detail = decoder.decode();
const int64_t qs = aps.quant_step_size_luma;
const double quantWeight = sqrt(weights[predictorIndex]);
const int64_t qs = aps.quant_step_size_luma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
const int64_t quantWeight = weights[predictorIndex];
auto& reflectance = reflectances[predictorIndex];
const int64_t delta = UIntToInt(detail);
const double reconstructedDelta = PCCInverseQuantization(delta, qs);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
reflectance = reconstructedDelta / quantWeight;
}
......@@ -623,11 +628,12 @@ AttributeDecoder::decodeReflectancesLift(
predictors, weights, startIndex, endIndex, false, reflectances);
PCCLiftPredict(predictors, startIndex, endIndex, false, reflectances);
}
const double maxReflectance = (1 << desc.attr_bitdepth) - 1;
const int64_t maxReflectance = (1 << desc.attr_bitdepth) - 1;
for (size_t f = 0; f < pointCount; ++f) {
const auto refl =
divExp2RoundHalfInf(reflectances[f], kFixedPointAttributeShift);
pointCloud.setReflectance(
indexesLOD[f],
uint16_t(PCCClip(std::round(reflectances[f]), 0.0, maxReflectance)));
indexesLOD[f], uint16_t(PCCClip(refl, int64_t(0), maxReflectance)));
}
}
......
......@@ -43,8 +43,8 @@
#include "FixedPoint.h"
// todo(df): promote to per-attribute encoder parameter
static const float kAttrPredLambdaR = 0.01;
static const float kAttrPredLambdaC = 0.01;
static const double kAttrPredLambdaR = 0.01;
static const double kAttrPredLambdaC = 0.01;
namespace pcc {
//============================================================================
......@@ -367,7 +367,7 @@ AttributeEncoder::computeReflectancePredictionWeights(
int64_t minValue = 0;
int64_t maxValue = 0;
for (size_t i = 0; i < predictor.neighborCount; ++i) {
const uint16_t reflectanceNeighbor = pointCloud.getReflectance(
const uint64_t reflectanceNeighbor = pointCloud.getReflectance(
indexesLOD[predictor.neighbors[i].predictorIndex]);
if (i == 0 || reflectanceNeighbor < minValue) {
minValue = reflectanceNeighbor;
......@@ -379,16 +379,16 @@ AttributeEncoder::computeReflectancePredictionWeights(
const int64_t maxDiff = maxValue - minValue;
if (maxDiff > aps.adaptive_prediction_threshold) {
const int qs = aps.quant_step_size_luma;
uint16_t attrValue =
uint64_t attrValue =
pointCloud.getReflectance(indexesLOD[predictorIndex]);
// base case: weighted average of n neighbours
predictor.predMode = 0;
uint16_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
uint64_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
int64_t attrResidualQuant =
computeReflectanceResidual(attrValue, attrPred, qs);
double best_score = attrResidualQuant + kAttrPredLambdaR * (double)qs;
double best_score = attrResidualQuant + kAttrPredLambdaR * qs;
for (int i = 0; i < predictor.neighborCount; i++) {
if (i == aps.max_num_direct_predictors)
......@@ -458,7 +458,7 @@ AttributeEncoder::encodeReflectancesPred(
aps, pointCloud, indexesLOD, predictorIndex, predictor, encoder,
context);
const uint32_t pointIndex = indexesLOD[predictorIndex];
const uint16_t reflectance = pointCloud.getReflectance(pointIndex);
const uint64_t reflectance = pointCloud.getReflectance(pointIndex);
const uint16_t predictedReflectance =
predictor.predictReflectance(pointCloud, indexesLOD);
const int64_t quantAttValue = reflectance;
......@@ -837,16 +837,16 @@ AttributeEncoder::encodeColorsLift(
++predictorIndex) {
predictors[predictorIndex].computeWeights();
}
std::vector<double> weights;
std::vector<uint64_t> weights;
PCCComputeQuantizationWeights(predictors, weights);
const size_t lodCount = numberOfPointsPerLOD.size();
std::vector<Vec3<double>> colors;
std::vector<Vec3<int64_t>> colors;
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) {
colors[index][d] = color[d];
colors[index][d] = int32_t(color[d]) << kFixedPointAttributeShift;
}
}
......@@ -859,16 +859,18 @@ AttributeEncoder::encodeColorsLift(
}
// compress
const int64_t qs = aps.quant_step_size_luma;
const size_t qs2 = aps.quant_step_size_chroma;
const int64_t qs = aps.quant_step_size_luma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
const size_t qs2 = aps.quant_step_size_chroma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
const double quantWeight = sqrt(weights[predictorIndex]);
const int64_t quantWeight = weights[predictorIndex];
auto& color = colors[predictorIndex];
const int64_t delta = PCCQuantization(color[0] * quantWeight, qs);
const int64_t detail = IntToUInt(delta);
assert(detail < std::numeric_limits<uint32_t>::max());
const double reconstructedDelta = PCCInverseQuantization(delta, qs);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
color[0] = reconstructedDelta / quantWeight;
uint32_t values[3];
values[0] = uint32_t(detail);
......@@ -876,7 +878,7 @@ AttributeEncoder::encodeColorsLift(
const int64_t delta = PCCQuantization(color[d] * quantWeight, qs2);
const int64_t detail = IntToUInt(delta);
assert(detail < std::numeric_limits<uint32_t>::max());
const double reconstructedDelta = PCCInverseQuantization(delta, qs2);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs2);
color[d] = reconstructedDelta / quantWeight;
values[d] = uint32_t(detail);
}
......@@ -891,11 +893,13 @@ AttributeEncoder::encodeColorsLift(
PCCLiftPredict(predictors, startIndex, endIndex, false, colors);
}
const double clipMax = (1 << desc.attr_bitdepth) - 1;
const int64_t clipMax = (1 << desc.attr_bitdepth) - 1;
for (size_t f = 0; f < pointCount; ++f) {
const auto color0 =
divExp2RoundHalfInf(colors[f], kFixedPointAttributeShift);
Vec3<uint8_t> color;
for (size_t d = 0; d < 3; ++d) {
color[d] = uint8_t(PCCClip(std::round(colors[f][d]), 0.0, clipMax));
color[d] = uint8_t(PCCClip(color0[d], 0.0, clipMax));
}
pointCloud.setColor(indexesLOD[f], color);
}
......@@ -931,15 +935,16 @@ AttributeEncoder::encodeReflectancesLift(
++predictorIndex) {
predictors[predictorIndex].computeWeights();
}
std::vector<double> weights;
std::vector<uint64_t> weights;
PCCComputeQuantizationWeights(predictors, weights);
const size_t lodCount = numberOfPointsPerLOD.size();
std::vector<double> reflectances;
std::vector<int64_t> reflectances;
reflectances.resize(pointCount);
for (size_t index = 0; index < pointCount; ++index) {
reflectances[index] = pointCloud.getReflectance(indexesLOD[index]);
reflectances[index] = int32_t(pointCloud.getReflectance(indexesLOD[index]))
<< kFixedPointAttributeShift;
}
for (size_t i = 0; (i + 1) < lodCount; ++i) {
......@@ -954,13 +959,14 @@ AttributeEncoder::encodeReflectancesLift(
// compress
for (size_t predictorIndex = 0; predictorIndex < pointCount;
++predictorIndex) {
const int64_t qs = aps.quant_step_size_luma;
const double quantWeight = sqrt(weights[predictorIndex]);
const int64_t qs = aps.quant_step_size_luma
<< (kFixedPointWeightShift / 2 + kFixedPointAttributeShift);
const int64_t quantWeight = weights[predictorIndex];
auto& reflectance = reflectances[predictorIndex];
const int64_t delta = PCCQuantization(reflectance * quantWeight, qs);
const int64_t detail = IntToUInt(delta);
assert(detail < std::numeric_limits<uint32_t>::max());
const double reconstructedDelta = PCCInverseQuantization(delta, qs);
const int64_t reconstructedDelta = PCCInverseQuantization(delta, qs);
reflectance = reconstructedDelta / quantWeight;
encoder.encode(detail);
}
......@@ -973,11 +979,12 @@ AttributeEncoder::encodeReflectancesLift(
predictors, weights, startIndex, endIndex, false, reflectances);
PCCLiftPredict(predictors, startIndex, endIndex, false, reflectances);
}
const double maxReflectance = (1 << desc.attr_bitdepth) - 1;
const int64_t maxReflectance = (1 << desc.attr_bitdepth) - 1;
for (size_t f = 0; f < pointCount; ++f) {
const int64_t refl =
divExp2RoundHalfInf(reflectances[f], kFixedPointAttributeShift);
pointCloud.setReflectance(
indexesLOD[f],
uint16_t(PCCClip(std::round(reflectances[f]), 0.0, maxReflectance)));
indexesLOD[f], uint16_t(PCCClip(refl, int64_t(0), maxReflectance)));
}
}
......
......@@ -387,6 +387,55 @@ mortonAddr(const Vec3<T>& vec, int depth)
//---------------------------------------------------------------------------
inline int64_t
PCCClip(const int64_t& n, const int64_t& lower, const int64_t& upper)
{
return std::max(lower, std::min(n, upper));
}
//---------------------------------------------------------------------------
// Integer division of @scalar by 2^shift, rounding intermediate half values
// away from zero.
inline int64_t
divExp2RoundHalfInf(int64_t scalar, int shift)
{
if (!shift)
return scalar;
int64_t s0 = 1 << (shift - 1);
return scalar >= 0 ? (s0 + scalar) >> shift : -((s0 - scalar) >> shift);
}
//---------------------------------------------------------------------------
// Integer division of @scalar by 2^shift, rounding intermediate half values
// away from zero.
inline uint64_t
divExp2RoundHalfInf(uint64_t scalar, int shift)
{
if (!shift)
return scalar;
return ((1 << (shift - 1)) + scalar) >> shift;
}
//---------------------------------------------------------------------------
// Component-wise integer division of @vec by 2^shift, rounding intermediate
// half values away from zero.
template<typename T>
inline Vec3<T>
divExp2RoundHalfInf(Vec3<T> vec, int shift)
{
for (int k = 0; k < 3; ++k)
vec[k] = divExp2RoundHalfInf(vec[k], shift);
return vec;
}
//---------------------------------------------------------------------------
} /* namespace pcc */
#endif /* PCCMath_h */
......@@ -86,7 +86,7 @@ struct MortonCodeWithIndex {
//---------------------------------------------------------------------------
struct PCCNeighborInfo {
double weight;
uint64_t weight;
uint32_t predictorIndex;
bool operator<(const PCCNeighborInfo& rhs) const
{
......@@ -105,7 +105,7 @@ struct PCCPredictor {
Vec3<uint8_t> predictColor(
const PCCPointSet3& pointCloud, const std::vector<uint32_t>& indexes) const
{
Vec3<double> predicted(0.0);
Vec3<int64_t> predicted(0);
if (predMode > neighborCount) {
/* nop */
} else if (predMode > 0) {
......@@ -118,21 +118,23 @@ struct PCCPredictor {
for (size_t i = 0; i < neighborCount; ++i) {
const Vec3<uint8_t> color =
pointCloud.getColor(indexes[neighbors[i].predictorIndex]);
const double w = neighbors[i].weight;
const uint32_t w = neighbors[i].weight;
for (size_t k = 0; k < 3; ++k) {
predicted[k] += w * color[k];
}
}
for (uint32_t k = 0; k < 3; ++k) {
predicted[k] =
divExp2RoundHalfInf(predicted[k], kFixedPointWeightShift);
}
return Vec3<uint8_t>(
uint8_t(std::round(predicted[0])), uint8_t(std::round(predicted[1])),
uint8_t(std::round(predicted[2])));
}
return Vec3<uint8_t>(predicted[0], predicted[1], predicted[2]);
}
uint16_t predictReflectance(
int64_t predictReflectance(
const PCCPointSet3& pointCloud, const std::vector<uint32_t>& indexes) const
{
double predicted(0.0);
int64_t predicted(0);
if (predMode > neighborCount) {
/* nop */
} else if (predMode > 0) {
......@@ -143,23 +145,54 @@ struct PCCPredictor {
predicted += neighbors[i].weight
* pointCloud.getReflectance(indexes[neighbors[i].predictorIndex]);
}
predicted = divExp2RoundHalfInf(predicted, kFixedPointWeightShift);
}
return uint16_t(std::round(predicted));
return predicted;
}
void computeWeights()
{
if (neighborCount <= 1) {
neighbors[0].weight = 1.0;
return;
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;
}
}
while (neighborCount > 1) {
if (
neighbors[neighborCount - 1].weight
>= (neighbors[neighborCount - 2].weight << kFixedPointWeightShift)) {
--neighborCount;
} else {
break;
}
double sum = 0.0;
for (uint32_t n = 0; n < neighborCount; ++n) {
sum += neighbors[n].weight;
}
assert(sum > 0.0);
for (uint32_t n = 0; n < neighborCount; ++n) {
neighbors[n].weight /= sum;
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);
}
}
......@@ -167,7 +200,7 @@ struct PCCPredictor {
{
neighborCount = (predictorIndex != PCC_UNDEFINED_INDEX) ? 1 : 0;
neighbors[0].predictorIndex = predictorIndex;
neighbors[0].weight = 1.0;
neighbors[0].weight = 1;
predMode = 0;
}
......@@ -175,7 +208,7 @@ struct PCCPredictor {
void insertNeighbor(
const uint32_t reference,
const double weight,
const uint64_t weight,
const uint32_t maxNeighborCount)
{
bool sort = false;
......@@ -226,71 +259,93 @@ PCCInverseQuantization(const int64_t value, const int64_t qs)
return qs == 0 ? value : (value * qs);
}
inline int64_t
PCCQuantization(const double value, const int64_t qs)
//---------------------------------------------------------------------------
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)
{
return int64_t(
value >= 0.0 ? std::floor(value / qs + 1.0 / 3.0)
: -std::floor(-value / qs + 1.0 / 3.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];
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;
}
}
}
//---------------------------------------------------------------------------
template<typename T>
void
inline void
PCCLiftPredict(
const std::vector<PCCPredictor>& predictors,
const size_t startIndex,
const size_t endIndex,
const bool direct,
std::vector<T>& attributes)
std::vector<int64_t>& attributes)
{
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];
T predicted(0.0);
auto& attribute = attributes[predictorIndex];
int64_t predicted(int64_t(0));
for (size_t i = 0; i < predictor.neighborCount; ++i) {
const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
const double weight = predictor.neighbors[i].weight;
const uint32_t weight = predictor.neighbors[i].weight;
assert(neighborPredIndex < startIndex);
predicted += weight * attributes[neighborPredIndex];
}
predicted = divExp2RoundHalfInf(predicted, kFixedPointWeightShift);
if (direct) {
attributes[predictorIndex] -= predicted;
attribute -= predicted;
} else {
attributes[predictorIndex] += predicted;
attribute += predicted;
}
}
}
//---------------------------------------------------------------------------
template<typename T>
void
inline void
PCCLiftUpdate(
const std::vector<PCCPredictor>& predictors,
const std::vector<double>& quantizationWeights,
const std::vector<uint64_t>& quantizationWeights,
const size_t startIndex,
const size_t endIndex,
const bool direct,
std::vector<T>& attributes)
std::vector<Vec3<int64_t>>& attributes)
{
std::vector<double> updateWeights;
updateWeights.resize(startIndex, 0.0);
std::vector<T> updates;
std::vector<uint64_t> updateWeights;
updateWeights.resize(startIndex, uint64_t(0));
std::vector<Vec3<int64_t>> updates;
updates.resize(startIndex);
for (size_t index = 0; index < startIndex; ++index) {
updates[index] = 0.0;
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 double currentQuantWeight = quantizationWeights[predictorIndex];
const auto currentQuantWeight = quantizationWeights[predictorIndex];
for (size_t i = 0; i < predictor.neighborCount; ++i) {
const size_t neighborPredIndex = predictor.neighbors[i].predictorIndex;
const double weight = predictor.neighbors[i].weight * currentQuantWeight;
const uint64_t weight =
predictor.neighbors[i].weight * currentQuantWeight;
assert(neighborPredIndex < startIndex);
updateWeights[neighborPredIndex] += weight;
updates[neighborPredIndex] += weight * attributes[predictorIndex];
......@@ -298,13 +353,61 @@ PCCLiftUpdate(
}
for (size_t predictorIndex = 0; predictorIndex < startIndex;
++predictorIndex) {
const double sumWeights = updateWeights[predictorIndex];
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);