【点云学习】二:获取并保存彩色点云PCD文件

本文详细介绍了如何使用Kinect与OpenCV获取RGB-D图像,转换为彩色点云数据,并保存为PCD文件格式。同时,分享了利用PCL库进行点云数据的读取与显示的方法。

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

【点云学习】二:获得彩色点云PCD文件并显示

上一篇博客讲的是如何用Kinect+opencv获得RGB-D图像和深度图像。这篇主要介绍pcd文件。整合了几篇不错的博客,方便自己理解。

PCD文件

点云数据是指在一个三维坐标系中的一组向量的集合。这些向量通常以X,Y,Z三维坐标的形式表示,一般主要代表一个物体的外表面几何形状,除此之外点云数据还可以附带RGB信息,即每个坐标点的颜色信息,或者是其他的信息。
PCL这个开源库专门处理pcd格式的文件,它实现了点云获取、滤波、分割、配准、检索、特征提取、识别、追踪、曲面重建、可视化等。

# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z rgba       RGB三个值通过位运算合一起
SIZE 4 4 4 4            以字节为单位指定每个数据所占用的内存。
TYPE F F F U            指定每个数据的数据类型。
                        I:可表示int8,int16,int32。
                        U:可表示uint8,unit16,uint32。
                        F:表示float(上图所用的为浮点类型)。
COUNT 1 1 1 1           指定每个维度有多少元素,xyz数据通常只有一个元素。
WIDTH 181550            指定数据点的宽度,它包含两个含义:
                        1.可指定点云总个数(与POINTS相同),用于无组织的数据。
                        2.可指定有组织点云数据的宽度(连续点的总数)。
HEIGHT 1                指定数据点的高度,它包含两个含义:
                        1.可指定有组织的点云数据的高度(总行数)。
                        2.对未组织的数据,它被设置为1。
VIEWPOINT 0 0 0 1 0 0 0 采集数据时的视点(由平移tx,ty,tz和四元数qw,qx,qy,qz组成)。
POINTS 181550           指定点云总个数。
DATA ascii              数据类型
1.3159573 0.89465034 1.7700001 4287533445
1.3135536 0.88792604 1.7680001 4287270020
1.3126484 0.88222772 1.7680001 4287402374
1.3117549 0.87654412 1.7680001 4287402887
1.3153214 0.87383044 1.774 4287270785
1.3174117 0.87011409 1.7780001 4287467650
1.3172878 0.86492777 1.779 4287335811

数据解说摘自

这个是我用Kinect显示出的彩色点云数据的一部分,图就是下面这个灯。
在这里插入图片描述

保存彩色点云

再来说是如何获得的,代码如下:来源

#include <Kinect.h>
#include <opencv2\opencv.hpp>
#include <pcl/visualization/cloud_viewer.h> 
#include <pcl/visualization/pcl_visualizer.h>
#include <opencv2/highgui/highgui.hpp>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>

using namespace cv;
using namespace std;


IKinectSensor* pSensor;
ICoordinateMapper* pMapper;
const int iDWidth = 512, iDHeight = 424;//深度图尺寸
const int iCWidth = 1920, iCHeight = 1080;//彩色图尺寸
CameraSpacePoint depth2xyz[iDWidth * iDHeight];
ColorSpacePoint depth2rgb[iCWidth * iCHeight];


void viewerOneOff(pcl::visualization::PCLVisualizer& viewer)
{
	viewer.setBackgroundColor(1.0, 1.0, 1.0);//设置背景颜色 
}


//启动Kinect
bool initKinect()
{
	if (FAILED(GetDefaultKinectSensor(&pSensor))) return false;
	if (pSensor)
	{
		pSensor->get_CoordinateMapper(&pMapper);
		pSensor->Open();
		cout << "已打开相机" << endl;
		return true;
	}
	else return false;
}
//获取深度帧
Mat DepthData()
{
	IDepthFrameSource* pFrameSource = nullptr;
	pSensor->get_DepthFrameSource(&pFrameSource);
	IDepthFrameReader* pFrameReader = nullptr;
	pFrameSource->OpenReader(&pFrameReader);
	IDepthFrame* pFrame = nullptr;
	Mat mDepthImg(iDHeight, iDWidth, CV_16UC1);
	while (true)
	{
		if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
		{

			pFrame->CopyFrameDataToArray(iDWidth * iDHeight, reinterpret_cast<UINT16*>(mDepthImg.data));
			cout << "已获取深度帧" << endl;
			pFrame->Release();
			return mDepthImg;
			break;
		}
	}
}
//获取彩色帧
Mat RGBData()
{
	IColorFrameSource* pFrameSource = nullptr;
	pSensor->get_ColorFrameSource(&pFrameSource);
	IColorFrameReader* pFrameReader = nullptr;
	pFrameSource->OpenReader(&pFrameReader);
	IColorFrame* pFrame = nullptr;
	Mat mColorImg(iCHeight, iCWidth, CV_8UC4);
	while (true)
	{
		if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
		{

			pFrame->CopyConvertedFrameDataToArray(iCWidth * iCHeight * 4, mColorImg.data, ColorImageFormat_Bgra);
			cout << "已获取彩色帧" << endl;
			pFrame->Release();
			return mColorImg;
			break;
		}
	}
}


