以PLY binary little-endian format格式保存RGB3D点云

本文介绍了如何使用PCL库移除点云中的无效点,并将保留的XYZ和RGB信息转换为PLYbinary小端格式,便于后续处理和文件存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PCL库中的3D点云包含的域相当丰富,但大多数时候我们用不到,在一些插件和脚本中使用PCL导出的PLY文件,这些多余的域可能会导致报错,因此我写了一个简单的PLY binary little-endian format格式,包含了XYZ的三维坐标信息和每个点的RGB信息。运行需要PCL库读取点云文件。

#include <iostream>          
#include <pcl/io/pcd_io.h>  
#include <pcl/point_types.h> 
#include <pcl/io/ply_io.h>  
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/filters/filter.h>

void removeNaN(pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud)//移除无效点
{
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_temp(new pcl::PointCloud<pcl::PointXYZRGB>);//实例化一个点云对象指针
	for (size_t i = 0; i < cloud->points.size(); i++)
	{
		if (pcl::isFinite(cloud->points[i]))
		{
			cloud_temp->points.push_back(cloud->points[i]);
		}
	}
	cloud->clear();
	cloud_temp->width = (int)cloud_temp->points.size();
	cloud_temp->height = 1;//设置点云形状和类型,无序点云
	pcl::copyPointCloud(*cloud_temp, *cloud);
}

std::string FilePathandName(const std::string &file_path)//提取打开文件除后缀的部分,作为保存文件的前缀
{
	std::string file_path_without_suffix;
	file_path_without_suffix = file_path.substr(0, file_path.find_last_of('.'));
	return file_path_without_suffix;
}

struct PositionData
{
	float x, y, z;
	PositionData() {
		x = y = z = 0.0f;
	}
};

struct colorData
{
	unsigned char r, g, b;
	colorData() {
		r = g = b = 0;
	}
};

int main(int argc, char** argv)
{
	if (argc < 2)
	{
		std::cout << "The Input is error!" << std::endl;
		return (-1);
	}//输入不符合要求时报错
	std::string ori_filename;
	ori_filename = argv[1];
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::io::loadPCDFile(ori_filename, *cloud);
	std::cout << "Read PCD file Success" << std::endl;
	removeNaN(cloud);
	int vertexCount = cloud->points.size();
	//pcl::io::savePLYFileBinary("Captured_Frame1.ply", *cloud);
	std::string file_path_without_suffix;
	file_path_without_suffix = FilePathandName(argv[1]);

	std::string new_filename;
	new_filename = file_path_without_suffix + ".ply";

	FILE *outputPly;
	struct PositionData P;
	struct colorData C;
	outputPly = fopen(new_filename.c_str(), "wb");

	fprintf(outputPly, "ply\n");
	fprintf(outputPly, "format binary_little_endian 1.0\n");
	fprintf(outputPly, "comment This contains a Splatted Point Cloud\n");
	fprintf(outputPly, "element vertex %d\n", cloud->points.size());
	fprintf(outputPly, "property float x\n");
	fprintf(outputPly, "property float y\n");
	fprintf(outputPly, "property float z\n");
	fprintf(outputPly, "property uchar red\n");
	fprintf(outputPly, "property uchar green\n");
	fprintf(outputPly, "property uchar blue\n");
	fprintf(outputPly, "end_header\n");

	//write vertices and normals
	for (long int i = 0; i < vertexCount; i++)
	{
		P.x = cloud->points[i].x;
		P.y = cloud->points[i].y;
		P.z = cloud->points[i].z;
		C.r = cloud->points[i].r;
		C.g = cloud->points[i].g;
		C.b = cloud->points[i].b;
		fwrite(&P, sizeof(struct PositionData), 1, outputPly);
		fwrite(&C, sizeof(struct colorData), 1, outputPly);
	}
	
	fclose(outputPly);
	std::cout << "Save PLY file Success" << std::endl;
	return 0;
}

