texture mapping纹理贴图

文章介绍了如何利用PCL库和OpenMVS库对3D模型进行纹理映射。通过读取PLY模型、彩色图片及相机内外参数,PCL库可以生成带有多个纹理的obj模型,而OpenMVS库则能创建包含png纹理的模型。最后,通过C++和Python代码示例展示了模型的可视化方法。
摘要由CSDN通过智能技术生成

输入数据包括ply模型、彩色图片和对应的相机内外参数。
在这里插入图片描述
其中txt文件中数据格式如下:

translation_x translation_y translation_z
rotation_00 rotation_01 rotation_02
rotation_10 rotation_11 rotation_12
rotation_20 rotation_21 rotation_22
fx
fy
cx
cy
image_height
image_width

使用PCL库

#include <fstream>
#include <iostream>
#include <sstream>

#include <pcl/io/pcd_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/texture_mapping.h>
#include <pcl/visualization/pcl_visualizer.h>


std::ifstream& go_to_line(std::ifstream& file, unsigned int num)
{
	file.seekg(std::ios::beg);
	for (int i = 0; i < num - 1; ++i) 
		file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	return file;
}


bool read_cam_pose_file(std::string filename, pcl::TextureMapping<pcl::PointXYZ>::Camera& cam) 
{
	std::ifstream myReadFile;
	myReadFile.open(filename.c_str(), ios::in);
	if (!myReadFile.is_open())
	{
		PCL_ERROR("Error opening file %d\n", filename.c_str());
		return false;
	}
	myReadFile.seekg(ios::beg);

	float val;

	go_to_line(myReadFile, 1);
	myReadFile >> val;
	cam.pose(0, 3) = val;  // TX
	myReadFile >> val;
	cam.pose(1, 3) = val;  // TY
	myReadFile >> val;
	cam.pose(2, 3) = val;  // TZ

	go_to_line(myReadFile, 2);
	myReadFile >> val;
	cam.pose(0, 0) = val;
	myReadFile >> val;
	cam.pose(0, 1) = val;
	myReadFile >> val;
	cam.pose(0, 2) = val;

	myReadFile >> val;
	cam.pose(1, 0) = val;
	myReadFile >> val;
	cam.pose(1, 1) = val;
	myReadFile >> val;
	cam.pose(1, 2) = val;

	myReadFile >> val;
	cam.pose(2, 0) = val;
	myReadFile >> val;
	cam.pose(2, 1) = val;
	myReadFile >> val;
	cam.pose(2, 2) = val;

	cam.pose(3, 0) = 0.0;
	cam.pose(3, 1) = 0.0;
	cam.pose(3, 2) = 0.0;
	cam.pose(3, 3) = 1.0;  // Scale

	go_to_line(myReadFile, 5);
	myReadFile >> val;
	cam.focal_length_w = val;
	myReadFile >> val;
	cam.focal_length_h = val;
	myReadFile >> val;
	cam.center_w = val;
	myReadFile >> val;
	cam.center_h = val;
	myReadFile >> val;
	cam.height = val;
	myReadFile >> val;
	cam.width = val;

	myReadFile.close();
	return true;
}


