基于SVM的点云分类(树木,建筑两类)

西南某高校遥感专业在读生(个人网站:YhQIAO)这学期选了院里张老师的《三维扫描技术》选修课,干货满满,期末有个大作业svm点云分类,正好前段时间对机器学习比较感兴趣,了解了点算法,但是一直没有实践。这次正好应用一下SVM

1.样本数据选取

1.1.原始样本:

基于SVM的点云分类(树木,建筑两类)_第1张图片
基于SVM的点云分类(树木,建筑两类)_第2张图片

1.2 样本预处理

首先去除地面,(滤波,随机采样等均可),利用连通性分析分离点云,同时删除点云个数小于20个点的点云快。(剔除一些杂点,不太便于辨别形状的点)。
基于SVM的点云分类(树木,建筑两类)_第3张图片
基于SVM的点云分类(树木,建筑两类)_第4张图片

1.3 训练样本数据选取

基于SVM的点云分类(树木,建筑两类)_第5张图片
基于SVM的点云分类(树木,建筑两类)_第6张图片

2.特征值的选取与计算(基于表面邻域分析)

由于树木和建筑直观上看还是比较好区分的,因此取两个特征值组成特征向量分类效果还可以。
基于SVM的点云分类(树木,建筑两类)_第7张图片

2.1 粗糙度的计算

基于SVM的点云分类(树木,建筑两类)_第8张图片
基于SVM的点云分类(树木,建筑两类)_第9张图片
寻找平面计算方法很类似于平差里面里的间接平差
ps(一开始想偷懒直接对整体进行计算,对于树还是可以,但对于建筑就有坑)
基于SVM的点云分类(树木,建筑两类)_第10张图片
建筑如果是由坡面组成,直接对整体进行计算粗糙度是不科学的。

2.2 基于KNN表面邻域分析

基于SVM的点云分类(树木,建筑两类)_第11张图片
在点云表面取一个点,找到距离最近的n个点,这样的话建筑表面取点,最近的n个点一般都会是平面,树木表面取最近的n个点的话,按上述方法计算出来的粗糙的比较大。

KNN算法很多库都有,C#里有Alglib,Python的一个强大的第三方库sklearn里面也有封装
基于SVM的点云分类(树木,建筑两类)_第12张图片

算下来的话可以看到建筑和树木差异还是比较大的,可以作为一个较好的分类标准。

2.3 点云表面局部协方差矩阵分析

老师上课PPT里有的一个东西,一开始没怎么懂,后来在《遥感学报》上看到了一篇论文用到了这个来做分类,实践了下后懂了原理,就是通过计算协方差阵的特征值来描述点云的分布。
基于SVM的点云分类(树木,建筑两类)_第13张图片
【来源】:老师PPT
基于SVM的点云分类(树木,建筑两类)_第14张图片
基于SVM的点云分类(树木,建筑两类)_第15张图片
【来源】:老师PPT
基于SVM的点云分类(树木,建筑两类)_第16张图片
【来源】遥感学报,马振宇,庞勇,李增元,卢昊,刘鲁霞,陈博伟.地基激光雷达森林近地面点云精细分类与倒木提取 [J].遥感学报,2019, 23(4)

如果面状性较好的话,可以知道会有两个特征值大小接近,一个特征值很小;如果点云很分散的话,那么3个特征值的大小应该是比较接近的。

这里取发散状指数进行计算,可以看出差异还是比较大的,效果比较好。
基于SVM的点云分类(树木,建筑两类)_第17张图片
把上面两个特征值作出特征空间(二维来说就是个平面),可以看出可分性还是比较强的,用一个线性SVM分类器分类效果还是可以。

3 支持向量机分类

基于SVM的点云分类(树木,建筑两类)_第18张图片
原理省略n字。。。。反正很复杂,但是我们把它看成一个黑箱模型就够了,就是往里面扔特征向量,往外面吐分类结果就可以了。

我用的是python里Sklearn库里面SVM,sklearn也安利一下,基本常见的机器学习算法里面都有封装好。
基于SVM的点云分类(树木,建筑两类)_第19张图片
基于SVM的点云分类(树木,建筑两类)_第20张图片
和函数里面可以选线性函数,高斯函数等,这里就直接线性了。

4 分类结果

基于SVM的点云分类(树木,建筑两类)_第21张图片
基于SVM的点云分类(树木,建筑两类)_第22张图片

5. 评价

6.源代码

完整代码以及点云可以访问Github
https://github.com/YhQIAO/PointCloudSVMDemo.git

6.1 说明

