Commit 220acf28 authored by Xiang Zhang's avatar Xiang Zhang Committed by David Flynn
Browse files

geom/m50927: slice-based geometry quantization

This extends the geometry based quantisation scheme to:
 - add a per-slice QP offset, and
 - permit uniform quantisation of the whole slice without
   requiring signalling of an additional node QP offset.

Per node QP offset signalling is disabled by setting
positionQuantisationOctreeDepth=-1
parent 4d0d1e66
...@@ -222,13 +222,18 @@ position scaling due to `positionQuantizationScale`. ...@@ -222,13 +222,18 @@ position scaling due to `positionQuantizationScale`.
### `--positionBaseQp=INT-VALUE` ### `--positionBaseQp=INT-VALUE`
The quantisation parameter used to quantise geometry positions. The The quantisation parameter used to quantise geometry positions. The
effective QP may be varied according to `positionQuantisationOctreeDepth`. effective QP may be varied according to `positionSliceQpOffset` and
`positionQuantisationOctreeDepth`.
A QP equal to 4 results in a scale factor of 1. A QP equal to 4 results in a scale factor of 1.
### `--positionSliceQpOffset=INT-VALUE`
A per-slice offset to be applied to `positionBaseQp`.
### `--positionQuantisationOctreeDepth=INT-VALUE` ### `--positionQuantisationOctreeDepth=INT-VALUE`
The depth in the octree at which per-node QP offsets are signalled. The depth in the octree at which per-node QP offsets are signalled.
A non-normative encoder process determines the QP offset based upon A non-normative encoder process determines the QP offset based upon
the local density of the octree. the local density of the octree. A value of -1 disables signalling
of per-node QP offsets.
### `--bitwiseOccupancyCoding=0|1` ### `--bitwiseOccupancyCoding=0|1`
In octree geometry coding, there are both byte-wise and bit-wise tools to In octree geometry coding, there are both byte-wise and bit-wise tools to
......
...@@ -551,9 +551,13 @@ ParseParameters(int argc, char* argv[], Parameters& params) ...@@ -551,9 +551,13 @@ ParseParameters(int argc, char* argv[], Parameters& params)
params.encoder.gps.geom_base_qp, 4, params.encoder.gps.geom_base_qp, 4,
"Base QP used in position quantisation") "Base QP used in position quantisation")
("positionSliceQpOffset",
params.encoder.gbh.geom_slice_qp_offset, 0,
"Per-slice QP offset used in position quantisation")
("positionQuantisationOctreeDepth", ("positionQuantisationOctreeDepth",
params.encoder.gbh.geom_octree_qp_offset_depth, 3, params.encoder.gbh.geom_octree_qp_offset_depth, -1,
"Octree depth used for signalling position QP offsets\n") "Octree depth used for signalling position QP offsets (-1 => disabled)")
(po::Section("Attributes")) (po::Section("Attributes"))
......
...@@ -282,6 +282,10 @@ PCCTMC3Encoder3::fixupParameterSets(EncoderParams* params) ...@@ -282,6 +282,10 @@ PCCTMC3Encoder3::fixupParameterSets(EncoderParams* params)
params->gps.geom_box_log2_scale_present_flag = true; params->gps.geom_box_log2_scale_present_flag = true;
params->gps.gps_geom_box_log2_scale = 0; params->gps.gps_geom_box_log2_scale = 0;
// don't enable octree qp offset signalling if disabled
params->gbh.geom_octree_qp_offset_enabled_flag =
params->gbh.geom_octree_qp_offset_depth >= 0;
// fixup attribute parameters // fixup attribute parameters
for (auto it : params->attributeIdxMap) { for (auto it : params->attributeIdxMap) {
auto& attr_sps = params->sps.attributeSets[it.second]; auto& attr_sps = params->sps.attributeSets[it.second];
...@@ -463,7 +467,9 @@ PCCTMC3Encoder3::encodeGeometryBrick( ...@@ -463,7 +467,9 @@ PCCTMC3Encoder3::encodeGeometryBrick(
gbh.geom_box_log2_scale = 0; gbh.geom_box_log2_scale = 0;
gbh.geom_max_node_size_log2 = nodeSizeLog2; gbh.geom_max_node_size_log2 = nodeSizeLog2;
gbh.geom_num_points = int(pointCloud.getPointCount()); gbh.geom_num_points = int(pointCloud.getPointCount());
gbh.geom_octree_qp_offset_enabled_flag = _gps->geom_scaling_enabled_flag; gbh.geom_slice_qp_offset = params->gbh.geom_slice_qp_offset;
gbh.geom_octree_qp_offset_enabled_flag =
params->gbh.geom_octree_qp_offset_enabled_flag;
gbh.geom_octree_qp_offset_depth = params->gbh.geom_octree_qp_offset_depth; gbh.geom_octree_qp_offset_depth = params->gbh.geom_octree_qp_offset_depth;
write(*_sps, *_gps, gbh, buf); write(*_sps, *_gps, gbh, buf);
......
...@@ -472,9 +472,13 @@ decodeGeometryOctree( ...@@ -472,9 +472,13 @@ decodeGeometryOctree(
occupancyAtlas.clear(); occupancyAtlas.clear();
} }
int sliceQp = gps.geom_base_qp + gbh.geom_slice_qp_offset;
int numLvlsUntilQpOffset = -1; int numLvlsUntilQpOffset = -1;
if (gbh.geom_octree_qp_offset_enabled_flag) if (gbh.geom_octree_qp_offset_enabled_flag)
numLvlsUntilQpOffset = gbh.geom_octree_qp_offset_depth; numLvlsUntilQpOffset = gbh.geom_octree_qp_offset_depth;
else if (gps.geom_scaling_enabled_flag)
node00.qp = sliceQp;
for (; !fifo.empty(); fifo.pop_front()) { for (; !fifo.empty(); fifo.pop_front()) {
if (fifo.begin() == fifoCurrLvlEnd) { if (fifo.begin() == fifoCurrLvlEnd) {
...@@ -500,7 +504,7 @@ decodeGeometryOctree( ...@@ -500,7 +504,7 @@ decodeGeometryOctree(
PCCOctree3Node& node0 = fifo.front(); PCCOctree3Node& node0 = fifo.front();
if (numLvlsUntilQpOffset == 0) { if (numLvlsUntilQpOffset == 0) {
node0.qp = decoder.decodeQpOffset() + gps.geom_base_qp; node0.qp = decoder.decodeQpOffset() + sliceQp;
node0.pos_base = node0.pos; node0.pos_base = node0.pos;
node0.pos_quant = {0, 0, 0}; node0.pos_quant = {0, 0, 0};
} }
......
...@@ -561,15 +561,20 @@ encodeGeometryOctree( ...@@ -561,15 +561,20 @@ encodeGeometryOctree(
} }
Vec3<uint32_t> occupancyAtlasOrigin(0xffffffff); Vec3<uint32_t> occupancyAtlasOrigin(0xffffffff);
int numLvlsUntilQpOffset = -1; int numLvlsUntilQuantization = -1;
if (gbh.geom_octree_qp_offset_enabled_flag) if (gps.geom_scaling_enabled_flag) {
numLvlsUntilQpOffset = gbh.geom_octree_qp_offset_depth; numLvlsUntilQuantization = 0;
if (gbh.geom_octree_qp_offset_enabled_flag)
numLvlsUntilQuantization = gbh.geom_octree_qp_offset_depth;
}
int sliceQp = gps.geom_base_qp + gbh.geom_slice_qp_offset;
// applied to the root node, just set the node qp // applied to the root node, just set the node qp
if (numLvlsUntilQpOffset == 0) if (numLvlsUntilQuantization == 0)
node00.qp = gps.geom_base_qp; node00.qp = sliceQp;
if (gbh.geom_octree_qp_offset_enabled_flag) if (gps.geom_scaling_enabled_flag)
pointCloud.copyPositions(); pointCloud.copyPositions();
for (; !fifo.empty(); fifo.pop_front()) { for (; !fifo.empty(); fifo.pop_front()) {
...@@ -588,20 +593,21 @@ encodeGeometryOctree( ...@@ -588,20 +593,21 @@ encodeGeometryOctree(
// determing a per node QP at the appropriate level // determing a per node QP at the appropriate level
// NB: this has no effect here if geom_octree_qp_offset_depth=0 // NB: this has no effect here if geom_octree_qp_offset_depth=0
if (--numLvlsUntilQpOffset == 0) if (--numLvlsUntilQuantization == 0)
calculateNodeQps(gps.geom_base_qp, fifo.begin(), fifoCurrLvlEnd); calculateNodeQps(gps.geom_base_qp, fifo.begin(), fifoCurrLvlEnd);
} }
PCCOctree3Node& node0 = fifo.front(); PCCOctree3Node& node0 = fifo.front();
// encode delta qp for each octree block // encode delta qp for each octree block
if (numLvlsUntilQpOffset == 0) if (
encoder.encodeQpOffset(node0.qp - gps.geom_base_qp); numLvlsUntilQuantization == 0 && gbh.geom_octree_qp_offset_enabled_flag)
encoder.encodeQpOffset(node0.qp - sliceQp);
int shiftBits = (node0.qp - 4) / 6; int shiftBits = (node0.qp - 4) / 6;
int effectiveNodeSizeLog2 = nodeSizeLog2 - shiftBits; int effectiveNodeSizeLog2 = nodeSizeLog2 - shiftBits;
int effectiveChildSizeLog2 = effectiveNodeSizeLog2 - 1; int effectiveChildSizeLog2 = effectiveNodeSizeLog2 - 1;
if (numLvlsUntilQpOffset == 0) { if (numLvlsUntilQuantization == 0) {
geometryQuantization(pointCloud, node0, nodeSizeLog2); geometryQuantization(pointCloud, node0, nodeSizeLog2);
if (gps.geom_unique_points_flag) if (gps.geom_unique_points_flag)
checkDuplicatePoints(pointCloud, node0, pointIdxToDmIdx); checkDuplicatePoints(pointCloud, node0, pointIdxToDmIdx);
...@@ -784,7 +790,7 @@ encodeGeometryOctree( ...@@ -784,7 +790,7 @@ encodeGeometryOctree(
// inverse quantise the point cloud // inverse quantise the point cloud
// NB: this is done here to handle the partial tree coding case // NB: this is done here to handle the partial tree coding case
if (gbh.geom_octree_qp_offset_enabled_flag) if (gps.geom_scaling_enabled_flag)
for (int i = 0; i < pointCloud.getPointCount(); i++) for (int i = 0; i < pointCloud.getPointCount(); i++)
pointCloud[i] = pointCloud.getPositionReconstructed(i); pointCloud[i] = pointCloud.getPositionReconstructed(i);
......
...@@ -288,6 +288,9 @@ struct GeometryBrickHeader { ...@@ -288,6 +288,9 @@ struct GeometryBrickHeader {
int geom_max_node_size_log2; int geom_max_node_size_log2;
int geom_num_points; int geom_num_points;
// qp offset for geometry scaling (if enabled)
int geom_slice_qp_offset;
// enables signalling of qp offsets within the octree // enables signalling of qp offsets within the octree
bool geom_octree_qp_offset_enabled_flag; bool geom_octree_qp_offset_enabled_flag;
......
...@@ -406,6 +406,7 @@ write( ...@@ -406,6 +406,7 @@ write(
bs.writeUn(sps.log2_max_frame_idx, gbh.frame_idx); bs.writeUn(sps.log2_max_frame_idx, gbh.frame_idx);
if (gps.geom_scaling_enabled_flag) { if (gps.geom_scaling_enabled_flag) {
bs.writeSe(gbh.geom_slice_qp_offset);
bs.write(gbh.geom_octree_qp_offset_enabled_flag); bs.write(gbh.geom_octree_qp_offset_enabled_flag);
if (gbh.geom_octree_qp_offset_enabled_flag) { if (gbh.geom_octree_qp_offset_enabled_flag) {
bs.writeUe(gbh.geom_octree_qp_offset_depth); bs.writeUe(gbh.geom_octree_qp_offset_depth);
...@@ -450,6 +451,7 @@ parseGbh( ...@@ -450,6 +451,7 @@ parseGbh(
gbh.geom_octree_qp_offset_enabled_flag = false; gbh.geom_octree_qp_offset_enabled_flag = false;
if (gps.geom_scaling_enabled_flag) { if (gps.geom_scaling_enabled_flag) {
bs.readSe(&gbh.geom_slice_qp_offset);
bs.read(&gbh.geom_octree_qp_offset_enabled_flag); bs.read(&gbh.geom_octree_qp_offset_enabled_flag);
if (gbh.geom_octree_qp_offset_enabled_flag) if (gbh.geom_octree_qp_offset_enabled_flag)
bs.readUe(&gbh.geom_octree_qp_offset_depth); bs.readUe(&gbh.geom_octree_qp_offset_depth);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment