Commit 2c74c097 authored by Sebastien Lasserre's avatar Sebastien Lasserre Committed by David Flynn
Browse files

geom/m48906: planar mode coding of octree occupancy

This commit introduces planar modes, in the three x,y, and directions,
as new representations of the geometry. Planar modes are activated at
eligible nodes through a planar mode activation flag coded in the
bitstream.  Extra syntax is added to the bitstream to indicate the
position of the plane associated with activated planar modes.

A local eligibility criteria avoids using planar modes in adverse
regions of the point cloud and thus avoiding worse compression
performance, particularly on dense point clouds.
parent 8bed893b
......@@ -16,12 +16,16 @@ categories:
- intra_pred_max_node_size_log2: 6
- positionQuantizationScale: 1
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -21,12 +21,16 @@ categories:
r05: '$eval{ $rp = 1; $gp = ${src-geometry-precision}; $p_min = max(gp - 9, 7); $start = min(1, $gp - ($p_min + 6)); $step = max(1, (min($gp - 1, $p_min + 7) - $p_min) / 5); $y = $start + round($rp * $step); $div = 1 << (abs($y) + 1); ((1 - 2*signbit($y)) % $div) / $div }'
r06: '$eval{ $rp = 0; $gp = ${src-geometry-precision}; $p_min = max(gp - 9, 7); $start = min(1, $gp - ($p_min + 6)); $step = max(1, (min($gp - 1, $p_min + 7) - $p_min) / 5); $y = $start + round($rp * $step); $div = 1 << (abs($y) + 1); ((1 - 2*signbit($y)) % $div) / $div }'
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -17,12 +17,16 @@ categories:
- neighbourAvailBoundaryLog2: 8
- intra_pred_max_node_size_log2: 6
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -17,12 +17,16 @@ categories:
- neighbourAvailBoundaryLog2: 8
- intra_pred_max_node_size_log2: 6
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -16,12 +16,16 @@ categories:
- intra_pred_max_node_size_log2: 6
- positionQuantizationScale: 1
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -21,12 +21,16 @@ categories:
r05: '$eval{ $rp = 1; $gp = ${src-geometry-precision}; $p_min = max(gp - 9, 7); $start = min(1, $gp - ($p_min + 6)); $step = max(1, (min($gp - 1, $p_min + 7) - $p_min) / 5); $y = $start + round($rp * $step); $div = 1 << (abs($y) + 1); ((1 - 2*signbit($y)) % $div) / $div }'
r06: '$eval{ $rp = 0; $gp = ${src-geometry-precision}; $p_min = max(gp - 9, 7); $start = min(1, $gp - ($p_min + 6)); $step = max(1, (min($gp - 1, $p_min + 7) - $p_min) / 5); $y = $start + round($rp * $step); $div = 1 << (abs($y) + 1); ((1 - 2*signbit($y)) % $div) / $div }'
# default qtbt with cat3-frame exception
# default qtbt and planar with cat3-frame exception
- max_num_implicit_qtbt_before_ot: 4
- min_implicit_qtbt_size_log2: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
-
- !conditional '"${group}" eq "cat3-frame"'
- max_num_implicit_qtbt_before_ot: 0
- planarEnabled: 0
- planarModeIdcmUse: 127
####
# attribute coding (common options -- relies on option ordering)
......
......@@ -13,6 +13,8 @@ categories:
- neighbourAvailBoundaryLog2: 8
- intra_pred_max_node_size_log2: 6
- inferredDirectCodingMode: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
- positionQuantizationScale: '$eval{ 1 / (1 << ( ${src-geometry-precision} - ${test-depth} )) }'
- trisoup_node_size_log2:
r01: 4
......
......@@ -13,6 +13,8 @@ categories:
- neighbourAvailBoundaryLog2: 8
- intra_pred_max_node_size_log2: 6
- inferredDirectCodingMode: 0
- planarEnabled: 1
- planarModeIdcmUse: 0
- positionQuantizationScale: '$eval{ 1 / (1 << ( ${src-geometry-precision} - ${test-depth} )) }'
- trisoup_node_size_log2:
r01: 4
......
......@@ -284,6 +284,26 @@ its occupancy. The prediction mode is enabled for octree nodes smaller
than or equal to the configured size. A value of 0 disables intra
occupancy prediction.
### `--planarEnabled=0|1`
Controls the use of planar coding mode for geometry occupancy.
### `--planarModeIdcmUse=0--127`
Controls the frequency of IDCM eligibility. A value of zero prevents
IDCM being eligible for use, while the value 127 does not further
constrain IDCM use.
### `--planarModeThreshold0=0--127`
Controls the eligibility threshold of the first planar mode based upon
local child node density.
### `--planarModeThreshold1=0--127`
Controls the eligibility threshold of the second planar mode based upon
local child node density.
### `--planarModeThreshold2=0--127`
Controls the eligibility threshold of the third planar mode based upon
local child node density.
### `--ctxOccupancyReductionFactor=INT-VALUE`
Adjusts the number of contexts used in bit-wise occupancy coding.
The total number of contexts used is 256 >> *VALUE*.
......
......@@ -546,6 +546,30 @@ ParseParameters(int argc, char* argv[], Parameters& params)
params.encoder.gps.intra_pred_max_node_size_log2, 0,
"octree nodesizes eligible for occupancy intra prediction")
("planarEnabled",
params.encoder.gps.geom_planar_mode_enabled_flag, true,
"Use planar mode for geometry coding")
("planarModeThreshold0",
params.encoder.gps.geom_planar_threshold0, 77,
"Activation threshold (0-127) of first planar mode. "
"Lower values imply more use of the first planar mode")
("planarModeThreshold1",
params.encoder.gps.geom_planar_threshold1, 99,
"Activation threshold (0-127) of second planar mode. "
"Lower values imply more use of the first planar mode")
("planarModeThreshold2",
params.encoder.gps.geom_planar_threshold2, 113,
"Activation threshold (0-127) of third planar mode. "
"Lower values imply more use of the third planar mode")
("planarModeIdcmUse",
params.encoder.gps.geom_planar_idcm_threshold, 0,
"Degree (0-127) of IDCM activation when planar mode is enabled.\n"
" 0 => never, 127 => always")
("ctxOccupancyReductionFactor",
params.encoder.gps.geom_occupancy_ctx_reduction_factor, 3,
"Adjusts the number of contexts used in occupancy coding")
......@@ -765,6 +789,13 @@ ParseParameters(int argc, char* argv[], Parameters& params)
params.encoder.gps.inferred_direct_coding_mode_enabled_flag = false;
}
// Planar coding mode is not available for bytewise coding
if (!params.encoder.gps.bitwise_occupancy_coding_flag) {
if (params.encoder.gps.geom_planar_mode_enabled_flag)
err.warn() << "Bytewise geometry coding does not support planar mode\n";
params.encoder.gps.geom_planar_mode_enabled_flag = false;
}
// support disabling attribute coding (simplifies configuration)
if (params.disableAttributeCoding) {
params.encoder.attributeIdxMap.clear();
......
......@@ -37,6 +37,7 @@
#include "dependencies/schroedinger/schroarith.h"
#include <algorithm>
#include <assert.h>
#include <stdlib.h>
#include <memory>
......@@ -57,6 +58,16 @@ namespace dirac {
}
};
//--------------------------------------------------------------------------
// The approximate (7 bit) probability of a symbol being 1 or 0 according
// to a context model.
inline int approxSymbolProbability(int bit, SchroContext& model)
{
int p = std::max(1, model.probability >> 9);
return bit ? 128 - p : p;
}
//==========================================================================
struct SchroContextFixed {
......
......@@ -48,8 +48,12 @@ namespace pcc {
// neighbour configuration @neighPattern.
//
uint8_t
mapGeometryOccupancy(uint8_t occupancy, uint8_t neighPattern)
mapGeometryOccupancy(uint8_t occupancy, uint8_t neighPattern, int planarMaskZ)
{
if (planarMaskZ && !(planarMaskZ & 1)) {
occupancy = kOccMapMirrorXY[occupancy];
}
switch (kOccMapRotateZIdFromPatternXY[neighPattern & 15]) {
case 1: occupancy = kOccMapRotateZ090[occupancy]; break;
case 2: occupancy = kOccMapRotateZ180[occupancy]; break;
......@@ -79,7 +83,8 @@ mapGeometryOccupancy(uint8_t occupancy, uint8_t neighPattern)
// neighbour configuration @neighPattern.
//
uint8_t
mapGeometryOccupancyInv(uint8_t occupancy, uint8_t neighPattern)
mapGeometryOccupancyInv(
uint8_t occupancy, uint8_t neighPattern, int planarMaskZ)
{
switch (kOccMapRotateXIdFromPattern[neighPattern]) {
case 1: occupancy = kOccMapRotateX270[occupancy]; break;
......@@ -102,6 +107,10 @@ mapGeometryOccupancyInv(uint8_t occupancy, uint8_t neighPattern)
case 3: occupancy = kOccMapRotateZ090[occupancy]; break;
}
if (planarMaskZ && !(planarMaskZ & 1)) {
occupancy = kOccMapMirrorXY[occupancy];
}
return occupancy;
}
......@@ -220,6 +229,231 @@ CtxMapOctreeOccupancy::CtxMapOctreeOccupancy()
fill(begin(map->b7), end(map->b7), 127);
}
//============================================================================
// determine if a 222 block is planar
void
isPlanarNode(
PCCPointSet3& pointCloud,
PCCOctree3Node& node0,
const Vec3<int>& nodeSizeLog2Minus1,
uint8_t& planarMode,
uint8_t& planePosBits,
bool planarEligible[3])
{
planarMode = 0;
planePosBits = 0;
bool occupX[2] = {false, false};
bool occupY[2] = {false, false};
bool occupZ[2] = {false, false};
bool notPlanar[3] = {!planarEligible[0], !planarEligible[1],
!planarEligible[2]};
// find occupancy N xyz-planes
for (int k = node0.start; k < node0.end; k++) {
if (!notPlanar[0]) {
uint32_t px = pointCloud[k][0];
px = (px & (1 << nodeSizeLog2Minus1[0])) >> nodeSizeLog2Minus1[0];
occupX[px] = true;
if (occupX[1 - px])
notPlanar[0] = true;
}
if (!notPlanar[1]) {
uint32_t py = pointCloud[k][1];
py = (py & (1 << nodeSizeLog2Minus1[1])) >> nodeSizeLog2Minus1[1];
occupY[py] = true;
if (occupY[1 - py])
notPlanar[1] = true;
}
if (!notPlanar[2]) {
uint32_t pz = pointCloud[k][2];
pz = (pz & (1 << nodeSizeLog2Minus1[2])) >> nodeSizeLog2Minus1[2];
occupZ[pz] = true;
if (occupZ[1 - pz])
notPlanar[2] = true;
}
}
// determine planar
if (!(occupX[0] && occupX[1])) {
planarMode |= 1;
planePosBits |= occupX[1] ? 1 : 0;
}
if (!(occupY[0] && occupY[1])) {
planarMode |= 2;
planePosBits |= occupY[1] ? 2 : 0;
}
if (!(occupZ[0] && occupZ[1])) {
planarMode |= 4;
planePosBits |= occupZ[1] ? 4 : 0;
}
}
//============================================================================
// intitialize planes for planar pred
void
planarInitPlanes(
const int kNumPlanarPlanes, int depth, int* planes3x3, int* planes[9])
{
const int kPlanarInit[9] = {-2, -1000, -1000, -1000, -2,
-1000, -1000, -1000, -2};
int shift = 0;
const int planeSize = kNumPlanarPlanes << depth;
for (int idxP = 0; idxP < 9; idxP++) {
int* plane = planes3x3 + shift;
shift += planeSize;
planes[idxP] = plane;
int v = kPlanarInit[idxP];
for (int p = 0; p < planeSize; p++) {
plane[p] = v;
}
}
}
//============================================================================
// update the plane rate depending on the occupancy
void
updateplanarRate(
int planarRate[3], int occupancy, int& localDensity, int numSiblings)
{
bool isPlanarX = !((occupancy & 0xf0) && (occupancy & 0x0f));
bool isPlanarY = !((occupancy & 0xcc) && (occupancy & 0x33));
bool isPlanarZ = !((occupancy & 0x55) && (occupancy & 0xaa));
planarRate[0] = (255 * planarRate[0] + (isPlanarX ? 256 * 8 : 0) + 128) >> 8;
planarRate[1] = (255 * planarRate[1] + (isPlanarY ? 256 * 8 : 0) + 128) >> 8;
planarRate[2] = (255 * planarRate[2] + (isPlanarZ ? 256 * 8 : 0) + 128) >> 8;
localDensity = (255 * localDensity + 1024 * numSiblings) >> 8;
}
//============================================================================
// planar eligbility
void
eligilityPlanar(
bool planarEligible[3],
int planarRate[3],
const int threshold[3],
int localDensity)
{
planarEligible[0] = false;
planarEligible[1] = false;
planarEligible[2] = false;
if (localDensity >= 3 * 1024) {
return;
}
if (planarRate[0] >= planarRate[1] && planarRate[0] >= planarRate[2]) {
// planar x dominates
planarEligible[0] = planarRate[0] >= threshold[0];
if (planarRate[1] >= planarRate[2]) {
planarEligible[1] = planarRate[1] >= threshold[1];
planarEligible[2] = planarRate[2] >= threshold[2];
} else {
planarEligible[2] = planarRate[2] >= threshold[1];
planarEligible[1] = planarRate[1] >= threshold[2];
}
} else if (
planarRate[1] >= planarRate[0] && planarRate[1] >= planarRate[2]) {
// planar y dominates
planarEligible[1] = planarRate[1] >= threshold[0];
if (planarRate[0] >= planarRate[2]) {
planarEligible[0] = planarRate[0] >= threshold[1];
planarEligible[2] = planarRate[2] >= threshold[2];
} else {
planarEligible[2] = planarRate[2] >= threshold[1];
planarEligible[0] = planarRate[0] >= threshold[2];
}
} else if (
planarRate[2] >= planarRate[0] && planarRate[2] >= planarRate[1]) {
// planar z dominates
planarEligible[2] = planarRate[2] >= threshold[0];
if (planarRate[0] >= planarRate[1]) {
planarEligible[0] = planarRate[0] >= threshold[1];
planarEligible[1] = planarRate[1] >= threshold[2];
} else {
planarEligible[1] = planarRate[1] >= threshold[1];
planarEligible[0] = planarRate[0] >= threshold[2];
}
}
}
//============================================================================
// directional mask depending on the planarity
int
maskPlanarX(PCCOctree3Node& node0, bool implicitSkip)
{
if (implicitSkip)
return 0xf0;
if ((node0.planarMode & 1) == 0)
return 0;
return (node0.planePosBits & 1) ? 0x0f : 0xf0;
}
//----------------------------------------------------------------------------
int
maskPlanarY(PCCOctree3Node& node0, bool implicitSkip)
{
if (implicitSkip)
return 0xcc;
if ((node0.planarMode & 2) == 0)
return 0;
return (node0.planePosBits & 2) ? 0x33 : 0xcc;
}
//----------------------------------------------------------------------------
int
maskPlanarZ(PCCOctree3Node& node0, bool implicitSkip)
{
// QTBT does not split in this direction
// => infer the mask low for occupancy bit coding
if (implicitSkip)
return 0xaa;
if ((node0.planarMode & 4) == 0)
return 0;
return (node0.planePosBits & 4) ? 0x55 : 0xaa;
}
//----------------------------------------------------------------------------
// three direction mask
void
maskPlanar(PCCOctree3Node& node0, int mask[3], const int occupancySkip)
{
static const uint8_t kPossibleMask[3] = {6, 5, 3};
for (int k = 0; k <= 2; k++)
if (occupancySkip & (4 >> k)) {
node0.planarPossible = node0.planarPossible | (1 << k);
node0.planePosBits = node0.planePosBits & kPossibleMask[k];
node0.planarMode = node0.planarMode | (1 << k);
}
mask[0] = maskPlanarX(node0, occupancySkip & 4);
mask[1] = maskPlanarY(node0, occupancySkip & 2);
mask[2] = maskPlanarZ(node0, occupancySkip & 1);
}
//============================================================================
} // namespace pcc
......@@ -80,12 +80,18 @@ struct PCCOctree3Node {
// The qp used for geometry quantisation
int qp;
// planar; first bit for x, second bit for y, third bit for z
uint8_t planarPossible = 7;
uint8_t planePosBits = 0;
uint8_t planarMode = 0;
};
//---------------------------------------------------------------------------
uint8_t mapGeometryOccupancy(uint8_t occupancy, uint8_t neighPattern);
uint8_t mapGeometryOccupancyInv(uint8_t occupancy, uint8_t neighPattern);
uint8_t
mapGeometryOccupancy(uint8_t occupancy, uint8_t neighPattern, int planarMaskZ);
uint8_t mapGeometryOccupancyInv(
uint8_t occupancy, uint8_t neighPattern, int planarMaskZ);
void updateGeometryNeighState(
bool siblingRestriction,
......@@ -302,4 +308,31 @@ nonSplitQtBtAxes(const Vec3<int>& nodeSizeLog2, const Vec3<int>& childSizeLog2)
//---------------------------------------------------------------------------
// determine if a 222 block is planar
void isPlanarNode(
PCCPointSet3& pointCloud,
PCCOctree3Node& node0,
const Vec3<int>& sizeLog2,
uint8_t& planarMode,
uint8_t& planePosBits,
bool planarEligible[3]);
void planarInitPlanes(
const int kNumPlanarPlanes, int depth, int* planes3x3, int* planes[9]);
void updateplanarRate(
int planarRate[3], int occupancy, int& localDensity, int numSiblings);
void eligilityPlanar(
bool planarEligible[3],
int plane_rate[3],
const int threshold[3],
int localDensity);
int maskPlanarX(PCCOctree3Node& node0, bool activatable);
int maskPlanarY(PCCOctree3Node& node0, bool activatable);
int maskPlanarZ(PCCOctree3Node& node0, bool activatable);
void maskPlanar(PCCOctree3Node& node0, int mask[3], const int occupancySkip);
//---------------------------------------------------------------------------
} // namespace pcc
This diff is collapsed.
This diff is collapsed.
......@@ -280,6 +280,13 @@ struct GeometryParameterSet {
// minimum size in log2 of implicit qtbt for geometry coding.
int min_implicit_qtbt_size_log2;
// Controls the use of planar mode
bool geom_planar_mode_enabled_flag;
int geom_planar_threshold0;
int geom_planar_threshold1;
int geom_planar_threshold2;
int geom_planar_idcm_threshold;
};
//============================================================================
......
......@@ -212,6 +212,15 @@ write(const GeometryParameterSet& gps)
bs.write(gps.inferred_direct_coding_mode_enabled_flag);
bs.write(gps.bitwise_occupancy_coding_flag);
bs.write(gps.adjacent_child_contextualization_enabled_flag);
bs.write(gps.geom_planar_mode_enabled_flag);
bs.writeUe(gps.geom_planar_idcm_threshold);
if (gps.geom_planar_mode_enabled_flag) {
bs.writeUe(gps.geom_planar_threshold0);
bs.writeUe(gps.geom_planar_threshold1);
bs.writeUe(gps.geom_planar_threshold2);
}
bs.writeUe(gps.geom_occupancy_ctx_reduction_factor);
bs.writeUe(gps.neighbour_avail_boundary_log2);
bs.writeUe(gps.intra_pred_max_node_size_log2);
......@@ -256,6 +265,15 @@ parseGps(const PayloadBuffer& buf)
bs.read(&gps.inferred_direct_coding_mode_enabled_flag);
bs.read(&gps.bitwise_occupancy_coding_flag);
bs.read(&gps.adjacent_child_contextualization_enabled_flag);
bs.read(&gps.geom_planar_mode_enabled_flag);
bs.readUe(&gps.geom_planar_idcm_threshold);
if (gps.geom_planar_mode_enabled_flag) {
bs.readUe(&gps.geom_planar_threshold0);
bs.readUe(&gps.geom_planar_threshold1);
bs.readUe(&gps.geom_planar_threshold2);
}