点云扫描线提取

点云提取扫描线并采样显示!

1.扫描线提取原理

目前车载LiDAR系统搭载的激光扫描仪主要是线性扫描,获得的扫描点在目标上按扫描线排列。在同一扫描线中,系统记录的连续激光脚点的扫描角度差值为固定值(一般为激光扫描仪的角度分辨率)。在一个完整的扫描周期中,若扫描视场角为顶部天空,会出现无激光脚点返回的情况。此时当前扫描线的最后一个点和下一条扫描线的起始点的扫描角度有一个非规律的阶跃。同理,因为车载激光点云的连续性,当扫描视角为顶部天空时,GPS时间差也会出现一个非规律的阶跃。因此可以设置一个角度阂值或时间阂值检测扫描线两端的断点,将连续点云归于一条扫描线中,从而将离散的扫描点转化成有序的二维扫描线数据集。(参考自方莉娜博士论文)
由于.las格式的点云文件包含每个离散点的扫描角度和GPS时间信息,所以可以按照上述方法提取出扫描线,并按照扫描线进行点云特征识别与分析等。本文的例子采用时间阈值提取扫描线。

2.核心代码实现

2.1 自定义pcl点云类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct myPointXYZTI
{
PCL_ADD_POINT4D;
union
{
float coordinate[3];
struct
{
float x;
float y;
float z;
};
};
double GPSTime;
int intensity;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
}EIGEN_ALIGN16;


POINT_CLOUD_REGISTER_POINT_STRUCT(myPointXYZTI,// 注册点类型宏
(float, x, x)
(float, y, y)
(float, z, z)
(double, GPSTime, GPSTime)
(int, intensity, intensity)
)
2.2 读取点云txt文件

txt文件点云按行保存,格式为:X,Y,Z,GPSTime,intensity .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool CloudIO::readTxtFile(const string &fileName, const char tag, const mypcXYZTIPtr &pointCloud)
{
cout << "reading file start..... " << endl;
ifstream fin(fileName);
string linestr;
while (getline(fin, linestr))
{
vector<string> strvec;
string s;
stringstream ss(linestr);
while (getline(ss, s, tag))
{
strvec.push_back(s);
}
myPointXYZTI p;
p.x = stod(strvec[0]);
p.y = stod(strvec[1]);
p.z = stod(strvec[2]);
p.GPSTime = stod(strvec[3]);
p.intensity = stoi(strvec[4]);
pointCloud->points.push_back(p);
}
fin.close();
cout << "reading file finished! " << endl;
cout << "There are " << pointCloud->points.size() << " points!" << endl;
return true;
}

2.3 扫描线提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void CloudIO::extractScanLine(mypcXYZTIPtr cloud, vector<vector<int> >& ScanLineVec)
{
vector<int> temp;
for (int i = 1; i < cloud->points.size() - 1; i++)
{

double time = cloud->points[i].GPSTime - cloud->points[i - 1].GPSTime;
if (fabs(time) < 0.0015){
temp.push_back(i);
}
else{
ScanLineVec.push_back(temp);
vector<int>().swap(temp);
}
}

//第一个点加入第一条扫描线
ScanLineVec[0].push_back(0);
cout << "ScanLineVec size: " << ScanLineVec.size() << endl;
}
2.4 扫描线采样显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void CloudIO::ScanLineDisplay(mypcXYZTIPtr &cloud, vector<vector<int> >& ScanLineVec, int ScanLineNum)
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgbcloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::PointXYZRGB p;
bool flag = true;
for (int i = 0; i < ScanLineVec.size(); i++)
{
if (i % ScanLineNum == 0)
{
for (int j = 0; j < ScanLineVec[i].size(); j++)
{
p.x = cloud->points[ScanLineVec[i].at(j)].x;
p.y = cloud->points[ScanLineVec[i].at(j)].y;
p.z = cloud->points[ScanLineVec[i].at(j)].z;
if (flag == true){
p.r = 225; p.g = 0; p.b = 0;
}else{
p.r = 0; p.g = 255; p.b = 0;
}
rgbcloud->points.push_back(p);
}

if (flag == true){
flag = false;
}
else{ flag = true; }
}
}
cout << rgbcloud->points.size() << endl;

boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(255, 255, 255);
viewer->addPointCloud(rgbcloud, "Ground");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
}
2.5 结果显示

原始点云
扫描线采样显示

2.6 备注

1.上述点云扫描线采样间隔为6,对相邻扫描线赋不同颜色进行区分。
2.时间间隔参数的设置主要依据激光扫描仪的扫描频率或对原始点云时间直方图进行分析。

-------------本文结束感谢您的阅读-------------