Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved obj file parsing efficiency. Make parsing robust against situations where there are more normals than points. Added unit tests. #2450

Merged
merged 7 commits into from
Sep 21, 2018
56 changes: 32 additions & 24 deletions io/src/obj_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,6 @@ pcl::OBJReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c
// bool material_found = false;
std::vector<std::string> material_files;
std::size_t nr_point = 0;
std::vector<std::string> st;

try
{
Expand All @@ -385,40 +384,42 @@ pcl::OBJReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c
if (line == "")
continue;

// Tokenize the line
std::stringstream sstream (line);
sstream.imbue (std::locale::classic ());
line = sstream.str ();
// Trim the line
boost::trim (line);
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);

// Ignore comments
if (st.at (0) == "#")
if (line[0] == '#')
continue;

// Vertex
if (st.at (0) == "v")
// Vertex, vertex texture or vertex normal
if (line[0] == 'v')
{
++nr_point;
continue;
}
// Vertex (v)
if ((line[1] == ' ')) {
++nr_point;
continue;
}

// Vertex texture
if ((st.at (0) == "vt") && !vertex_texture_found)
{
vertex_texture_found = true;
continue;
}
// Vertex texture (vt)
else if ((line[1] == 't') && !vertex_texture_found)
{
vertex_texture_found = true;
continue;
}

// Vertex normal
if ((st.at (0) == "vn") && !vertex_normal_found)
{
vertex_normal_found = true;
continue;
// Vertex normal (vn)
else if ((line[1] == 'n') && !vertex_normal_found)
{
vertex_normal_found = true;
continue;
}
}

// Material library, skip for now!
if (st.at (0) == "mtllib")
if (line.substr (0, 6) == "mtllib")
{
std::vector<std::string> st;
boost::split(st, line, boost::is_any_of("\t\r "), boost::token_compress_on);
material_files.push_back (st.at (1));
continue;
}
Expand Down Expand Up @@ -588,6 +589,13 @@ pcl::OBJReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
// Vertex normal
if (st[0] == "vn")
{
if (normal_idx >= cloud.width)
{
if (normal_idx == cloud.width)
PCL_WARN ("[pcl:OBJReader] Too many vertex normals (expected %d), skipping remaining normals.\n", cloud.width, normal_idx + 1);
++normal_idx;
continue;
}
try
{
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
Expand Down
114 changes: 113 additions & 1 deletion test/io/test_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/ascii_io.h>
#include <pcl/io/obj_io.h>
#include <fstream>
#include <locale>
#include <stdexcept>
Expand Down Expand Up @@ -156,6 +157,8 @@ TEST (PCL, ComplexPCDFileASCII)
EXPECT_EQ (val[30], 0);
EXPECT_EQ (val[31], 0);
EXPECT_EQ (val[32], 0);

remove ("complex_ascii.pcd");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -255,6 +258,8 @@ TEST (PCL, AllTypesPCDFile)
EXPECT_FLOAT_EQ (float (b8), -250.05f);
memcpy (&b8, &blob.data[blob.fields[7].offset + sizeof (double)], sizeof (double));
EXPECT_FLOAT_EQ (float (b8), -251.05f);

remove ("all_types.pcd");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -652,6 +657,10 @@ TEST (PCL, IO)
EXPECT_FLOAT_EQ (cloud.points[0].y, first.y); // test for fromPCLPointCloud2 ()
EXPECT_FLOAT_EQ (cloud.points[0].z, first.z); // test for fromPCLPointCloud2 ()
EXPECT_FLOAT_EQ (cloud.points[0].intensity, first.intensity); // test for fromPCLPointCloud2 ()

remove ("test_pcl_io_ascii.pcd");
remove ("test_pcl_io_binary.pcd");
remove ("test_pcl_io.pcd");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -720,6 +729,8 @@ TEST (PCL, PCDReaderWriter)
EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].y, last.y); // test for fromPCLPointCloud2 ()
EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].z, last.z); // test for fromPCLPointCloud2 ()
EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].intensity, last.intensity); // test for fromPCLPointCloud2 ()

remove ("test_pcl_io.pcd");
}

TEST (PCL, PCDReaderWriterASCIIColorPrecision)
Expand Down Expand Up @@ -763,10 +774,13 @@ TEST (PCL, PCDReaderWriterASCIIColorPrecision)
EXPECT_EQ (cloud[i].g, cloud_ascii[i].g);
EXPECT_EQ (cloud[i].b, cloud_ascii[i].b);
}

remove ("temp_binary_color.pcd");
remove ("temp_ascii_color.pcd");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, ASCIIReader)
TEST (PCL, ASCIIRead)
{
PointCloud<PointXYZI> cloud, rcloud;

Expand Down Expand Up @@ -806,6 +820,98 @@ TEST (PCL, ASCIIReader)
EXPECT_FLOAT_EQ(cloud.points[i].intensity, rcloud.points[i].intensity);
}

