利用PCL库函数解析OBJ
利用pcl库函数解析obj的方法有bug,解析过来的时候纹理坐标确实,也就是缺少RGB信息。
这是PCL库的bug,见参考1 参考2
因此,只好自己读懂obj的格式后,自己写一下解析函数。
利用meshlab转化
转化后也缺失了RGB信息
自己解析OBJ
OBJ格式解析
obj文件如下:
mtllib Tile_+003_+002_+000.mtl
v -9.67653046e+001 -2.54838577e+002 2.69715271e+001
v -9.76558914e+001 -2.55053558e+002 2.70283375e+001
v -9.92712021e+001 -2.52890640e+002 2.68646297e+001
...
vt 0.11269743 0.9384023
vt 0.11295749 0.93755996
vt 0.11307663 0.93885833
vt 0.11191387 0.93955261
vt 0.11083307 0.93842274
vt 0.1146261 0.93710536
vt 0.11188133 0.93438381
vt 0.11429998 0.93874836
...
usemtl Tile_+003_+002_+000_0
f 4/59167 8/59168 3/59169
f 38/59173 32/59170 3/59169
f 106240/59180 64527/59188 2/59199
f 38/59173 35/59196 34/59198
f 3/59169 32/59170 4/59167
f 31/59192 10/59204 20/59217
f 21/151853 12/151854 49010/151855
f 13/151879 14/151881 64452/151880
f 18/151857 17/151862 15/151867
f 23/140584 22/140585 24/140586
f 113546/151859 25/151866 17/151862
f 25/151866 113546/151859 105583/151870
f 30275/59265 19/59229 105583/59286
f 30275/59265 28/59248 19/59229
...
v:vertices(顶点坐标)
vt:vertices texture(顶点纹理坐标,这里的坐标系与OPENCV的坐标系不一样,见下图,左边为这里的坐标系,右边为OpenCV的坐标系)
f:face(面,一个面对应三个顶点)其格式如下(索引从1开始):
f 顶点索引/顶点纹理索引 顶点索引/顶点纹理索引 顶点索引/顶点纹理索引
f 1/1 2/2 3/3
转换代码
该代码并不通用,只试用于我的这种格式,其他的格式需要相应修改,仅供参考。
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/PolygonMesh.h>
#include <pcl/point_cloud.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <string>
#include <cstring>
#include <fstream>
#include <sstream>
#include <map>
#include <algorithm>
#include <bits/stdc++.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
using namespace cv;
using namespace std;
struct _xyz{
public:
float x;
float y;
float z;
unsigned char r;
unsigned char g;
unsigned char b;
public:
_xyz():x(0.0),y(0.0),z(0.0),r(255),g(255),b(255)
{
}
};
struct _xy{
float x;
float y;
};
int WIDTH,HEIGHT;
Mat img;
vector<int> split(string s,string delim)
{
vector<int> a;
int index = 0;
for(unsigned int i = 0;i < s.size();i++)
{
if(delim[0] == s[i] || i == s.size() - 1)
{
istringstream ss;
if(i == s.size() - 1)
ss.str(s.substr(index,i + 1 - index));
else
ss.str(s.substr(index,i - index));
int tmp;
ss >> tmp;
a.push_back(tmp);
index = i + 1;
}
}
return a;
}
int main(int argc,char** argv)
{
ifstream file(argv[1],ios::in);
if(!file.is_open())
{
cout << "open file error!" << endl;
return -1;
}
string line;
getline(file,line);
vector<_xyz> xyz;
vector<_xy> rgb_index;
map<int,int> Imap;
while(getline(file,line) && line.size() >0)
{
istringstream ss(line);
string type;
ss >> type;
if(type == "v")
{
_xyz item;
ss >> item.x >> item.y >> item.z;
xyz.push_back(item);
}
else if(type == "vt")
{
_xy item;
ss >> item.x >> item.y;
rgb_index.push_back(item);
}
else if(type == "mtllib")
{
continue;
}
else if(type == "usemtl")
{
string img_path;
ss >> img_path;
if(img_path.find("untextured") != string::npos)
continue;
img_path+=".jpg";
string img_path1(argv[1]);
size_t pos = img_path1.rfind("/");
img_path1.replace(pos+1,img_path1.size() - pos-1,img_path);
img = imread(img_path1,1);
if(img.data == NULL)
{
cout << "can not find the texture file!" << endl;
return -1;
}
WIDTH = img.cols;
HEIGHT = img.rows;
}
else if(type == "f")
{
string s1,s2,s3;
ss >> s1 >> s2 >> s3;
auto s1_v = split(s1,"/");
auto s2_v = split(s2,"/");
auto s3_v = split(s3,"/");
Imap[s1_v[0]] = s1_v[1];
Imap[s2_v[0]] = s2_v[1];
Imap[s3_v[0]] = s3_v[1];
}
}
for(int i = 0;i < xyz.size();++i)
{
int index = Imap[i+1];
_xy tmp = rgb_index[index-1];
int x = tmp.x * WIDTH;
int y = tmp.y * HEIGHT;
y = HEIGHT - y;
int b = img.at<Vec3b>(y, x)[0];
int g = img.at<Vec3b>(y, x)[1];
int r = img.at<Vec3b>(y, x)[2];
xyz[i].r = r;
xyz[i].g = g;
xyz[i].b = b;
}
// get label
// pcl::PointCloud<pcl::PointXYZL>::Ptr cloud_xyzl (new pcl::PointCloud<pcl::PointXYZL>());
pcl::PLYReader reader;
// string label_file(argv[1]);
// label_file.replace(label_file.find("good"),4,"good/data");
// label_file.replace(label_file.rfind(".obj"),4,".ply");
reader.read<pcl::PointXYZL>(label_file,*cloud_xyzl);
// if (pcl::io::loadPLYFile<pcl::PointXYZL>(label_file, *cloud_xyzl) == -1) {
// PCL_ERROR("Couldnot read file.\n");
// return -1;
// }
// assert(cloud_xyzl->width * cloud_xyzl->height == xyz.size());
pcl::PointCloud<pcl::PointXYZRGBL>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBL>());
cloud->resize(xyz.size());
for(int i = 0;i < xyz.size();++i)
{
cloud->points[i].x = xyz[i].x;
cloud->points[i].y = xyz[i].y;
cloud->points[i].z = xyz[i].z;
cloud->points[i].r = xyz[i].r;
cloud->points[i].g = xyz[i].g;
cloud->points[i].b = xyz[i].b;
// cloud->points[i].label = cloud_xyzl->points[i].label;
}
pcl::PLYWriter writer;
string save_name(argv[1]);
save_name.replace(save_name.rfind(".obj"),4,".ply");
writer.write<pcl::PointXYZRGBL>(save_name,*cloud,false,false);
cout << "convert finished" << endl;
return 0;
}
编译环境
OpenCV3.4.0 + PCL1.9
CMakeLists.txt 如下:
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
project(ObjToPly)
find_package(PCL 1.7 REQUIRED)
find_package(OpenCV REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
include_directories(/usr/include)
link_directories(${PCL_LIBRARY_DIRS})
link_directories(${OpenCV_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_definitions(-std=c++11)
add_executable(2_objTopcd 2_objTopcd.cpp)
target_link_libraries(2_objTopcd
${PCL_LIBRARIES}
${OpenCV_LIBRARIES}
)
可视化效果
左边图为利用meshlab打开的obj格式图,右图为利用meshlab打开的ply图。