欧式聚类(C++编写实现<不需要任何库>)

欧式聚类(C++编写实现<不需要任何库>)

简介:
应用图

本项目应用背景:点云数在1000个以内,但需要使用欧式聚类,故过程中未使用建树过程,针对实际项目接口编写,此项目数据结构 (struct OBJPOINT) 为项目中数据结构,不能改变,故采用pair反应点状态。输入接口有两个 (ImportPointCloud 、SetDate), 一个测试使用,一个实际项目接口使用。

1、设置三个参数

**tolerance **是设置 kdtree 的近邻搜索的搜索半径,从实验结果来看,tolerance 越大,检测到的范围也就越大; 同时如果搜索半径取一个非常小的值,那么一个实际的对象就会被分割为多个聚类;如果将值设置得太高,那么多个对象就会被分割为一个聚类;
**setMinClusterSize()**来限制一个聚类最少需要的点数目;
**setMaXClusterSize()**来限制最多需要的点数目;这两个不要解释;

2、聚类过程

1、 找到空间中某点p10,有kdTree找到离他最近的n个点,判断这n个点到p的距离。将距离小于阈值r的点p12,p13,p14…放在类Q里

2、 在 Q(p10) 里找到一点p12,重复1

3 、在 Q(p10,p12) 找到一点,重复1,找到p22,p23,p24…全部放进Q里

4 、当 Q 再也不能有新点加入了,则完成搜索了;

3、PCL源码解析
pcl::extractEuclideanClusters (const PointCloud<PointT> &cloud,
                               const typename search::Search<PointT>::Ptr &tree,
                               float tolerance, std::vector<PointIndices> &clusters,
                               unsigned int min_pts_per_cluster,
                               unsigned int max_pts_per_cluster)
{
  if (tree->getInputCloud ()->points.size () != cloud.points.size ())   // 点数量检查
  {
    PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", tree->getInputCloud ()->points.size (), cloud.points.size ());
    return;
  }
  // Check if the tree is sorted -- if it is we don't need to check the first element
  int nn_start_idx = tree->getSortedResults () ? 1 : 0;
  // Create a bool vector of processed point indices, and initialize it to false
  std::vector<bool> processed (cloud.points.size (), false);
  std::vector<int> nn_indices;
  std::vector<float> nn_distances;   // 定义需要的变量


  // Process all points in the indices vector
  for (int i = 0; i < static_cast<int> (cloud.points.size ()); ++i)   //遍历点云中的每一个点
  {
    if (processed[i])   //如果该点已经处理则跳过
      continue;

    std::vector<int> seed_queue;   //定义一个种子队列

    int sq_idx = 0;
    seed_queue.push_back (i);  //加入一个种子


    processed[i] = true;

    while (sq_idx < static_cast<int> (seed_queue.size ()))    //遍历每一个种子
    {
      // Search for sq_idx  kdtree 树的近邻搜索
      if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances))  
      {
        sq_idx++;
        continue;   //没找到近邻点就继续
      }

      for (size_t j = nn_start_idx; j < nn_indices.size (); ++j)             // can't assume sorted (default isn't!)
      {
        if (nn_indices[j] == -1 || processed[nn_indices[j]])        // Has this point been processed before ?
          continue;   // 种子点的近邻点中如果已经处理就跳出此次循环继续

        // Perform a simple Euclidean clustering
        seed_queue.push_back (nn_indices[j]);   //将此种子点的临近点作为新的种子点。入队操作
        processed[nn_indices[j]] = true;  // 该点已经处理,打标签
      }

      sq_idx++;
    }

    // If this queue is satisfactory, add to the clusters    最大点数和最小点数的类过滤
    if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster)
    {
      pcl::PointIndices r;
      r.indices.resize (seed_queue.size ());
      for (size_t j = 0; j < seed_queue.size (); ++j)
        r.indices[j] = seed_queue[j];

      // These two lines should not be needed: (can anyone confirm?) -FF
      std::sort (r.indices.begin (), r.indices.end ());
      r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());

      r.header = cloud.header;
      clusters.push_back (r);   // We could avoid a copy by working directly in the vector
    }
  }
}
4、C++编写

adapt_cluster.h文件

#pragma once
#include <vector>

struct OBJPOINT
{
	int nid;//ID
	double dx;
	double dy;
	double dz;
	double devp;//重投影误差
	OBJPOINT()
	{
		nid = -1;
		devp = 0;
	}
};

class ADAPT_Cluster
{
public:
	ADAPT_Cluster();
	~ADAPT_Cluster();
	void ImportPointCloud(char& filename);
	void SetDate(std::vector<double*>& vPoints, double& R, int minPts, int maxPts);
	bool RadiusSearch(OBJPOINT& mPoint, std::vector<std::pair<OBJPOINT, bool>>& mvPtStatusList);  // Neighborhood radius search
	void ExtractEuclideanClusters();   // Euclidean clustering
	void slove();  // Test function

public:
	double radius;
	int minPtsPerCluster;
	int maxPtsPerCluster;

private:
	// The status of each point
	std::vector<std::pair<OBJPOINT, bool>> vPtStatusList; 
};

adapt_cluster.cpp 文件

#include <iostream>
#include <fstream>
#include "adapt_cluster.h"