remove ("test_pcd.txt");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(PCL, OBJRead)
{
std::ofstream fs;
fs.open ("test_obj.obj");
fs << "# Blender v2.79 (sub 4) OBJ File: ''\n"
"mtllib test_obj.mtl\n"
"o Cube_Cube.001\n"
"v -1.000000 -1.000000 1.000000\n"
"v -1.000000 1.000000 1.000000\n"
"v -1.000000 -1.000000 -1.000000\n"
"v -1.000000 1.000000 -1.000000\n"
"v 1.000000 -1.000000 1.000000\n"
"v 1.000000 1.000000 1.000000\n"
"v 1.000000 -1.000000 -1.000000\n"
"v 1.000000 1.000000 -1.000000\n"
"vn -1.0000 0.0000 0.0000\n"
"vn 0.0000 0.0000 -1.0000\n"
"vn 1.0000 0.0000 0.0000\n"
"vn 0.0000 0.0000 1.0000\n"
"vn 0.0000 -1.0000 0.0000\n"
"vn 0.0000 1.0000 0.0000\n"
"# Redundant vertex normal to test error handling\n"
"vn 0.0000 0.0000 0.0000\n"
"usemtl None\n"
"s off\n"
"f 1//1 2//1 4//1 3//1\n"
"f 3//2 4//2 8//2 7//2\n"
"f 7//3 8//3 6//3 5//3\n"
"f 5//4 6//4 2//4 1//4\n"
"f 3//5 7//5 5//5 1//5\n"
"f 8//6 4//6 2//6 6//6\n";

fs.close ();
fs.open ("test_obj.mtl");
fs << "# Blender MTL File: 'None'\n"
"# Material Count: 1\n"
"newmtl None\n"
"Ns 0\n"
"Ka 0.000000 0.000000 0.000000\n"
"Kd 0.8 0.8 0.8\n"
"Ks 0.8 0.8 0.8\n"
"d 1\n"
"illum 2\n";

fs.close ();
SergioRAgostinho marked this conversation as resolved.
Show resolved Hide resolved

pcl::PCLPointCloud2 blob;
pcl::OBJReader objreader = pcl::OBJReader();
int res = objreader.read ("test_obj.obj", blob);
EXPECT_NE (int (res), -1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here you want a result which is specifically 0.

EXPECT_EQ (blob.width, 8);
EXPECT_EQ (blob.height, 1);
EXPECT_EQ (blob.is_dense, true);
EXPECT_EQ (blob.data.size (), 8 * 6 * 4); // 8 verts, 6 values per vertex (vx,vy,vz,vnx,vny,vnz), 4 byte per value

// Check fields
EXPECT_EQ (blob.fields[0].name, "x");
EXPECT_EQ (blob.fields[0].offset, 0);
EXPECT_EQ (blob.fields[0].count, 1);
EXPECT_EQ (blob.fields[0].datatype, pcl::PCLPointField::FLOAT32);

EXPECT_EQ (blob.fields[1].name, "y");
EXPECT_EQ (blob.fields[1].offset, 4 * 1);
EXPECT_EQ (blob.fields[1].count, 1);
EXPECT_EQ (blob.fields[1].datatype, pcl::PCLPointField::FLOAT32);

EXPECT_EQ (blob.fields[2].name, "z");
EXPECT_EQ (blob.fields[2].offset, 4 * 2);
EXPECT_EQ (blob.fields[2].count, 1);
EXPECT_EQ (blob.fields[2].datatype, pcl::PCLPointField::FLOAT32);

EXPECT_EQ (blob.fields[3].name, "normal_x");
EXPECT_EQ (blob.fields[3].offset, 4 * 3);
EXPECT_EQ (blob.fields[3].count, 1);
EXPECT_EQ (blob.fields[3].datatype, pcl::PCLPointField::FLOAT32);

EXPECT_EQ (blob.fields[4].name, "normal_y");
EXPECT_EQ (blob.fields[4].offset, 4 * 4);
EXPECT_EQ (blob.fields[4].count, 1);
EXPECT_EQ (blob.fields[4].datatype, pcl::PCLPointField::FLOAT32);

EXPECT_EQ (blob.fields[5].name, "normal_z");
EXPECT_EQ (blob.fields[5].offset, 4 * 5);
EXPECT_EQ (blob.fields[5].count, 1);
EXPECT_EQ (blob.fields[5].datatype, pcl::PCLPointField::FLOAT32);

remove ("test_obj.obj");
remove ("test_obj.mtl");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -858,6 +964,8 @@ TEST (PCL, ExtendedIO)
ASSERT_EQ (cloud.points[0].histogram[i], i);
ASSERT_EQ (cloud.points[1].histogram[i], 33-i);
}

remove ("v.pcd");
}


Expand Down Expand Up @@ -1057,6 +1165,8 @@ TEST (PCL, LZFExtended)
EXPECT_EQ (cloud2.points[i].normal_z, cloud.points[i].normal_z);
EXPECT_EQ (cloud2.points[i].rgb, cloud.points[i].rgb);
}

remove ("test_pcl_io_compressed.pcd");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1202,6 +1312,8 @@ TEST (PCL, Locale)
catch (const std::exception&)
{
}

remove ("test_pcl_io_ascii.pcd");
#endif
}

Expand Down