int main()
{
	initKinect();
	pcl::visualization::CloudViewer viewer("Cloud Viewer");//简单显示点云的可视化工具类
	viewer.runOnVisualizationThreadOnce(viewerOneOff);//点云显示线程,渲染输出时每次都调用
	pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
	Mat mColorImg;
	Mat mDepthImg;
	int count = 0;
	while (count <= 0)
	{
		Sleep(5000);
		mColorImg = RGBData();
		mDepthImg = DepthData();
		imshow("RGB", mColorImg);
		pMapper->MapDepthFrameToColorSpace(iDHeight * iDWidth, reinterpret_cast<UINT16*>(mDepthImg.data), iDHeight* iDWidth, depth2rgb);//深度图到颜色的映射
		pMapper->MapDepthFrameToCameraSpace(iDHeight * iDWidth, reinterpret_cast<UINT16*>(mDepthImg.data), iDHeight* iDWidth, depth2xyz);//深度图到相机三维空间的映射


		float maxX = depth2xyz[0].X, maxY = depth2xyz[0].Y, maxZ = depth2xyz[0].Z;
		float minX = depth2xyz[0].X, minY = depth2xyz[0].Y, minZ = depth2xyz[0].Z;
		for (size_t i = 0; i < iDWidth; i++)
		{
			for (size_t j = 0; j < iDHeight; j++)
			{
				pcl::PointXYZRGBA pointTemp;
				if (depth2xyz[i + j * iDWidth].Z > 0.5 && depth2rgb[i + j * iDWidth].X < 1920 && depth2rgb[i + j * iDWidth].X>0 && depth2rgb[i + j * iDWidth].Y < 1080 && depth2rgb[i + j * iDWidth].Y>0)
				{
					pointTemp.x = -depth2xyz[i + j * iDWidth].X;
					if (depth2xyz[i + j * iDWidth].X > maxX) maxX = -depth2xyz[i + j * iDWidth].X;
					if (depth2xyz[i + j * iDWidth].X < minX) minX = -depth2xyz[i + j * iDWidth].X;
					pointTemp.y = depth2xyz[i + j * iDWidth].Y;
					if (depth2xyz[i + j * iDWidth].Y > maxY) maxY = depth2xyz[i + j * iDWidth].Y;
					if (depth2xyz[i + j * iDWidth].Y < minY) minY = depth2xyz[i + j * iDWidth].Y;
					pointTemp.z = depth2xyz[i + j * iDWidth].Z;
					if (depth2xyz[i + j * iDWidth].Z != 0.0)
					{
						if (depth2xyz[i + j * iDWidth].Z > maxZ) maxZ = depth2xyz[i + j * iDWidth].Z;
						if (depth2xyz[i + j * iDWidth].Z < minZ) minZ = depth2xyz[i + j * iDWidth].Z;
					}
					pointTemp.b = mColorImg.at<cv::Vec4b>(depth2rgb[i + j * iDWidth].Y, depth2rgb[i + j * iDWidth].X)[0];
					pointTemp.g = mColorImg.at<cv::Vec4b>(depth2rgb[i + j * iDWidth].Y, depth2rgb[i + j * iDWidth].X)[1];
					pointTemp.r = mColorImg.at<cv::Vec4b>(depth2rgb[i + j * iDWidth].Y, depth2rgb[i + j * iDWidth].X)[2];
					pointTemp.a = mColorImg.at<cv::Vec4b>(depth2rgb[i + j * iDWidth].Y, depth2rgb[i + j * iDWidth].X)[3];
					cloud->push_back(pointTemp);
				}

			}

		}
		//pcl::io::savePCDFileASCII("deng.pcd", *cloud);
		/*char image_name[13];
		sprintf(image_name, "%s%d%s", "C:/Users/Xu_Ruijie/Desktop", count, ".pcd");
		imwrite(image_name, im);*/
		string s = "我来了";
		s += ".pcd";
		pcl::io::savePCDFile(s, *cloud, false);
		std::cerr << "Saved " << cloud->points.size() << " data points." << std::endl;
		viewer.showCloud(cloud);
		
		count++;
		mColorImg.release();
		mDepthImg.release();
		cloud->clear();
		waitKey(10);

	}
	return 0;
}

在基础上加了保存pcd的命令,while那里count可以随便改,注意保存的时候修改一下每次保存的文件名就行了。再贴一个无颜色的点云获取 原始点云获取

余留问题

  1. 我获得原始点云时候,保存了pcd,但是我读不出来,显示pcd::io报错,特别奇怪,就是read那里中止了,希望明天可以解决。【已解决】
    代码直接贴出来了:文件名改成自己的就行,但是这个读出来只是没有颜色的点云,还需进一步完善。
#include<iostream>
#include<pcl/io/pcd_io.h>
#include<pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
void viewerOneOff(pcl::visualization::PCLVisualizer& viewer) {
    viewer.setBackgroundColor(1.0f, 0.5f, 1.0f);
}

int main(int argc, char** argv) {
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    //*打开点云文件
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("test2.pcd", *cloud) == -1) {
        PCL_ERROR("Couldn't read file rabbit.pcd\n");
        return(-1);
    }
    std::cout << cloud->points.size() << std::endl;
    pcl::visualization::CloudViewer viewer("cloud viewer");
    viewer.showCloud(cloud);
    viewer.runOnVisualizationThreadOnce(viewerOneOff);

    system("pause");
    return 0;
}
  1. pointnet的图片是ply格式的。
  2. 代码有些还不太明白。
  3. 之后就是点云分割和滤波处理了。

还在学习中,欢迎留言。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值