ADAPT_Cluster::ADAPT_Cluster()
{
    radius = 0;
    minPtsPerCluster = 0;
    maxPtsPerCluster = INT_MAX;
}

ADAPT_Cluster::~ADAPT_Cluster()
{}

void ADAPT_Cluster::ImportPointCloud(char& filename)
{
    OBJPOINT temp;
    bool mStatus = false;
    std::ifstream import;
    import.open(&filename);
    while (import.peek() != EOF)
    {
        import >> temp.dx >> temp.dy >> temp.dz ;
        vPtStatusList.push_back(std::pair<OBJPOINT, bool>(temp, mStatus));
    }
    import.close();

    radius = 1;
    minPtsPerCluster = 0;
    maxPtsPerCluster = 1000;

}

void ADAPT_Cluster::SetDate(std::vector<double*>& vPoints, double& R, int minPts, int maxPts)
{
    radius = R;
    minPtsPerCluster = minPts;
    maxPtsPerCluster = maxPts;

    OBJPOINT mPt;
    bool mStatus;
    for (int i = 0; i < static_cast<int>(vPoints.size()); ++i)
    {
        mPt.dx = vPoints[i][1];
        mPt.dy = vPoints[i][2];
        mPt.dz = vPoints[i][3];
        mStatus = false;          // Initialize all point states
        vPtStatusList.push_back(std::pair<OBJPOINT, bool>(mPt, mStatus));
    }
}

bool ADAPT_Cluster::RadiusSearch(OBJPOINT& mPoint, std::vector<std::pair<OBJPOINT, bool>>& mvPtStatusList)
{
    OBJPOINT nPt;
    bool nStatus;
    int num = 0;  // Point within radius
    for (int i = 0; i < static_cast<int>(vPtStatusList.size()); ++i) //Status is false
    {
        double dis = 0, disX, disY, disZ;
        if (!vPtStatusList[i].second)
        {
            disX = (mPoint.dx - vPtStatusList[i].first.dx) * (mPoint.dx - vPtStatusList[i].first.dx);
            disY = (mPoint.dy - vPtStatusList[i].first.dy) * (mPoint.dy - vPtStatusList[i].first.dy);
            disZ = (mPoint.dz - vPtStatusList[i].first.dz) * (mPoint.dz - vPtStatusList[i].first.dz);
            dis = sqrt(disX + disY + disZ);

            if (dis < radius)
            {
                nPt.dx = vPtStatusList[i].first.dx;
                nPt.dy = vPtStatusList[i].first.dy;
                nPt.dz = vPtStatusList[i].first.dz;
                nStatus = vPtStatusList[i].second;
                mvPtStatusList.push_back(std::pair<OBJPOINT, bool>(nPt, nStatus));
                num++;
            }
        }
    }
    if (num == 0)
        return false;
    else
        return true;
}

void ADAPT_Cluster::ExtractEuclideanClusters()
{
    std::vector<std::pair<OBJPOINT, bool>> seedvPtStatusList;
    for (int i = 0; i < static_cast<int>(vPtStatusList.size()); ++i)
    {
        // Skip if the point has been processed
        if (vPtStatusList[i].second)
            continue;

        vPtStatusList[i].second = true;
        seedvPtStatusList.push_back(std::pair<OBJPOINT, bool>(vPtStatusList[i].first, vPtStatusList[i].second));

        int n = 0;
        while (n < static_cast<int>(seedvPtStatusList.size()))
        {
            std::vector<std::pair<OBJPOINT, bool>> RvPtStatusList;
            if (!RadiusSearch(seedvPtStatusList[n].first, RvPtStatusList))
            {
                n++;
                continue;   // Continue without finding a neighbor
            }

            for (int j = 0; j < static_cast<int>(RvPtStatusList.size()); ++j)
            {
                seedvPtStatusList.push_back(std::pair<OBJPOINT, bool>(RvPtStatusList[j].first, RvPtStatusList[j].second));
                for (int m = 0; m < static_cast<int>(vPtStatusList.size()); ++m)
                {
                    if (RvPtStatusList[j].first.dx == vPtStatusList[m].first.dx &&
                        RvPtStatusList[j].first.dy == vPtStatusList[m].first.dy &&
                        RvPtStatusList[j].first.dz == vPtStatusList[m].first.dz &&
                        vPtStatusList[m].second == false)
                    {vPtStatusList[m].second = true; break;}
                }
            }
            RvPtStatusList.clear();
            n++;
        }

        // Judge the number of each category
        if (seedvPtStatusList.size() >= minPtsPerCluster && seedvPtStatusList.size() <= maxPtsPerCluster)
        {
            for (int a = 0; a < static_cast<int>(seedvPtStatusList.size()); ++a)
            {
                std::cout << seedvPtStatusList[a].first.dx << " "
                          << seedvPtStatusList[a].first.dy << " "
                          << seedvPtStatusList[a].first.dz << std::endl;
            }
        }

        std::cout << std::endl;
        seedvPtStatusList.clear();
    }
}

void ADAPT_Cluster::slove()
{
    char filename[255];
    sprintf_s(filename, 255, "import3.asc");
    ImportPointCloud(*filename);
    ExtractEuclideanClusters();
}

int main()
{
    ADAPT_Cluster mADAPT_Cluster;
    mADAPT_Cluster.slove();
    return 0;
}

5、测试效果
应用图
应用图
  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值