int main(int argc, char** argv) 
{
	pcl::PolygonMesh triangles;
	pcl::io::loadPolygonFilePLY("./data/obj.ply", triangles);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::fromPCLPointCloud2(triangles.cloud, *cloud);

	// Create the texturemesh object that will contain our UV-mapped mesh
	pcl::TextureMesh mesh;
	mesh.cloud = triangles.cloud;
	std::vector<pcl::Vertices> polygon_1;

	// push faces into the texturemesh object
	polygon_1.resize(triangles.polygons.size());
	for (size_t i = 0; i < triangles.polygons.size(); ++i) 
		polygon_1[i] = triangles.polygons[i];

	mesh.tex_polygons.push_back(polygon_1);

	// Load textures and cameras poses and intrinsics
	pcl::texture_mapping::CameraVector my_cams;

	const int nums = 3;
	std::string prefix = "./data/color";
	std::vector<std::string> filenames;
	for (size_t i = 0; i < nums; i++)
	{
		std::stringstream ss;
		ss << std::setw(3) << std::setfill('0') << i;
		std::string filename = prefix + ss.str() + ".txt";
		filenames.push_back(filename);
	}

	for (int i = 0; i < nums; ++i)
	{
		pcl::TextureMapping<pcl::PointXYZ>::Camera cam;
		read_cam_pose_file(filenames[i], cam);
		cam.texture_file = filenames[i].substr(0, filenames[i].size() - 3) + "png";
		my_cams.push_back(cam);
	}

	// Create materials for each texture (and one extra for occluded faces)
	mesh.tex_materials.resize(my_cams.size() + 1);
	for (int i = 0; i <= my_cams.size(); ++i) 
	{
		pcl::TexMaterial mesh_material;
		mesh_material.tex_Ka.r = 0.2f;
		mesh_material.tex_Ka.g = 0.2f;
		mesh_material.tex_Ka.b = 0.2f;

		mesh_material.tex_Kd.r = 0.8f;
		mesh_material.tex_Kd.g = 0.8f;
		mesh_material.tex_Kd.b = 0.8f;

		mesh_material.tex_Ks.r = 1.0f;
		mesh_material.tex_Ks.g = 1.0f;
		mesh_material.tex_Ks.b = 1.0f;

		mesh_material.tex_d = 1.0f;
		mesh_material.tex_Ns = 75.0f;
		mesh_material.tex_illum = 2;

		std::stringstream tex_name;
		tex_name << "material_" << i;
		tex_name >> mesh_material.tex_name;

		if (i < my_cams.size())
			mesh_material.tex_file = my_cams[i].texture_file;
		else
			mesh_material.tex_file = "occluded.jpg";

		mesh.tex_materials[i] = mesh_material;
	}

	// Sort faces
	pcl::TextureMapping<pcl::PointXYZ> tm;  // TextureMapping object that will perform the sort
	tm.textureMeshwithMultipleCameras(mesh, my_cams);

	// compute normals for the mesh
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	tree->setInputCloud(cloud);
	n.setInputCloud(cloud);
	n.setSearchMethod(tree);
	n.setKSearch(20);
	n.compute(*normals);

	// Concatenate XYZ and normal fields
	pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
	pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
	pcl::toPCLPointCloud2(*cloud_with_normals, mesh.cloud);
	pcl::io::saveOBJFile("./data/res.obj", mesh, 5);

	return 0;
}

使用OpenMVS库

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/vtk_lib_io.h>
#include <opencv2/opencv.hpp>
#include <OpenMVS/MVS/Common.h>
#include <OpenMVS/MVS/Scene.h>
#include <OpenMVS/Common/Types.h>


