OpenDroneMap-ODM/modules/odm_filterpoints/src/FloatPlyReader.cpp

455 wiersze
13 KiB
C++

/******************************************************************************
* Copyright (c) 2015, Peter J. Gadomski <pete.gadomski@gmail.com>
*
* 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 Hobu, Inc. or Flaxen Geo Consulting 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 OWNER 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.
****************************************************************************/
// Modified to not cast to double and to use certain type identifier ("float" vs "float32")
#include "FloatPlyReader.hpp"
#include <sstream>
#include <pdal/PDALUtils.hpp>
#include <pdal/PointView.hpp>
#include <pdal/util/IStream.hpp>
namespace pdal
{
FloatPlyReader::FloatPlyReader() : m_vertexElt(nullptr)
{}
std::string FloatPlyReader::readLine()
{
m_line.clear();
if (m_lines.size())
{
m_line = m_lines.top();
m_lines.pop();
}
else
{
do
{
std::getline(*m_stream, m_line);
} while (m_line.empty() && m_stream->good());
}
Utils::trimTrailing(m_line);
m_linePos = Utils::extract(m_line, 0,
[](char c){ return !std::isspace(c); });
return std::string(m_line, 0, m_linePos);
}
void FloatPlyReader::pushLine()
{
m_lines.push(m_line);
}
std::string FloatPlyReader::nextWord()
{
std::string s;
std::string::size_type cnt = Utils::extractSpaces(m_line, m_linePos);
m_linePos += cnt;
if (m_linePos == m_line.size())
return s;
cnt = Utils::extract(m_line, m_linePos,
[](char c){ return !std::isspace(c); });
s = std::string(m_line, m_linePos, cnt);
m_linePos += cnt;
return s;
}
void FloatPlyReader::extractMagic()
{
std::string first = readLine();
if (first != "ply")
throwError("File isn't a PLY file. 'ply' not found.");
if (m_linePos != m_line.size())
throwError("Text found following 'ply' keyword.");
}
void FloatPlyReader::extractEnd()
{
std::string first = readLine();
if (first != "end_header")
throwError("'end_header' expected but found line beginning with '" +
first + "' instead.");
if (m_linePos != m_line.size())
throwError("Text found following 'end_header' keyword.");
}
void FloatPlyReader::extractFormat()
{
std::string word = readLine();
if (word != "format")
throwError("Expected format line not found in PLY file.");
word = nextWord();
if (word == "ascii")
m_format = Format::Ascii;
else if (word == "binary_big_endian")
m_format = Format::BinaryBe;
else if (word == "binary_little_endian")
m_format = Format::BinaryLe;
else
throwError("Unrecognized PLY format: '" + word + "'.");
word = nextWord();
if (word != "1.0")
throwError("Unsupported PLY version: '" + word + "'.");
}
Dimension::Type FloatPlyReader::getType(const std::string& name)
{
static std::map<std::string, Dimension::Type> types =
{
{ "int8", Dimension::Type::Signed8 },
{ "uint8", Dimension::Type::Unsigned8 },
{ "int16", Dimension::Type::Signed16 },
{ "uint16", Dimension::Type::Unsigned16 },
{ "int32", Dimension::Type::Signed32 },
{ "uint32", Dimension::Type::Unsigned32 },
{ "float32", Dimension::Type::Float },
{ "float64", Dimension::Type::Double },
{ "char", Dimension::Type::Signed8 },
{ "uchar", Dimension::Type::Unsigned8 },
{ "short", Dimension::Type::Signed16 },
{ "ushort", Dimension::Type::Unsigned16 },
{ "int", Dimension::Type::Signed32 },
{ "uint", Dimension::Type::Unsigned32 },
{ "float", Dimension::Type::Float },
{ "double", Dimension::Type::Double }
};
try
{
return types.at(name);
}
catch (std::out_of_range&)
{}
return Dimension::Type::None;
}
void FloatPlyReader::extractProperty(Element& element)
{
std::string word = nextWord();
Dimension::Type type = getType(word);
if (type != Dimension::Type::None)
{
std::string name = nextWord();
if (name.empty())
throwError("No name for property of element '" +
element.m_name + "'.");
element.m_properties.push_back(
std::unique_ptr<Property>(new SimpleProperty(name, type)));
}
else if (word == "list")
{
if (element.m_name == "vertex")
throwError("List properties are not supported for the 'vertex' "
"element.");
word = nextWord();
Dimension::Type countType = getType(word);
if (countType == Dimension::Type::None)
throwError("No valid count type for list property of element '" +
element.m_name + "'.");
word = nextWord();
Dimension::Type listType = getType(word);
if (listType == Dimension::Type::None)
throwError("No valid list type for list property of element '" +
element.m_name + "'.");
std::string name = nextWord();
if (name.empty())
throwError("No name for property of element '" +
element.m_name + "'.");
element.m_properties.push_back(
std::unique_ptr<Property>(new ListProperty(name, countType,
listType)));
}
else
throwError("Invalid property type '" + word + "'.");
}
void FloatPlyReader::extractProperties(Element& element)
{
while (true)
{
std::string word = readLine();
if (word == "comment" || word == "obj_info")
continue;
else if (word == "property")
extractProperty(element);
else
{
pushLine();
break;
}
}
}
bool FloatPlyReader::extractElement()
{
std::string word = readLine();
if (word == "comment" || word == "obj_info")
return true;
else if (word == "end_header")
{
pushLine();
return false;
}
else if (word == "element")
{
std::string name = nextWord();
if (name.empty())
throwError("Missing element name.");
long count = std::stol(nextWord());
if (count < 0)
throwError("Invalid count for element '" + name + "'.");
m_elements.emplace_back(name, count);
extractProperties(m_elements.back());
return true;
}
throwError("Invalid keyword '" + word + "' when expecting an element.");
return false; // quiet compiler
}
void FloatPlyReader::extractHeader()
{
m_elements.clear();
extractMagic();
extractFormat();
while (extractElement())
;
extractEnd();
m_dataPos = m_stream->tellg();
for (Element& elt : m_elements)
if (elt.m_name == "vertex")
m_vertexElt = &elt;
if (!m_vertexElt)
throwError("Can't read PLY file without a 'vertex' element.");
}
std::string FloatPlyReader::getName() const
{
return "FloatPlyReader";
}
void FloatPlyReader::initialize()
{
m_stream = Utils::openFile(m_filename, true);
if (!m_stream)
throwError("Couldn't open '" + m_filename + "'.");
extractHeader();
Utils::closeFile(m_stream);
m_stream = nullptr;
}
void FloatPlyReader::addDimensions(PointLayoutPtr layout)
{
// Override XYZ
// layout->registerDim(Dimension::Id::X);
// layout->registerDim(Dimension::Id::Y);
// layout->registerDim(Dimension::Id::Z);
for (auto& elt : m_elements)
{
if (elt.m_name == "vertex")
{
for (auto& prop : elt.m_properties)
{
auto vprop = static_cast<SimpleProperty *>(prop.get());
layout->registerOrAssignDim(vprop->m_name, vprop->m_type);
vprop->setDim(
layout->registerOrAssignDim(vprop->m_name, vprop->m_type));
}
return;
}
}
throwError("No 'vertex' element in header.");
}
bool FloatPlyReader::readProperty(Property *prop, PointRef& point)
{
if (!m_stream->good())
return false;
prop->read(m_stream, m_format, point);
return true;
}
void FloatPlyReader::SimpleProperty::read(std::istream *stream,
FloatPlyReader::Format format, PointRef& point)
{
if (format == Format::Ascii)
{
double d;
*stream >> d;
point.setField(m_dim, d);
}
else if (format == Format::BinaryLe)
{
ILeStream in(stream);
Everything e = Utils::extractDim(in, m_type);
point.setField(m_dim, m_type, &e);
}
else if (format == Format::BinaryBe)
{
IBeStream in(stream);
Everything e = Utils::extractDim(in, m_type);
point.setField(m_dim, m_type, &e);
}
}
// Right now we don't support list properties for point data. We just
// read the data and throw it away.
void FloatPlyReader::ListProperty::read(std::istream *stream,
FloatPlyReader::Format format, PointRef& point)
{
if (format == Format::Ascii)
{
size_t cnt;
*stream >> cnt;
double d;
while (cnt--)
*stream >> d;
}
else if (format == Format::BinaryLe)
{
ILeStream istream(stream);
Everything e = Utils::extractDim(istream, m_countType);
size_t cnt = (size_t)Utils::toDouble(e, m_countType);
cnt *= Dimension::size(m_listType);
istream.seek(cnt, std::ios_base::cur);
}
else if (format == Format::BinaryBe)
{
IBeStream istream(stream);
Everything e = Utils::extractDim(istream, m_countType);
size_t cnt = (size_t)Utils::toDouble(e, m_countType);
cnt *= Dimension::size(m_listType);
istream.seek(cnt, std::ios_base::cur);
}
}
void FloatPlyReader::readElement(Element& elt, PointRef& point)
{
for (auto& prop : elt.m_properties)
if (!readProperty(prop.get(), point))
throwError("Error reading data for point/element " +
std::to_string(point.pointId()) + ".");
}
void FloatPlyReader::ready(PointTableRef table)
{
m_stream = Utils::openFile(m_filename, true);
if (m_stream)
m_stream->seekg(m_dataPos);
for (Element& elt : m_elements)
{
if (&elt == m_vertexElt)
break;
// We read an element into point 0. Since the element's properties
// weren't registered as dimensions, we'll try to write the data
// to a NULL dimension, which is a noop.
// This essentially just gets us to the vertex element.
// In binary mode, this is all silliness, since we should be able
// to seek right where we want to go, but in text mode, you've got
// to go through the data.
PointRef point(table, 0);
for (PointId idx = 0; idx < elt.m_count; ++idx)
readElement(elt, point);
}
m_index = 0;
}
bool FloatPlyReader::processOne(PointRef& point)
{
if (m_index < m_vertexElt->m_count)
{
readElement(*m_vertexElt, point);
m_index++;
return true;
}
return false;
}
// We're just reading the vertex element here.
point_count_t FloatPlyReader::read(PointViewPtr view, point_count_t num)
{
point_count_t cnt(0);
PointRef point(view->point(0));
for (PointId idx = 0; idx < m_vertexElt->m_count && idx < num; ++idx)
{
point.setPointId(idx);
processOne(point);
cnt++;
}
return cnt;
}
void FloatPlyReader::done(PointTableRef table)
{
Utils::closeFile(m_stream);
}
} // namespace pdal