程序结构说明
依赖文件:
MyHelper.py:封装计算特征值和特征向量的函数。
FileOperator.py: 封装文件读取和写入功能。
程序运行:
1.首先运行SaveFV.py,读取./points/trees以及./points/buildings中的训练样本数据,计算特征值,设树木标签为1,建筑标签为-1。保存的文件位于./FeatureVectors.txt。
2.再运行svmdemo.py,训练支持向量机分类器,并且读取待分类数据(位于./points/testdata/data1文件夹下),若分类为树木,则在每一点坐标后添加0 255 0,使其在导入CloudCompare时颜色为绿色;若分类为建筑,则在每一点坐标后添加0 0 255,使其在导入CloudCompare时颜色为蓝色。

测试步骤:
1.运行SaveFV.py,提取样本数据的特征向量,保存于FeatureVectors.txt文件中。
2.运行svmdemo.py,训练分类器,并对待分类数据进行分类
3.在CloudCompare中查看分类结果(蓝色的表示分类为建筑,绿色表示分类为树木)

文件路径格式说明
本程序运行在MacOS(类Unix,Linux)系统上,文件路径格式为./…/… ,若在windows环境下运行,需修改文件路径格式。如./points/trees需修改为D:\points\trees。

文件内容说明
所有点云数据在./points文件夹内
./points/buildings内为建筑样本
./points/trees内为树木样本
./points/testdata/data1 为BlockA_FULLA.txt测试数据
./points/testdata/data2 为BlockB_FULL.txt测试数据
./points/testdata/resultdata 为分类完成后的点云数据

6.2 代码

封装计算特征值函数

import numpy as np
from sklearn.neighbors import NearestNeighbors
from sklearn.decomposition import PCA

'''
计算各特征值的方法
#邻域分析,表面粗糙等
'''

#计算最小二乘平面及距离
def CaculateAverageSquareDistance(p):
    num = p.shape[0]
    B = np.zeros((p.shape[0],3))
    one = np.ones((p.shape[0],1))
    B[:,0] = p[:,0]
    B[:,1] = p[:,1]
    B[:,2] = one[:,0]
    l = p[:,2]
    BTB = np.matmul(B.T,B)
    BTB_1 = np.linalg.pinv(BTB)
    temp = np.matmul(BTB_1,B.T)
    result = np.matmul(temp,l)
    V  = np.matmul(B,result)-l
    sum = 0
    for i in range (0,V.shape[0]):
        sum = sum+V[i]**2
    return sum/V.shape[0]

#粗糙度
def CaculateRoughness(p):
    p1 = np.zeros((p.shape[0],3),float)
    p1[:,0] = p[:,0]
    p1[:,1] = p[:,1]
    p1[:,2] = p[:,2]
    #print(p1)

    neigh = NearestNeighbors(n_neighbors=8)
    neigh.fit(p1)
    #index = neigh.kneighbors([[1.04291550e+05,4.79228380e+05,-8.10000050e-01]],return_distance=False)

    sum = 0
    for i in range(p1.shape[0]):
        index = neigh.kneighbors([p1[i]],return_distance=False)
        avedis2 = CaculateAverageSquareDistance(p1[index].reshape(8,3))
        sum = sum+avedis2

    return sum/p1.shape[0]*100

'''
def PcaPartAna(p):
    p1 = np.zeros((p.shape[0],3),float)
    p1[:,0:3] = p[:,0:3]

    pca = PCA(n_components=3)
    newX = pca.fit_transform(p1)
    a =(pca.explained_variance_ratio_)
    return (a[2]/a[0])

def PCAPlaneAynalize(p):
    p1 = np.zeros((p.shape[0],3),float)
    p1[:,0] = p[:,0]
    p1[:,1] = p[:,1]
    p1[:,2] = p[:,2]
    #print(p1)

    neigh = NearestNeighbors(n_neighbors=20)
    neigh.fit(p1)
    #index = neigh.kneighbors([[1.04291550e+05,4.79228380e+05,-8.10000050e-01]],return_distance=False)

    sum = 0
    for i in range(p1.shape[0]):
        index = neigh.kneighbors([p1[i]],return_distance=False)
        avedis2 = PcaPartAna(p1[index].reshape(20,3))
        sum = sum+avedis2

    return sum/p1.shape[0]

    # p1 = np.zeros((p.shape[0],3),float)
    # p1[:,0:3] = p[:,0:3]

    # pca = PCA(n_components=3)
    # newX = pca.fit_transform(p1)
    # a =(pca.explained_variance_ratio_)
    # return (a[2]/a[0])
'''

