Commit d96e5851 authored by David Flynn's avatar David Flynn
Browse files

hls/m51027: signal geometry axis order

This adoption permits encoding the geometry positions in an order other
than {x,y,z}.  The --geometry_axis_order option causes the encoder to
load data from the ply file in a different order.  The order is
signalled in the SPS and the decoder will label the output axis as
appropriate.
parent 31d57093
...@@ -150,6 +150,22 @@ Controls the ability to code duplicate points. When duplicate point ...@@ -150,6 +150,22 @@ Controls the ability to code duplicate points. When duplicate point
merging is enabled, bitstream syntax related to duplicate points is merging is enabled, bitstream syntax related to duplicate points is
disabled and a pre-filtering process is used to remove co-located points. disabled and a pre-filtering process is used to remove co-located points.
### `--geometry_axis_order=INT-VALUE`
Configures the order in which axes are internally coded. Changing
the axis order does not change the orientation of the reconstructed
point cloud.
| Value | Coding order |
|:-----:| -------------|
| 0 | z, y, x |
| 1 | x, y, z |
| 2 | x, z, y |
| 3 | y, z, x |
| 4 | z, y, x |
| 5 | z, x, y |
| 6 | y, x, z |
| 7 | x, y, z |
### `--disableAttributeCoding=0|1` ### `--disableAttributeCoding=0|1`
This option instructs the encoder to ignore all options relating to This option instructs the encoder to ignore all options relating to
attribute coding, as if they had never been configured. attribute coding, as if they had never been configured.
......
...@@ -128,7 +128,8 @@ private: ...@@ -128,7 +128,8 @@ private:
class PCCTMC3Decoder3::Callbacks { class PCCTMC3Decoder3::Callbacks {
public: public:
virtual void onOutputCloud(const PCCPointSet3&) = 0; virtual void
onOutputCloud(const SequenceParameterSet&, const PCCPointSet3&) = 0;
}; };
//============================================================================ //============================================================================
......
...@@ -104,6 +104,8 @@ protected: ...@@ -104,6 +104,8 @@ protected:
void onPostRecolour(const PCCPointSet3& cloud) override; void onPostRecolour(const PCCPointSet3& cloud) override;
private: private:
ply::PropertyNameMap _plyAttrNames;
Parameters* params; Parameters* params;
PCCTMC3Encoder3 encoder; PCCTMC3Encoder3 encoder;
...@@ -122,7 +124,9 @@ public: ...@@ -122,7 +124,9 @@ public:
int decompress(Stopwatch* clock); int decompress(Stopwatch* clock);
protected: protected:
void onOutputCloud(const PCCPointSet3& decodedPointCloud) override; void onOutputCloud(
const SequenceParameterSet& sps,
const PCCPointSet3& decodedPointCloud) override;
private: private:
const Parameters* params; const Parameters* params;
...@@ -170,6 +174,19 @@ main(int argc, char* argv[]) ...@@ -170,6 +174,19 @@ main(int argc, char* argv[])
return ret; return ret;
} }
//---------------------------------------------------------------------------
std::array<const char*, 3>
axisOrderToPropertyNames(AxisOrder order)
{
static const std::array<const char*, 3> kAxisOrderToPropertyNames[] = {
{"z", "y", "x"}, {"x", "y", "z"}, {"x", "z", "y"}, {"y", "z", "x"},
{"z", "y", "x"}, {"z", "x", "y"}, {"y", "x", "z"}, {"x", "y", "z"},
};
return kAxisOrderToPropertyNames[int(order)];
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// :: Command line / config parsing helpers // :: Command line / config parsing helpers
...@@ -183,6 +200,14 @@ readUInt(std::istream& in, T& val) ...@@ -183,6 +200,14 @@ readUInt(std::istream& in, T& val)
return in; return in;
} }
namespace pcc {
static std::istream&
operator>>(std::istream& in, AxisOrder& val)
{
return readUInt(in, val);
}
} // namespace pcc
static std::istream& static std::istream&
operator>>(std::istream& in, ColorTransform& val) operator>>(std::istream& in, ColorTransform& val)
{ {
...@@ -205,6 +230,24 @@ operator>>(std::istream& in, PartitionMethod& val) ...@@ -205,6 +230,24 @@ operator>>(std::istream& in, PartitionMethod& val)
} }
} // namespace pcc } // namespace pcc
namespace pcc {
static std::ostream&
operator<<(std::ostream& out, const AxisOrder& val)
{
switch (val) {
case AxisOrder::kZYX: out << "0 (zyx)"; break;
case AxisOrder::kXYZ: out << "1 (xyz)"; break;
case AxisOrder::kXZY: out << "2 (xzy)"; break;
case AxisOrder::kYZX: out << "3 (yzx)"; break;
case AxisOrder::kZYX_4: out << "4 (zyx)"; break;
case AxisOrder::kZXY: out << "5 (zxy)"; break;
case AxisOrder::kYXZ: out << "6 (yxz)"; break;
case AxisOrder::kXYZ_7: out << "7 (xyz)"; break;
}
return out;
}
} // namespace pcc
namespace pcc { namespace pcc {
static std::ostream& static std::ostream&
operator<<(std::ostream& out, const AttributeEncoding& val) operator<<(std::ostream& out, const AttributeEncoding& val)
...@@ -371,6 +414,13 @@ ParseParameters(int argc, char* argv[], Parameters& params) ...@@ -371,6 +414,13 @@ ParseParameters(int argc, char* argv[], Parameters& params)
(po::Section("Encoder")) (po::Section("Encoder"))
("geometry_axis_order",
params.encoder.sps.geometry_axis_order, AxisOrder::kXYZ,
"Sets the geometry axis coding order:\n"
" 0: (zyx)\n 1: (xyz)\n 2: (xzy)\n"
" 3: (yzx)\n 4: (zyx)\n 5: (zxy)\n"
" 6: (yxz)\n 7: (xyz)")
("seq_bounding_box_xyz0", ("seq_bounding_box_xyz0",
params.encoder.sps.seq_bounding_box_xyz0, {0}, params.encoder.sps.seq_bounding_box_xyz0, {0},
"seq_bounding_box_xyz0. NB: seq_bounding_box_whd must be set for this " "seq_bounding_box_xyz0. NB: seq_bounding_box_whd must be set for this "
...@@ -847,7 +897,11 @@ ParseParameters(int argc, char* argv[], Parameters& params) ...@@ -847,7 +897,11 @@ ParseParameters(int argc, char* argv[], Parameters& params)
//============================================================================ //============================================================================
SequenceEncoder::SequenceEncoder(Parameters* params) : params(params) SequenceEncoder::SequenceEncoder(Parameters* params) : params(params)
{} {
// determine the naming (ordering) of ply properties
_plyAttrNames.position =
axisOrderToPropertyNames(params->encoder.sps.geometry_axis_order);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
...@@ -878,7 +932,9 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock) ...@@ -878,7 +932,9 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock)
{ {
std::string srcName{expandNum(params->uncompressedDataPath, frameNum)}; std::string srcName{expandNum(params->uncompressedDataPath, frameNum)};
PCCPointSet3 pointCloud; PCCPointSet3 pointCloud;
if (!ply::read(srcName, pointCloud) || pointCloud.getPointCount() == 0) { if (
!ply::read(srcName, _plyAttrNames, pointCloud)
|| pointCloud.getPointCount() == 0) {
cout << "Error: can't open input file!" << endl; cout << "Error: can't open input file!" << endl;
return -1; return -1;
} }
...@@ -946,7 +1002,8 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock) ...@@ -946,7 +1002,8 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock)
} }
std::string recName{expandNum(params->reconstructedDataPath, frameNum)}; std::string recName{expandNum(params->reconstructedDataPath, frameNum)};
ply::write(*reconPointCloud, recName, !params->outputBinaryPly); ply::write(
*reconPointCloud, _plyAttrNames, recName, !params->outputBinaryPly);
} }
return 0; return 0;
...@@ -973,13 +1030,13 @@ SequenceEncoder::onPostRecolour(const PCCPointSet3& cloud) ...@@ -973,13 +1030,13 @@ SequenceEncoder::onPostRecolour(const PCCPointSet3& cloud)
// todo(df): stop the clock // todo(df): stop the clock
if (params->colorTransform != COLOR_TRANSFORM_RGB_TO_YCBCR) { if (params->colorTransform != COLOR_TRANSFORM_RGB_TO_YCBCR) {
ply::write(cloud, plyName, !params->outputBinaryPly); ply::write(cloud, _plyAttrNames, plyName, !params->outputBinaryPly);
return; return;
} }
PCCPointSet3 tmpCloud(cloud); PCCPointSet3 tmpCloud(cloud);
tmpCloud.convertYUVToRGB(); tmpCloud.convertYUVToRGB();
ply::write(tmpCloud, plyName, !params->outputBinaryPly); ply::write(tmpCloud, _plyAttrNames, plyName, !params->outputBinaryPly);
} }
//============================================================================ //============================================================================
...@@ -1034,7 +1091,8 @@ SequenceDecoder::decompress(Stopwatch* clock) ...@@ -1034,7 +1091,8 @@ SequenceDecoder::decompress(Stopwatch* clock)
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud) SequenceDecoder::onOutputCloud(
const SequenceParameterSet& sps, const PCCPointSet3& decodedPointCloud)
{ {
// copy the point cloud in order to modify it according to the output options // copy the point cloud in order to modify it according to the output options
PCCPointSet3 pointCloud(decodedPointCloud); PCCPointSet3 pointCloud(decodedPointCloud);
...@@ -1051,10 +1109,16 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud) ...@@ -1051,10 +1109,16 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud)
} }
} }
// the order of the property names must be determined from the sps
ply::PropertyNameMap attrNames;
attrNames.position = axisOrderToPropertyNames(sps.geometry_axis_order);
// Dump the decoded colour using the pre inverse scaled geometry // Dump the decoded colour using the pre inverse scaled geometry
if (!params->preInvScalePath.empty()) { if (!params->preInvScalePath.empty()) {
std::string filename{expandNum(params->preInvScalePath, frameNum)}; std::string filename{expandNum(params->preInvScalePath, frameNum)};
ply::write(pointCloud, params->preInvScalePath, !params->outputBinaryPly); ply::write(
pointCloud, attrNames, params->preInvScalePath,
!params->outputBinaryPly);
} }
decoder.inverseQuantization(pointCloud); decoder.inverseQuantization(pointCloud);
...@@ -1062,7 +1126,7 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud) ...@@ -1062,7 +1126,7 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud)
clock->stop(); clock->stop();
std::string decName{expandNum(params->reconstructedDataPath, frameNum)}; std::string decName{expandNum(params->reconstructedDataPath, frameNum)};
if (!ply::write(pointCloud, decName, !params->outputBinaryPly)) { if (!ply::write(pointCloud, attrNames, decName, !params->outputBinaryPly)) {
cout << "Error: can't open output file!" << endl; cout << "Error: can't open output file!" << endl;
} }
......
...@@ -90,7 +90,7 @@ PCCTMC3Decoder3::decompress( ...@@ -90,7 +90,7 @@ PCCTMC3Decoder3::decompress(
if (!buf) { if (!buf) {
// flush decoder, output pending cloud if any // flush decoder, output pending cloud if any
callback->onOutputCloud(_accumCloud); callback->onOutputCloud(*_sps, _accumCloud);
_accumCloud.clear(); _accumCloud.clear();
return 0; return 0;
} }
...@@ -106,7 +106,8 @@ PCCTMC3Decoder3::decompress( ...@@ -106,7 +106,8 @@ PCCTMC3Decoder3::decompress(
// NB: frame counter is reset to avoid outputing a runt point cloud // NB: frame counter is reset to avoid outputing a runt point cloud
// on the next slice. // on the next slice.
case PayloadType::kFrameBoundaryMarker: case PayloadType::kFrameBoundaryMarker:
callback->onOutputCloud(_accumCloud); // todo(df): if no sps is activated ...
callback->onOutputCloud(*_sps, _accumCloud);
_accumCloud.clear(); _accumCloud.clear();
_currentFrameIdx = -1; _currentFrameIdx = -1;
return 0; return 0;
...@@ -114,7 +115,7 @@ PCCTMC3Decoder3::decompress( ...@@ -114,7 +115,7 @@ PCCTMC3Decoder3::decompress(
case PayloadType::kGeometryBrick: case PayloadType::kGeometryBrick:
activateParameterSets(parseGbhIds(*buf)); activateParameterSets(parseGbhIds(*buf));
if (frameIdxChanged(parseGbh(*_sps, *_gps, *buf, nullptr))) { if (frameIdxChanged(parseGbh(*_sps, *_gps, *buf, nullptr))) {
callback->onOutputCloud(_accumCloud); callback->onOutputCloud(*_sps, _accumCloud);
_accumCloud.clear(); _accumCloud.clear();
} }
return decodeGeometryBrick(*buf); return decodeGeometryBrick(*buf);
......
...@@ -121,6 +121,20 @@ enum class AttributeEncoding ...@@ -121,6 +121,20 @@ enum class AttributeEncoding
//============================================================================ //============================================================================
enum class AxisOrder
{
kZYX = 0,
kXYZ = 1,
kXZY = 2,
kYZX = 3,
kZYX_4 = 4,
kZXY = 5,
kYXZ = 6,
kXYZ_7 = 7,
};
//============================================================================
// invariant properties // invariant properties
struct AttributeDescription { struct AttributeDescription {
int attr_num_dimensions; int attr_num_dimensions;
...@@ -162,6 +176,9 @@ struct SequenceParameterSet { ...@@ -162,6 +176,9 @@ struct SequenceParameterSet {
// The number of bits to use for frame_idx // The number of bits to use for frame_idx
int log2_max_frame_idx; int log2_max_frame_idx;
// Defines the ordering of the position components (eg, xyz vs zyx)
AxisOrder geometry_axis_order;
// Controls whether bypass bins are written to a seperate sub-stream, or // Controls whether bypass bins are written to a seperate sub-stream, or
// encoded as ep bins via CABAC. // encoded as ep bins via CABAC.
bool cabac_bypass_stream_enabled_flag; bool cabac_bypass_stream_enabled_flag;
......
...@@ -112,6 +112,7 @@ write(const SequenceParameterSet& sps) ...@@ -112,6 +112,7 @@ write(const SequenceParameterSet& sps)
} }
bs.writeUn(5, sps.log2_max_frame_idx); bs.writeUn(5, sps.log2_max_frame_idx);
bs.writeUn(3, sps.geometry_axis_order);
bs.write(sps.cabac_bypass_stream_enabled_flag); bs.write(sps.cabac_bypass_stream_enabled_flag);
bool sps_extension_flag = false; bool sps_extension_flag = false;
...@@ -173,6 +174,7 @@ parseSps(const PayloadBuffer& buf) ...@@ -173,6 +174,7 @@ parseSps(const PayloadBuffer& buf)
} }
bs.readUn(5, &sps.log2_max_frame_idx); bs.readUn(5, &sps.log2_max_frame_idx);
bs.readUn(3, &sps.geometry_axis_order);
bs.read(&sps.cabac_bypass_stream_enabled_flag); bs.read(&sps.cabac_bypass_stream_enabled_flag);
bool sps_extension_flag = bs.read(); bool sps_extension_flag = bs.read();
......
...@@ -87,7 +87,10 @@ getTokens( ...@@ -87,7 +87,10 @@ getTokens(
bool bool
ply::write( ply::write(
const PCCPointSet3& cloud, const std::string& fileName, bool asAscii) const PCCPointSet3& cloud,
const PropertyNameMap& attributeNames,
const std::string& fileName,
bool asAscii)
{ {
std::ofstream fout(fileName, std::ofstream::out); std::ofstream fout(fileName, std::ofstream::out);
if (!fout.is_open()) { if (!fout.is_open()) {
...@@ -109,13 +112,13 @@ ply::write( ...@@ -109,13 +112,13 @@ ply::write(
} }
fout << "element vertex " << pointCount << std::endl; fout << "element vertex " << pointCount << std::endl;
if (asAscii) { if (asAscii) {
fout << "property float x" << std::endl; fout << "property float " << attributeNames.position[0] << std::endl;
fout << "property float y" << std::endl; fout << "property float " << attributeNames.position[1] << std::endl;
fout << "property float z" << std::endl; fout << "property float " << attributeNames.position[2] << std::endl;
} else { } else {
fout << "property float64 x" << std::endl; fout << "property float64 " << attributeNames.position[0] << std::endl;
fout << "property float64 y" << std::endl; fout << "property float64 " << attributeNames.position[1] << std::endl;
fout << "property float64 z" << std::endl; fout << "property float64 " << attributeNames.position[2] << std::endl;
} }
if (cloud.hasColors()) { if (cloud.hasColors()) {
...@@ -182,7 +185,10 @@ ply::write( ...@@ -182,7 +185,10 @@ ply::write(
//============================================================================ //============================================================================
bool bool
ply::read(const std::string& fileName, PCCPointSet3& cloud) ply::read(
const std::string& fileName,
const PropertyNameMap& attributeNames,
PCCPointSet3& cloud)
{ {
std::ifstream ifs(fileName, std::ifstream::in | std::ifstream::binary); std::ifstream ifs(fileName, std::ifstream::in | std::ifstream::binary);
if (!ifs.is_open()) { if (!ifs.is_open()) {
...@@ -317,15 +323,15 @@ ply::read(const std::string& fileName, PCCPointSet3& cloud) ...@@ -317,15 +323,15 @@ ply::read(const std::string& fileName, PCCPointSet3& cloud)
for (size_t a = 0; a < attributeCount; ++a) { for (size_t a = 0; a < attributeCount; ++a) {
const auto& attributeInfo = attributesInfo[a]; const auto& attributeInfo = attributesInfo[a];
if ( if (
attributeInfo.name == "x" attributeInfo.name == attributeNames.position[0]
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) { && (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexX = a; indexX = a;
} else if ( } else if (
attributeInfo.name == "y" attributeInfo.name == attributeNames.position[1]
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) { && (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexY = a; indexY = a;
} else if ( } else if (
attributeInfo.name == "z" attributeInfo.name == attributeNames.position[2]
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) { && (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexZ = a; indexZ = a;
} else if (attributeInfo.name == "red" && attributeInfo.byteCount == 1) { } else if (attributeInfo.name == "red" && attributeInfo.byteCount == 1) {
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#pragma once #pragma once
#include <array>
#include <string> #include <string>
#include "PCCPointSet.h" #include "PCCPointSet.h"
...@@ -43,10 +44,26 @@ namespace pcc { ...@@ -43,10 +44,26 @@ namespace pcc {
namespace ply { namespace ply {
//============================================================================ //============================================================================
// This defines the the attribute names to be used when reading/writing ply
// files. The order of the names define the ordering used in the internal
// point cloud representation.
bool struct PropertyNameMap {
write(const PCCPointSet3& cloud, const std::string& fileName, bool asAscii); // The names of the position attributes, typically {"x", "y", "z"}
bool read(const std::string& fileName, PCCPointSet3& cloud); std::array<const char*, 3> position;
};
//============================================================================
bool write(
const PCCPointSet3& cloud,
const PropertyNameMap& propertyNames,
const std::string& fileName,
bool asAscii);
bool read(
const std::string& fileName,
const PropertyNameMap& propertyNames,
PCCPointSet3& cloud);
//============================================================================ //============================================================================
......
...@@ -219,6 +219,9 @@ parseParameters(int argc, char* argv[], Options& params) ...@@ -219,6 +219,9 @@ parseParameters(int argc, char* argv[], Options& params)
void void
runMerge(const Options& opts) runMerge(const Options& opts)
{ {
ply::PropertyNameMap propNames;
propNames.position = {"x", "y", "z"};
// iterate over all input frames in groups // iterate over all input frames in groups
int outFrameNum = opts.firstOutputFrameNum; int outFrameNum = opts.firstOutputFrameNum;
for (int i = 0; i < opts.frameCount; outFrameNum++) { for (int i = 0; i < opts.frameCount; outFrameNum++) {
...@@ -232,7 +235,9 @@ runMerge(const Options& opts) ...@@ -232,7 +235,9 @@ runMerge(const Options& opts)
srcClouds.emplace_back(); srcClouds.emplace_back();
auto& srcCloud = srcClouds.back(); auto& srcCloud = srcClouds.back();
if (!ply::read(srcName, srcCloud) || srcCloud.getPointCount() == 0) { if (
!ply::read(srcName, propNames, srcCloud)
|| srcCloud.getPointCount() == 0) {
throw runtime_error("failed to read input file: " + srcName); throw runtime_error("failed to read input file: " + srcName);
} }
} }
...@@ -268,7 +273,7 @@ runMerge(const Options& opts) ...@@ -268,7 +273,7 @@ runMerge(const Options& opts)
} }
string outName{expandNum(opts.outPath, outFrameNum)}; string outName{expandNum(opts.outPath, outFrameNum)};
if (!ply::write(outCloud, outName, !opts.outputBinaryPly)) if (!ply::write(outCloud, propNames, outName, !opts.outputBinaryPly))
throw runtime_error("failed to write output file: " + outName); throw runtime_error("failed to write output file: " + outName);
cout << outName << endl; cout << outName << endl;
} }
...@@ -281,6 +286,9 @@ runMerge(const Options& opts) ...@@ -281,6 +286,9 @@ runMerge(const Options& opts)
void void
runSplit(const Options& opts) runSplit(const Options& opts)
{ {
ply::PropertyNameMap propNames;
propNames.position = {"x", "y", "z"};
int outFrameNum = opts.firstOutputFrameNum; int outFrameNum = opts.firstOutputFrameNum;
int srcFrameNum = opts.firstFrameNum; int srcFrameNum = opts.firstFrameNum;
...@@ -288,7 +296,9 @@ runSplit(const Options& opts) ...@@ -288,7 +296,9 @@ runSplit(const Options& opts)
string srcName{expandNum(opts.srcPath, srcFrameNum)}; string srcName{expandNum(opts.srcPath, srcFrameNum)};
PCCPointSet3 srcCloud; PCCPointSet3 srcCloud;
if (!ply::read(srcName, srcCloud) || srcCloud.getPointCount() == 0) { if (
!ply::read(srcName, propNames, srcCloud)
|| srcCloud.getPointCount() == 0) {
throw runtime_error("failed to read input file: " + srcName); throw runtime_error("failed to read input file: " + srcName);
} }
...@@ -331,7 +341,7 @@ runSplit(const Options& opts) ...@@ -331,7 +341,7 @@ runSplit(const Options& opts)
string outName{expandNum(opts.outPath, outFrameNum)}; string outName{expandNum(opts.outPath, outFrameNum)};
if (outCloud.getPointCount() > 0) if (outCloud.getPointCount() > 0)
if (!ply::write(outCloud, outName, !opts.outputBinaryPly)) if (!ply::write(outCloud, propNames, outName, !opts.outputBinaryPly))
throw runtime_error("failed to write output file: " + outName); throw runtime_error("failed to write output file: " + outName);
cout << outName << endl; cout << outName << endl;
......