int main()
{
	const int numViews = 3;
	std::string prefix = "./data/color";
	std::vector<cv::Mat> v_color;
	double t1, t2, t3;
	double r11, r12, r13, r21, r22, r23, r31, r32, r33;
	std::vector<Eigen::Matrix4d> poses;
	for (size_t i = 0; i < numViews; i++)
	{
		std::stringstream ss;
		ss << std::setw(3) << std::setfill('0') << i;
		cv::Mat img = cv::imread(prefix + ss.str() + ".png");
		std::cout << prefix + ss.str() + ".png" << std::endl;
		v_color.push_back(img);

		std::ifstream f(prefix + ss.str() + ".txt");
		f >> t1 >> t2 >> t3
			>> r11 >> r12 >> r13
			>> r21 >> r22 >> r23
			>> r31 >> r32 >> r33;

		Eigen::Matrix4d pose;
		pose << r11, r12, r13, t1,
			r21, r22, r23, t2,
			r31, r32, r33, t3,
			0, 0, 0, 1;
		std::cout << pose << std::endl;

		poses.push_back(pose);
	}

	MVS::Scene MVS_scene(8);
	MVS_scene.platforms.Reserve(numViews);
	MVS_scene.images.Reserve(numViews);

	for (size_t i = 0; i < numViews; i++)
	{
		MVS::Image& image = MVS_scene.images.AddEmpty();
		image.image = cv::MatExpr(v_color[i]);
		image.platformID = i;
		image.ID = i;
		image.height = 1080;
		image.width = 1920;
		image.cameraID = 0;
		image.poseID = 0;

		MVS::Platform& platform = MVS_scene.platforms.AddEmpty();
		MVS::Platform::Camera& camera = platform.cameras.AddEmpty();

		//内参
		camera.K(0, 0) = 1.045238890839152646e+03;
		camera.K(1, 1) = 1.046345842268217893e+03;
		camera.K(0, 2) = 9.518466822597985129e+02;
		camera.K(1, 2) = 5.069212131671924340e+02;
		camera.K = camera.GetScaledK(REAL(1) / MVS::Camera::GetNormalizationScale(image.height, image.width));
		camera.R = RMatrix::IDENTITY;
		camera.C = CMatrix::ZERO;

		//外参
		MVS::Platform::Pose & openMVS_pose = platform.poses.AddEmpty();
		openMVS_pose.R = poses[i].block<3, 3>(0, 0);
		openMVS_pose.R = openMVS_pose.R.inv();
		openMVS_pose.C = poses[i].block<3, 1>(0, 3);

		image.UpdateCamera(MVS_scene.platforms);
	}

	pcl::PolygonMesh triangles;
	pcl::io::loadPolygonFilePLY("./data/obj.ply", triangles);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::fromPCLPointCloud2(triangles.cloud, *cloud);

	MVS_scene.mesh.vertices.Resize(cloud->size());
	MVS_scene.mesh.faces.Resize(triangles.polygons.size());

	for (size_t i = 0; i < cloud->size(); i++)
	{
		MVS_scene.mesh.vertices[i].x = cloud->points[i].x;
		MVS_scene.mesh.vertices[i].y = cloud->points[i].y;
		MVS_scene.mesh.vertices[i].z = cloud->points[i].z;
	}
	for (size_t i = 0; i < triangles.polygons.size(); i++)
	{
		MVS_scene.mesh.faces[i].x = triangles.polygons[i].vertices[0];
		MVS_scene.mesh.faces[i].y = triangles.polygons[i].vertices[1];
		MVS_scene.mesh.faces[i].z = triangles.polygons[i].vertices[2];
	}

	MVS_scene.TextureMesh(0, 1920, 0.0, 0.0, 1, 1, 0, 0);
	MVS_scene.mesh.Save("./data/res.obj");

	return 0;
}

PCL和OpenMVS库都能实现上面展示的纹理贴图效果,生成结果都有一个obj模型文件和一个mtl材质文件。不同的是使用PCL生成的mtl文件对应多张图片,而使用OpenMVS库会另外生成一个png文件。结果可以通过meshlab可视化查看:
输入ply文件
在这里插入图片描述
输出obj文件
在这里插入图片描述

可视化

C++版本:使用PCL库,但无法读取上面PCL库贴图生成的obj模型纹理,可能是PCL库内部对多视角贴图的不兼容。

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/visualization/pcl_visualizer.h>


int main(int argc, char** argv)
{
	std::string objPath = "./data/res.obj";
	pcl::TextureMesh mesh;
	pcl::io::loadOBJFile(objPath, mesh);
	pcl::TextureMesh mesh2;
	pcl::io::loadPolygonFileOBJ(objPath, mesh2);
	mesh2.tex_materials = mesh.tex_materials;
	boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("viewer"));
	viewer->addTextureMesh(mesh2, "mesh");
	while (!viewer->wasStopped())  // 在按下 "q" 键之前一直会显示窗口
	{
		viewer->spinOnce();
	}
	return 0;
}

python版本:使用open3d库。

import open3d as o3d