#邻域分析
def NeiAna(p):
    p1 = p[:,0:3]

    sum = 0
    for i in range(0,p1.shape[0],3):
        neigh = NearestNeighbors(n_neighbors=20)
        neigh.fit(p1)
        index = neigh.kneighbors([p1[i]],return_distance=False)

        pp = p1[index].reshape(20,3)
        pp[:,0] = pp[:,0]-np.mean(p1[:,0])
        pp[:,1] = pp[:,1]-np.mean(p1[:,1])
        pp[:,2] = pp[:,2]-np.mean(p1[:,2])
        a = pp.T
        b  = np.matmul(pp.T,pp)
        fev = np.linalg.eigvals(b)
        fev = (np.sort(fev))
        sum = sum+(fev[0])/fev[2]*1000
    
    return (sum/p1.shape[0]/3)

#组成特征向量
def GetFeatureVector(p):
    fv = np.array([[0.0,0.0]],dtype=float)
    fv[0,0] = NeiAna(p)
    fv[0,1] = CaculateRoughness(p)
    return fv

文件操作

import numpy as np
import MyHelper
import FileOperator

ResultPath = "./points/testdata/resultData/re_"

def WriteData(path,v,type):
    f = open(path,'a')
    f.writelines(str(v[0,0])+","+str(v[0,1])+","+str(type)+"\n")
    f.close()

def WritePointCloud(ResultPath,p,label):
    #ResultPath = "./points/testdata/resultData/re_"+str(i)+".txt"
    f = open(ResultPath,'a')
    
    if label == 1:
        #给树着色 颜色为绿色
        for j in range(0,p.shape[0]):
            f.writelines(str(p[j,0])+" "+str(p[j,1])+" "+str(p[j,2])+" 0 "+"255 "+"0"+"\n")
        f.close()

    else:
        #给建筑着色 颜色为蓝色
        for j in range(0,p.shape[0]):
            f.writelines(str(p[j,0])+" "+str(p[j,1])+" "+str(p[j,2])+" 0 "+"0 "+"255"+"\n")
        f.close()

计算特征值并保存为文本

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import MyHelper
import FileOperator

f = open("./FeatureVectors.txt",'a')
f.seek(0)
f.truncate()
f.close()

print("-----trees-----")
for i in range(1,16):
    p= np.loadtxt("./points/trees/t"+str(i)+".txt")
    print(MyHelper.GetFeatureVector(p))
    v = MyHelper.GetFeatureVector(p)
    plt.scatter(v[0,0],v[0,1],c = 'g')
    FileOperator.WriteData("./FeatureVectors.txt",v,1)
    

print("-----buildings-----")
for i in range(1,16):
    p= np.loadtxt("./points/buildings/b"+str(i)+".txt")
    print(MyHelper.GetFeatureVector(p))
    v = MyHelper.GetFeatureVector(p)
    plt.scatter(v[0,0],v[0,1],c = 'b')
    FileOperator.WriteData("./FeatureVectors.txt",v,-1)

plt.show()

svm分类

from sklearn import svm
import random
import numpy as np
from matplotlib import pyplot as plt
import MyHelper
import FileOperator
import os

data = np.loadtxt("./FeatureVectors.txt",dtype = float,delimiter=',')

'''
for i in range(data.shape[0]):
    if data[i,2] == 1:
        plt.scatter(data[i,0],data[i,1],c = 'b')
    else:
        plt.scatter(data[i,0],data[i,1],c = 'r')
'''

# svm training
x,y=np.split(data,indices_or_sections=(2,),axis=1)

clf = svm.SVC(kernel='linear')
clf.fit(x,y)
print(clf)
#plt.show()

#print(clf.predict([[5.69866481e-05,1.02679807e+01]]))


pathStr = "./points/testdata/data1/pc_"
ResultPathStr = "./points/testdata/resultData/re_"

#清除文件夹内的所有文件
path = './points/testdata/resultData'
for i in os.listdir(path):
   path_file = os.path.join(path,i)  
   if os.path.isfile(path_file):
      os.remove(path_file)

for i in range(1,144):
    
    filepath = pathStr+str(i)+".txt"
    p= np.loadtxt(filepath)
    fv = MyHelper.GetFeatureVector(p)
    label = clf.predict(fv)
    resultPath = ResultPathStr +str(i)+".txt"
    FileOperator.WritePointCloud(resultPath,p,label)
    print("第"+str(i)+"个数据处理成功,标签为"+str(label))

你可能感兴趣的:(遥感,三维激光,点云,机器学习,SVM)