forked from JaminJiang/dbscan_kdtree
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DBSCAN_simple.h
145 lines (125 loc) · 5.28 KB
/
DBSCAN_simple.h
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#ifndef DBSCAN_H
#define DBSCAN_H
#include <pcl/point_types.h>
#define UN_PROCESSED 0
#define PROCESSING 1
#define PROCESSED 2
inline bool comparePointClusters (const pcl::PointIndices &a, const pcl::PointIndices &b) {
return (a.indices.size () < b.indices.size ());
}
template <typename PointT>
class DBSCANSimpleCluster {
public:
typedef typename pcl::PointCloud<PointT>::Ptr PointCloudPtr;
typedef typename pcl::search::KdTree<PointT>::Ptr KdTreePtr;
virtual void setInputCloud(PointCloudPtr cloud) {
input_cloud_ = cloud;
}
void setSearchMethod(KdTreePtr tree) {
search_method_ = tree;
}
void extract(std::vector<pcl::PointIndices>& cluster_indices) {
std::vector<int> nn_indices;
std::vector<float> nn_distances;
std::vector<bool> is_noise(input_cloud_->points.size(), false);
std::vector<int> types(input_cloud_->points.size(), UN_PROCESSED);
for (int i = 0; i < input_cloud_->points.size(); i++) {
if (types[i] == PROCESSED) {
continue;
}
int nn_size = radiusSearch(i, eps_, nn_indices, nn_distances);
if (nn_size < minPts_) {
is_noise[i] = true;
continue;
}
std::vector<int> seed_queue;
seed_queue.push_back(i);
types[i] = PROCESSED;
for (int j = 0; j < nn_size; j++) {
if (nn_indices[j] != i) {
seed_queue.push_back(nn_indices[j]);
types[nn_indices[j]] = PROCESSING;
}
} // for every point near the chosen core point.
int sq_idx = 1;
while (sq_idx < seed_queue.size()) {
int cloud_index = seed_queue[sq_idx];
if (is_noise[cloud_index] || types[cloud_index] == PROCESSED) {
// seed_queue.push_back(cloud_index);
types[cloud_index] = PROCESSED;
sq_idx++;
continue; // no need to check neighbors.
}
nn_size = radiusSearch(cloud_index, eps_, nn_indices, nn_distances);
if (nn_size >= minPts_) {
for (int j = 0; j < nn_size; j++) {
if (types[nn_indices[j]] == UN_PROCESSED) {
seed_queue.push_back(nn_indices[j]);
types[nn_indices[j]] = PROCESSING;
}
}
}
types[cloud_index] = PROCESSED;
sq_idx++;
}
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 (int 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 = input_cloud_->header;
cluster_indices.push_back (r); // We could avoid a copy by working directly in the vector
}
} // for every point in input cloud
std::sort (cluster_indices.rbegin (), cluster_indices.rend (), comparePointClusters);
}
void setClusterTolerance(double tolerance) {
eps_ = tolerance;
}
void setMinClusterSize (int min_cluster_size) {
min_pts_per_cluster_ = min_cluster_size;
}
void setMaxClusterSize (int max_cluster_size) {
max_pts_per_cluster_ = max_cluster_size;
}
void setCorePointMinPts(int core_point_min_pts) {
minPts_ = core_point_min_pts;
}
protected:
PointCloudPtr input_cloud_;
double eps_ {0.0};
int minPts_ {1}; // not including the point itself.
int min_pts_per_cluster_ {1};
int max_pts_per_cluster_ {std::numeric_limits<int>::max()};
KdTreePtr search_method_;
virtual int radiusSearch(
int index, double radius, std::vector<int> &k_indices,
std::vector<float> &k_sqr_distances) const
{
k_indices.clear();
k_sqr_distances.clear();
k_indices.push_back(index);
k_sqr_distances.push_back(0);
int size = input_cloud_->points.size();
double radius_square = radius * radius;
for (int i = 0; i < size; i++) {
if (i == index) {
continue;
}
double distance_x = input_cloud_->points[i].x - input_cloud_->points[index].x;
double distance_y = input_cloud_->points[i].y - input_cloud_->points[index].y;
double distance_z = input_cloud_->points[i].z - input_cloud_->points[index].z;
double distance_square = distance_x * distance_x + distance_y * distance_y + distance_z * distance_z;
if (distance_square <= radius_square) {
k_indices.push_back(i);
k_sqr_distances.push_back(std::sqrt(distance_square));
}
}
return k_indices.size();
}
}; // class DBSCANCluster
#endif // DBSCAN_H