#include <opencv2/opencv.hpp> #include <iostream> #include <fstream> #include <vector>   // 生成PLY点云文件(二进制格式,兼容CloudCompare) void savePointCloudToPLY(const cv::Mat & points, const cv::Mat & colors, const std::string & filename, double min_depth = 0.1, double max_depth = 10000.0) { std::ofstream plyFile(filename, std::ios::binary); if (!plyFile.is_open()) { std::cerr << "Error: Could not open PLY file for writing." << std::endl; return; }   // 统计有效点数量 int validCount = 0; for (int y = 0; y < points.rows; y++) { for (int x = 0; x < points.cols; x++) { cv::Vec3f point = points.at<cv::Vec3f>(y, x); // 检查深度值是否在有效范围内且不是无穷大 if (point[2] > min_depth && point[2] < max_depth && !std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) { validCount++; } } }   // 写入PLY头(二进制格式plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; plyFile << "element vertex " << validCount << "\n"; plyFile << "property float x\n"; plyFile << "property float y\n"; plyFile << "property float z\n"; plyFile << "property uchar red\n"; plyFile << "property uchar green\n"; plyFile << "property uchar blue\n"; plyFile << "end_header\n";   // 写入点云数据(二进制) for (int y = 0; y < points.rows; y++) { for (int x = 0; x < points.cols; x++) { cv::Vec3f point = points.at<cv::Vec3f>(y, x); cv::Vec3b color = colors.at<cv::Vec3b>(y, x);   // 过滤无效点 if (point[2] > min_depth && point[2] < max_depth && !std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) { // 写入坐标(小端序浮点数) plyFile.write(reinterpret_cast<const char*>(&point[0]), sizeof(float)); plyFile.write(reinterpret_cast<const char*>(&point[1]), sizeof(float)); plyFile.write(reinterpret_cast<const char*>(&point[2]), sizeof(float));   // 写入颜色(注意OpenCV使用BGR顺序) plyFile.write(reinterpret_cast<const char*>(&color[2]), sizeof(uint8_t)); // R plyFile.write(reinterpret_cast<const char*>(&color[1]), sizeof(uint8_t)); // G plyFile.write(reinterpret_cast<const char*>(&color[0]), sizeof(uint8_t)); // B } } }   plyFile.close(); std::cout << "Saved " << validCount << " points to " << filename << std::endl; }   int main() { // 1. 定义投影矩阵(使用您提供的数据) cv::Mat P1 = (cv::Mat_<double>(3, 4) << 2791.1531638651541, -3164.2425130586262, -1932.4112841478736, 9203066124.8177795410156, -3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201114654541, -0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603910);   cv::Mat P2 = (cv::Mat_<double>(3, 4) << 2791.1885344521997, -3164.2173819650006, -1928.6860517671789, 9202236641.5202198028564, -3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201095581055, -0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603947);   // 2. 读取视差图和左影像(用于点云着色) cv::Mat disparity = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\DispImg\\017ER030_017ER031_DISP_ET0.tif", cv::IMREAD_UNCHANGED); // 16位视差图 cv::Mat leftImage = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\EpiImg\\017ER030_017ER031_ET0\\017ER030_REC.tif"); // 左核线影像   if (disparity.empty() || leftImage.empty()) { std::cerr << "Error: Could not read input images." << std::endl; return -1; }   // 3. 验证视差图数据 std::cout << "Disparity image size: " << disparity.size() << std::endl; std::cout << "Disparity image type: " << disparity.type() << " (CV_16UC1=" << CV_16UC1 << ")" << std::endl;   // 检查视差图是否为16位格式 if (disparity.type() != CV_16UC1 && disparity.type() != CV_16SC1) { std::cerr << "Warning: Disparity image is not 16-bit format. Results may be incorrect." << std::endl; }   // 4. 计算重投影矩阵Q cv::Mat K1, R1, t1, K2, R2, t2; cv::decomposeProjectionMatrix(P1, K1, R1, t1); cv::decomposeProjectionMatrix(P2, K2, R2, t2);   // 转换为非齐次坐标 t1 = t1.rowRange(0, 3) / t1.at<double>(3); t2 = t2.rowRange(0, 3) / t2.at<double>(3);   // 计算基线长度(单位:世界坐标单位) cv::Mat C1 = -R1.inv() * t1; cv::Mat C2 = -R2.inv() * t2; double Tx = cv::norm(C2 - C1);   // 提取内参参数 double fx = K1.at<double>(0, 0); double fy = K1.at<double>(1, 1); double cx = K1.at<double>(0, 2); double cy = K1.at<double>(1, 2);   std::cout << "\nCamera Parameters:" << std::endl; std::cout << " Focal length (fx, fy): " << fx << ", " << fy << std::endl; std::cout << " Principal point (cx, cy): " << cx << ", " << cy << std::endl; std::cout << " Baseline (Tx): " << Tx << " world units" << std::endl;   // 修改Q矩阵构造 double cx_right = K2.at<double>(0, 2); // 右相机主点x cv::Mat Q = (cv::Mat_<double>(4, 4) << 1.0, 0.0, 0.0, -cx, 0.0, 1.0, 0.0, -cy, 0.0, 0.0, 0.0, fx, 0.0, 0.0, -1.0 / Tx, (cx - cx_right) / Tx); // 关键修改   std::cout << "\nReprojection Matrix Q:" << std::endl; std::cout << Q << std::endl;   // 6. 处理视差图 - 关键步骤! cv::Mat disp_float;   // 修改视差图处理部分 double scale_factor = 1.0; if (disparity.type() == CV_16UC1) { scale_factor = 1.0 / 16.0; } else if (disparity.type() == CV_16SC1) { scale_factor = 1.0 / 16.0; } else if (disparity.type() == CV_32FC1) { // 显式匹配32FC1 scale_factor = 1.0; } else if (disparity.type() == CV_32F) { // 后备匹配通用浮点 scale_factor = 1.0; } else { std::cerr << "Warning: Unknown disparity format. Using default scaling." << std::endl; scale_factor = 1.0 / 16.0; } disparity.convertTo(disp_float, CV_32F, scale_factor);   // 7. 计算3D点云 cv::Mat xyz; cv::reprojectImageTo3D(disp_float, xyz, Q, false, CV_32F);   // 8. 提取深度图(Z通道) std::vector<cv::Mat> channels; cv::split(xyz, channels); cv::Mat depth = channels[2];   // 修改深度图可视化部分 cv::Mat depthVis; double minVal, maxVal;   // 创建掩码:有效点 = (深度>0) 且 (非无穷大) cv::Mat validMask = (depth > 0) & (depth < std::numeric_limits<float>::infinity()); cv::Mat validMask8U; validMask.convertTo(validMask8U, CV_8U);   // 仅对有效点计算深度范围 cv::minMaxLoc(depth, &minVal, &maxVal, nullptr, nullptr, validMask8U); std::cout << "Valid Depth Range: min = " << minVal << ", max = " << maxVal << std::endl;   if (maxVal > minVal) { // 归一化有效点到[0, 255] cv::Mat temp = cv::Mat::zeros(depth.size(), CV_32F); depth.copyTo(temp, validMask8U); // 仅复制有效点 temp.convertTo(depthVis, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal)); } else { depthVis = cv::Mat::zeros(depth.size(), CV_8U); }   cv::imwrite("depth_map.png", depthVis);   // 10. 生成彩色点云(使用二进制PLY格式) savePointCloudToPLY(xyz, leftImage, "point_cloud.ply");   std::cout << "\nProcessing completed." << std::endl; std::cout << "Depth map saved as 'depth_map.png'" << std::endl; std::cout << "Point cloud saved as 'point_cloud.ply'" << std::endl;   cv::waitKey(0); return 0; }这是一段生成深度图和三位点云的代码,但是生成的三维点云质量很差,你帮我重新写一段C++代码,运行出高精度的三位点云
最新发布
07-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值