def visualize(mesh):
    vis = o3d.visualization.Visualizer()
    vis.create_window()
    vis.add_geometry(mesh)
    vis.run()
    vis.destroy_window()
    

mesh = o3d.io.read_triangle_mesh("./data/res.obj",True)
mesh.compute_vertex_normals()
#visualize(mesh)
o3d.visualization.draw([mesh])

所有代码和数据放在下载链接:texture mapping纹理贴图

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
Open3D是一个用于处理三维数据的库,它可以加载和显示OBJ文件,但是目前Open3D不支持OBJ纹理贴图。不过,你可以使用其他的库来加载和显示带有纹理贴图的OBJ文件,例如Assimp和OpenGL。 具体步骤如下: 1. 使用Assimp库来加载OBJ文件及其相关的纹理贴图。Assimp是一个用C++编写的开源库,可以处理多种三维模型格式,并支持加载纹理贴图。 2. 在OpenGL中创建纹理对象并将纹理贴图映射到OBJ模型上。OpenGL是一个强大的图形库,可以用来显示三维模型并进行纹理贴图。你可以使用OpenGL中的glTexImage2D函数创建纹理对象,并使用glTexCoordPointer函数将纹理坐标映射到OBJ模型的顶点上。 3. 在程序中添加代码,将纹理贴图应用到OBJ模型上。你可以使用OpenGL中的glBindTexture函数将纹理对象绑定到模型上,并使用glEnable函数启用纹理映射。 下面是一个简单的示例代码,使用Assimp和OpenGL实现OBJ纹理贴图: ```cpp #include <iostream> #include <GL/glut.h> #include <assimp/Importer.hpp> #include <assimp/scene.h> #include <assimp/postprocess.h> GLuint textureId; void loadTexture(const char* filename) { // Load image using SOIL library int width, height; unsigned char* image = SOIL_load_image(filename, &width, &height, 0, SOIL_LOAD_RGB); // Generate texture object glGenTextures(1, &textureId); // Bind texture object glBindTexture(GL_TEXTURE_2D, textureId); // Set texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Load texture data glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Free image data SOIL_free_image_data(image); } void drawModel(const char* filename) { // Load model using Assimp library Assimp::Importer importer; const aiScene* scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs); if (!scene) { std::cerr << "Error loading model: " << importer.GetErrorString() << std::endl; return; } // Load texture loadTexture("texture.jpg"); // Enable texture mapping glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureId); // Draw model glPushMatrix(); glTranslatef(0.0f, -1.5f, 0.0f); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glColor3f(1.0f, 1.0f, 1.0f); for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { const aiMesh* mesh = scene->mMeshes[i]; glBegin(GL_TRIANGLES); for (unsigned int j = 0; j < mesh->mNumFaces; ++j) { const aiFace& face = mesh->mFaces[j]; for (unsigned int k = 0; k < face.mNumIndices; ++k) { unsigned int index = face.mIndices[k]; glTexCoord2f(mesh->mTextureCoords[0][index].x, mesh->mTextureCoords[0][index].y); glVertex3f(mesh->mVertices[index].x, mesh->mVertices[index].y, mesh->mVertices[index].z); } } glEnd(); } glPopMatrix(); // Disable texture mapping glDisable(GL_TEXTURE_2D); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); drawModel("model.obj"); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("OBJ Texture Mapping"); glutDisplayFunc(display); glEnable(GL_DEPTH_TEST); glutMainLoop(); return 0; } ``` 在这个示例代码中,我们使用Assimp库加载OBJ模型及其关联的纹理贴图,并使用OpenGL将纹理贴图映射到模型上。在drawModel函数中,我们首先调用loadTexture函数加载纹理贴图,然后使用glEnable函数启用纹理映射,并将纹理对象绑定到模型上。在绘制模型时,我们通过glTexCoord2f函数将纹理坐标映射到模型的顶点上。 需要注意的是,Assimp库和SOIL库需要安装并配置好环境。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值