Commit 31d57093 authored by David Flynn's avatar David Flynn
Browse files

ply: move ply io methods to separate compilation unit

This commit migrates PCCPointSet3::read and write to ply::read and write.
parent ef9b47b9
......@@ -88,6 +88,7 @@ file(GLOB PROJECT_INC_FILES
"osspecific.h"
"partitioning.h"
"pcc_chrono.h"
"ply.h"
"quantization.h"
"ringbuf.h"
"tables.h"
......@@ -122,6 +123,7 @@ file(GLOB PROJECT_CPP_FILES
"osspecific.cpp"
"partitioning.cpp"
"pcc_chrono.cpp"
"ply.cpp"
"quantization.cpp"
"tables.cpp"
"../dependencies/arithmetic-coding/src/*.cpp"
......@@ -153,6 +155,7 @@ add_dependencies(tmc3 genversion)
add_executable (ply-merge EXCLUDE_FROM_ALL
"../tools/ply-merge.cpp"
"misc.cpp"
"ply.cpp"
"../dependencies/program-options-lite/program_options_lite.cpp"
${VERSION_FILE}
)
......
......@@ -491,420 +491,6 @@ public:
return bbox;
}
//--------------------------------------------------------------------------
static bool compareSeparators(char aChar, const char* const sep)
{
int i = 0;
while (sep[i] != '\0') {
if (aChar == sep[i])
return false;
i++;
}
return true;
}
static inline bool getTokens(
const char* str, const char* const sep, std::vector<std::string>& tokens)
{
if (!tokens.empty())
tokens.clear();
std::string buf = "";
size_t i = 0;
size_t length = ::strlen(str);
while (i < length) {
if (compareSeparators(str[i], sep)) {
buf += str[i];
} else if (buf.length() > 0) {
tokens.push_back(buf);
buf = "";
}
i++;
}
if (!buf.empty())
tokens.push_back(buf);
return !tokens.empty();
}
bool write(const std::string& fileName, const bool asAscii = false) const
{
std::ofstream fout(fileName, std::ofstream::out);
if (!fout.is_open()) {
return false;
}
const size_t pointCount = getPointCount();
fout << "ply" << std::endl;
if (asAscii) {
fout << "format ascii 1.0" << std::endl;
} else {
PCCEndianness endianess = PCCSystemEndianness();
if (endianess == PCC_BIG_ENDIAN) {
fout << "format binary_big_endian 1.0" << std::endl;
} else {
fout << "format binary_little_endian 1.0" << std::endl;
}
}
fout << "element vertex " << pointCount << std::endl;
if (asAscii) {
fout << "property float x" << std::endl;
fout << "property float y" << std::endl;
fout << "property float z" << std::endl;
} else {
fout << "property float64 x" << std::endl;
fout << "property float64 y" << std::endl;
fout << "property float64 z" << std::endl;
}
if (hasColors()) {
fout << "property uchar red" << std::endl;
fout << "property uchar green" << std::endl;
fout << "property uchar blue" << std::endl;
}
if (hasReflectances()) {
fout << "property uint16 refc" << std::endl;
}
if (hasFrameIndex()) {
fout << "property uint8 frameindex" << std::endl;
}
fout << "element face 0" << std::endl;
fout << "property list uint8 int32 vertex_index" << std::endl;
fout << "end_header" << std::endl;
if (asAscii) {
// fout << std::setprecision(std::numeric_limits<double>::max_digits10);
fout << std::fixed << std::setprecision(5);
for (size_t i = 0; i < pointCount; ++i) {
const Vec3<double>& position = (*this)[i];
fout << position.x() << " " << position.y() << " " << position.z();
if (hasColors()) {
const Vec3<uint8_t>& color = getColor(i);
fout << " " << static_cast<int>(color[0]) << " "
<< static_cast<int>(color[1]) << " "
<< static_cast<int>(color[2]);
}
if (hasReflectances()) {
fout << " " << static_cast<int>(getReflectance(i));
}
if (hasFrameIndex()) {
fout << " " << static_cast<int>(getFrameIndex(i));
}
fout << std::endl;
}
} else {
fout.clear();
fout.close();
fout.open(fileName, std::ofstream::binary | std::ofstream::app);
for (size_t i = 0; i < pointCount; ++i) {
const Vec3<double>& position = (*this)[i];
fout.write(
reinterpret_cast<const char* const>(&position), sizeof(double) * 3);
if (hasColors()) {
const Vec3<uint8_t>& color = getColor(i);
fout.write(
reinterpret_cast<const char*>(&color), sizeof(uint8_t) * 3);
}
if (hasReflectances()) {
const uint16_t& reflectance = getReflectance(i);
fout.write(
reinterpret_cast<const char*>(&reflectance), sizeof(uint16_t));
}
if (hasFrameIndex()) {
const uint16_t& findex = getFrameIndex(i);
fout.write(reinterpret_cast<const char*>(&findex), sizeof(uint16_t));
}
}
}
fout.close();
return true;
}
bool read(const std::string& fileName)
{
std::ifstream ifs(fileName, std::ifstream::in | std::ifstream::binary);
if (!ifs.is_open()) {
return false;
}
enum AttributeType
{
ATTRIBUTE_TYPE_FLOAT64 = 0,
ATTRIBUTE_TYPE_FLOAT32 = 1,
ATTRIBUTE_TYPE_UINT64 = 2,
ATTRIBUTE_TYPE_UINT32 = 3,
ATTRIBUTE_TYPE_UINT16 = 4,
ATTRIBUTE_TYPE_UINT8 = 5,
ATTRIBUTE_TYPE_INT64 = 6,
ATTRIBUTE_TYPE_INT32 = 7,
ATTRIBUTE_TYPE_INT16 = 8,
ATTRIBUTE_TYPE_INT8 = 9,
};
struct AttributeInfo {
std::string name;
AttributeType type;
size_t byteCount;
};
std::vector<AttributeInfo> attributesInfo;
attributesInfo.reserve(16);
const size_t MAX_BUFFER_SIZE = 4096;
char tmp[MAX_BUFFER_SIZE];
const char* sep = " \t\r";
std::vector<std::string> tokens;
ifs.getline(tmp, MAX_BUFFER_SIZE);
getTokens(tmp, sep, tokens);
if (tokens.empty() || tokens[0] != "ply") {
std::cout << "Error: corrupted file!" << std::endl;
return false;
}
bool isAscii = false;
double version = 1.0;
size_t pointCount = 0;
bool isVertexProperty = true;
while (1) {
if (ifs.eof()) {
std::cout << "Error: corrupted header!" << std::endl;
return false;
}
ifs.getline(tmp, MAX_BUFFER_SIZE);
getTokens(tmp, sep, tokens);
if (tokens.empty() || tokens[0] == "comment") {
continue;
}
if (tokens[0] == "format") {
if (tokens.size() != 3) {
std::cout << "Error: corrupted format info!" << std::endl;
return false;
}
isAscii = tokens[1] == "ascii";
version = atof(tokens[2].c_str());
} else if (tokens[0] == "element") {
if (tokens.size() != 3) {
std::cout << "Error: corrupted element info!" << std::endl;
return false;
}
if (tokens[1] == "vertex") {
pointCount = atoi(tokens[2].c_str());
} else {
isVertexProperty = false;
}
} else if (tokens[0] == "property" && isVertexProperty) {
if (tokens.size() != 3) {
std::cout << "Error: corrupted property info!" << std::endl;
return false;
}
const std::string& propertyType = tokens[1];
const std::string& propertyName = tokens[2];
const size_t attributeIndex = attributesInfo.size();
attributesInfo.resize(attributeIndex + 1);
AttributeInfo& attributeInfo = attributesInfo[attributeIndex];
attributeInfo.name = propertyName;
if (propertyType == "float64") {
attributeInfo.type = ATTRIBUTE_TYPE_FLOAT64;
attributeInfo.byteCount = 8;
} else if (propertyType == "float" || propertyType == "float32") {
attributeInfo.type = ATTRIBUTE_TYPE_FLOAT32;
attributeInfo.byteCount = 4;
} else if (propertyType == "uint64") {
attributeInfo.type = ATTRIBUTE_TYPE_UINT64;
attributeInfo.byteCount = 8;
} else if (propertyType == "uint32") {
attributeInfo.type = ATTRIBUTE_TYPE_UINT32;
attributeInfo.byteCount = 4;
} else if (propertyType == "uint16") {
attributeInfo.type = ATTRIBUTE_TYPE_UINT16;
attributeInfo.byteCount = 2;
} else if (propertyType == "uchar" || propertyType == "uint8") {
attributeInfo.type = ATTRIBUTE_TYPE_UINT8;
attributeInfo.byteCount = 1;
} else if (propertyType == "int64") {
attributeInfo.type = ATTRIBUTE_TYPE_INT64;
attributeInfo.byteCount = 8;
} else if (propertyType == "int32") {
attributeInfo.type = ATTRIBUTE_TYPE_INT32;
attributeInfo.byteCount = 4;
} else if (propertyType == "int16") {
attributeInfo.type = ATTRIBUTE_TYPE_INT16;
attributeInfo.byteCount = 2;
} else if (propertyType == "char" || propertyType == "int8") {
attributeInfo.type = ATTRIBUTE_TYPE_INT8;
attributeInfo.byteCount = 1;
}
} else if (tokens[0] == "end_header") {
break;
}
}
if (version != 1.0) {
std::cout << "Error: non-supported version!" << std::endl;
return false;
}
size_t indexX = PCC_UNDEFINED_INDEX;
size_t indexY = PCC_UNDEFINED_INDEX;
size_t indexZ = PCC_UNDEFINED_INDEX;
size_t indexR = PCC_UNDEFINED_INDEX;
size_t indexG = PCC_UNDEFINED_INDEX;
size_t indexB = PCC_UNDEFINED_INDEX;
size_t indexReflectance = PCC_UNDEFINED_INDEX;
size_t indexFrame = PCC_UNDEFINED_INDEX;
size_t indexNX = PCC_UNDEFINED_INDEX;
size_t indexNY = PCC_UNDEFINED_INDEX;
size_t indexNZ = PCC_UNDEFINED_INDEX;
const size_t attributeCount = attributesInfo.size();
for (size_t a = 0; a < attributeCount; ++a) {
const auto& attributeInfo = attributesInfo[a];
if (
attributeInfo.name == "x"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexX = a;
} else if (
attributeInfo.name == "y"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexY = a;
} else if (
attributeInfo.name == "z"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexZ = a;
} else if (attributeInfo.name == "red" && attributeInfo.byteCount == 1) {
indexR = a;
} else if (
attributeInfo.name == "green" && attributeInfo.byteCount == 1) {
indexG = a;
} else if (
attributeInfo.name == "blue" && attributeInfo.byteCount == 1) {
indexB = a;
} else if (
(attributeInfo.name == "reflectance" || attributeInfo.name == "refc")
&& attributeInfo.byteCount <= 2) {
indexReflectance = a;
} else if (
attributeInfo.name == "frameindex" && attributeInfo.byteCount <= 2) {
indexFrame = a;
} else if (
attributeInfo.name == "nx"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexNX = a;
} else if (
attributeInfo.name == "ny"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexNY = a;
} else if (
attributeInfo.name == "nz"
&& (attributeInfo.byteCount == 8 || attributeInfo.byteCount == 4)) {
indexNZ = a;
}
}
if (
indexX == PCC_UNDEFINED_INDEX || indexY == PCC_UNDEFINED_INDEX
|| indexZ == PCC_UNDEFINED_INDEX) {
std::cout << "Error: missing coordinates!" << std::endl;
return false;
}
withColors = indexR != PCC_UNDEFINED_INDEX && indexG != PCC_UNDEFINED_INDEX
&& indexB != PCC_UNDEFINED_INDEX;
withReflectances = indexReflectance != PCC_UNDEFINED_INDEX;
withFrameIndex = indexFrame != PCC_UNDEFINED_INDEX;
resize(pointCount);
if (isAscii) {
size_t pointCounter = 0;
while (!ifs.eof() && pointCounter < pointCount) {
ifs.getline(tmp, MAX_BUFFER_SIZE);
getTokens(tmp, sep, tokens);
if (tokens.empty()) {
continue;
}
if (tokens.size() < attributeCount) {
return false;
}
auto& position = positions[pointCounter];
position[0] = atof(tokens[indexX].c_str());
position[1] = atof(tokens[indexY].c_str());
position[2] = atof(tokens[indexZ].c_str());
if (hasColors()) {
auto& color = colors[pointCounter];
color[0] = atoi(tokens[indexR].c_str());
color[1] = atoi(tokens[indexG].c_str());
color[2] = atoi(tokens[indexB].c_str());
}
if (hasReflectances()) {
reflectances[pointCounter] =
uint16_t(atoi(tokens[indexReflectance].c_str()));
}
if (hasFrameIndex()) {
frameidx[pointCounter] = uint8_t(atoi(tokens[indexFrame].c_str()));
}
++pointCounter;
}
} else {
for (size_t pointCounter = 0; pointCounter < pointCount && !ifs.eof();
++pointCounter) {
auto& position = positions[pointCounter];
for (size_t a = 0; a < attributeCount && !ifs.eof(); ++a) {
const auto& attributeInfo = attributesInfo[a];
if (a == indexX) {
if (attributeInfo.byteCount == 4) {
float x;
ifs.read(reinterpret_cast<char*>(&x), sizeof(float));
position[0] = x;
} else {
double x;
ifs.read(reinterpret_cast<char*>(&x), sizeof(double));
position[0] = x;
}
} else if (a == indexY) {
if (attributeInfo.byteCount == 4) {
float y;
ifs.read(reinterpret_cast<char*>(&y), sizeof(float));
position[1] = y;
} else {
double y;
ifs.read(reinterpret_cast<char*>(&y), sizeof(double));
position[1] = y;
}
} else if (a == indexZ) {
if (attributeInfo.byteCount == 4) {
float z;
ifs.read(reinterpret_cast<char*>(&z), sizeof(float));
position[2] = z;
} else {
double z;
ifs.read(reinterpret_cast<char*>(&z), sizeof(double));
position[2] = z;
}
} else if (a == indexR && attributeInfo.byteCount == 1) {
auto& color = colors[pointCounter];
ifs.read(reinterpret_cast<char*>(&color[0]), sizeof(uint8_t));
} else if (a == indexG && attributeInfo.byteCount == 1) {
auto& color = colors[pointCounter];
ifs.read(reinterpret_cast<char*>(&color[1]), sizeof(uint8_t));
} else if (a == indexB && attributeInfo.byteCount == 1) {
auto& color = colors[pointCounter];
ifs.read(reinterpret_cast<char*>(&color[2]), sizeof(uint8_t));
} else if (a == indexReflectance && attributeInfo.byteCount <= 2) {
if (attributeInfo.byteCount == 1) {
uint8_t reflectance;
ifs.read(reinterpret_cast<char*>(&reflectance), sizeof(uint8_t));
reflectances[pointCounter] = reflectance;
} else {
auto& reflectance = reflectances[pointCounter];
ifs.read(
reinterpret_cast<char*>(&reflectance), sizeof(uint16_t));
}
} else if (a == indexFrame && attributeInfo.byteCount <= 2) {
if (attributeInfo.byteCount == 1) {
auto& findex = frameidx[pointCounter];
ifs.read(reinterpret_cast<char*>(&findex), sizeof(uint8_t));
} else {
uint16_t findex;
ifs.read(reinterpret_cast<char*>(&findex), sizeof(uint16_t));
frameidx[pointCounter] = uint8_t(findex);
}
} else {
char buffer[128];
ifs.read(buffer, attributeInfo.byteCount);
}
}
}
}
return true;
}
void convertRGBToYUV()
{ // BT709
for (auto& color : colors) {
......
......@@ -40,6 +40,7 @@
#include "PCCTMC3Encoder.h"
#include "PCCTMC3Decoder.h"
#include "constants.h"
#include "ply.h"
#include "program_options_lite.h"
#include "io_tlv.h"
#include "version.h"
......@@ -877,7 +878,7 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock)
{
std::string srcName{expandNum(params->uncompressedDataPath, frameNum)};
PCCPointSet3 pointCloud;
if (!pointCloud.read(srcName) || pointCloud.getPointCount() == 0) {
if (!ply::read(srcName, pointCloud) || pointCloud.getPointCount() == 0) {
cout << "Error: can't open input file!" << endl;
return -1;
}
......@@ -945,7 +946,7 @@ SequenceEncoder::compressOneFrame(Stopwatch* clock)
}
std::string recName{expandNum(params->reconstructedDataPath, frameNum)};
reconPointCloud->write(recName, !params->outputBinaryPly);
ply::write(*reconPointCloud, recName, !params->outputBinaryPly);
}
return 0;
......@@ -972,13 +973,13 @@ SequenceEncoder::onPostRecolour(const PCCPointSet3& cloud)
// todo(df): stop the clock
if (params->colorTransform != COLOR_TRANSFORM_RGB_TO_YCBCR) {
cloud.write(plyName);
ply::write(cloud, plyName, !params->outputBinaryPly);
return;
}
PCCPointSet3 tmpCloud(cloud);
tmpCloud.convertYUVToRGB();
tmpCloud.write(plyName);
ply::write(tmpCloud, plyName, !params->outputBinaryPly);
}
//============================================================================
......@@ -1053,7 +1054,7 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud)
// Dump the decoded colour using the pre inverse scaled geometry
if (!params->preInvScalePath.empty()) {
std::string filename{expandNum(params->preInvScalePath, frameNum)};
pointCloud.write(params->preInvScalePath, !params->outputBinaryPly);
ply::write(pointCloud, params->preInvScalePath, !params->outputBinaryPly);
}
decoder.inverseQuantization(pointCloud);
......@@ -1061,7 +1062,7 @@ SequenceDecoder::onOutputCloud(const PCCPointSet3& decodedPointCloud)
clock->stop();
std::string decName{expandNum(params->reconstructedDataPath, frameNum)};
if (!pointCloud.write(decName, !params->outputBinaryPly)) {
if (!ply::write(pointCloud, decName, !params->outputBinaryPly)) {
cout << "Error: can't open output file!" << endl;
}
......
......@@ -45,6 +45,7 @@
#include "osspecific.h"
#include "partitioning.h"
#include "pcc_chrono.h"
#include "ply.h"
namespace pcc {
......
/* The copyright in this software is being made available under the BSD
* Licence, included below. This software may be subject to other third
* party and contributor rights, including patent rights, and no such
* rights are granted under this licence.
*
* Copyright (c) 2017-2018, ISO/IEC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the ISO/IEC nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "ply.h"
#include "PCCMisc.h"
#include "PCCPointSet.h"
#include <fstream>
#include <string>
#include <vector>
namespace pcc {
//============================================================================
static bool
compareSeparators(char aChar, const char* const sep)
{
int i = 0;
while (sep[i] != '\0') {
if (aChar == sep[i])
return false;
i++;
}
return true;
}
//============================================================================
static bool
getTokens(
const char* str, const char* const sep, std::vector<std::string>& tokens)
{
if (!tokens.empty())
tokens.clear();
std::string buf = "";
size_t i = 0;
size_t length = ::strlen(str);
while (i < length) {
if (compareSeparators(str[i], sep)) {
buf += str[i];
} else if (buf.length() > 0) {
tokens.push_back(buf);
buf = "";
}
i++;
}
if (!buf.empty())
tokens.push_back(buf);
return !tokens.empty();
}
//============================================================================
bool
ply::write(
const PCCPointSet3& cloud, const std::string& fileName, bool asAscii)
{
std::ofstream fout(fileName, std::ofstream::out);