1 | """ |
||
2 | Point cloud segmentation using the DBSCAN clustering algorithm. |
||
3 | |||
4 | DBSCAN - Density-Based Spatial Clustering of Applications with Noise. |
||
5 | Finds core samples of high density and expands clusters from them. |
||
6 | Good for data which contains clusters of similar density. |
||
7 | |||
8 | See the scikit-learn documentation for reference: |
||
9 | http://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html. |
||
10 | """ |
||
11 | 1 | import numpy as np |
|
12 | 1 | from sklearn.cluster import dbscan |
|
13 | 1 | from patty.utils import extract_mask |
|
14 | |||
15 | |||
16 | 1 | def dbscan_labels(pointcloud, epsilon, minpoints, rgb_weight=0, |
|
17 | algorithm='ball_tree'): |
||
18 | ''' |
||
19 | Find an array of point-labels of clusters found by the DBSCAN algorithm. |
||
20 | |||
21 | Parameters |
||
22 | ---------- |
||
23 | pointcloud : pcl.PointCloud |
||
24 | Input pointcloud. |
||
25 | epsilon : float |
||
26 | Neighborhood radius for DBSCAN. |
||
27 | minpoints : integer |
||
28 | Minimum neighborhood density for DBSCAN. |
||
29 | rgb_weight : float, optional |
||
30 | If non-zero, cluster on color information as well as location; |
||
31 | specifies the relative weight of the RGB components to spatial |
||
32 | coordinates in distance computations. |
||
33 | (RGB values have wildly different scales than spatial coordinates.) |
||
34 | |||
35 | Returns |
||
36 | ------- |
||
37 | labels : Sequence |
||
38 | A sequence of labels per point. Label -1 indicates a point does not |
||
39 | belong to any cluster, other labels indicate the cluster number a |
||
40 | point belongs to. |
||
41 | ''' |
||
42 | |||
43 | 1 | if rgb_weight > 0: |
|
44 | 1 | X = pointcloud.to_array() |
|
0 ignored issues
–
show
|
|||
45 | 1 | X[:, 3:] *= rgb_weight |
|
46 | else: |
||
47 | 1 | X = pointcloud |
|
0 ignored issues
–
show
The name
X does not conform to the variable naming conventions ([a-z_][a-z0-9_]{1,30}$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. ![]() |
|||
48 | |||
49 | 1 | _, labels = dbscan(X, eps=epsilon, min_samples=minpoints, |
|
50 | algorithm=algorithm) |
||
51 | 1 | return np.asarray(labels) |
|
52 | |||
53 | |||
54 | 1 | def segment_dbscan(pointcloud, epsilon, minpoints, **kwargs): |
|
55 | """Run the DBSCAN clustering+outlier detection algorithm on pointcloud. |
||
56 | |||
57 | Parameters |
||
58 | ---------- |
||
59 | pointcloud : pcl.PointCloud |
||
60 | Input pointcloud. |
||
61 | epsilon : float |
||
62 | Neighborhood radius for DBSCAN. |
||
63 | minpoints : integer |
||
64 | Minimum neighborhood density for DBSCAN. |
||
65 | **kwargs : keyword arguments, optional |
||
66 | arguments passed to _dbscan_labels |
||
67 | |||
68 | Returns |
||
69 | ------- |
||
70 | clusters : iterable over registered PointCloud |
||
71 | """ |
||
72 | 1 | labels = dbscan_labels(pointcloud, epsilon, minpoints, **kwargs) |
|
73 | |||
74 | 1 | return (extract_mask(pointcloud, labels == label) |
|
75 | for label in np.unique(labels[labels != -1])) |
||
76 | |||
77 | |||
78 | 1 | def get_largest_dbscan_clusters(pointcloud, min_return_fragment=0.7, |
|
79 | epsilon=0.1, minpoints=250, rgb_weight=0): |
||
80 | ''' |
||
81 | Finds the largest clusters containing together at least min_return_fragment |
||
82 | of the complete point cloud. In case less points belong to clusters, all |
||
83 | clustered points are returned. |
||
84 | |||
85 | Parameters |
||
86 | ---------- |
||
87 | pointcloud : pcl.PointCloud |
||
88 | Input pointcloud. |
||
89 | min_return_fragment : float |
||
90 | Minimum desired fragment of pointcloud to be returned |
||
91 | epsilon : float |
||
92 | Neighborhood radius for DBSCAN. |
||
93 | minpoints : integer |
||
94 | Minimum neighborhood density for DBSCAN. |
||
95 | rgb_weight : float, optional |
||
96 | If non-zero, cluster on color information as well as location; |
||
97 | specifies the relative weight of the RGB components to spatial |
||
98 | coordinates in distance computations. |
||
99 | (RGB values have wildly different scales than spatial coordinates.) |
||
100 | |||
101 | Returns |
||
102 | ------- |
||
103 | cluster : pcl.PointCloud |
||
104 | Registered pointcloud of the largest cluster found by dbscan. |
||
105 | ''' |
||
106 | 1 | labels = dbscan_labels(pointcloud, epsilon, minpoints, |
|
107 | rgb_weight=rgb_weight).astype(np.int64) |
||
108 | 1 | selection, selected_count = _get_top_labels(labels, min_return_fragment) |
|
109 | |||
110 | # No clusters were found |
||
111 | 1 | if selected_count < min_return_fragment * len(labels): |
|
112 | return extract_mask(pointcloud, np.ones(len(pointcloud), dtype=bool)) |
||
113 | else: |
||
114 | 1 | mask = [label in selection for label in labels] |
|
115 | 1 | return extract_mask(pointcloud, mask) |
|
116 | |||
117 | |||
118 | 1 | def _get_top_labels(labels, min_return_fragment): |
|
119 | """Return labels of the smallest set of clusters that contain at least |
||
120 | min_return_fragment of the points (or everything).""" |
||
121 | |||
122 | # +1 to make bincount happy, [1:] to get rid of outliers. |
||
123 | 1 | bins = np.bincount(labels + 1)[1:] |
|
124 | 1 | labelbinpairs = sorted(enumerate(bins), key=lambda x: x[1]) |
|
125 | |||
126 | 1 | total = len(labels) |
|
127 | 1 | minimum = min_return_fragment * total |
|
128 | 1 | selected = [] |
|
129 | 1 | selected_count = 0 |
|
130 | 1 | while selected_count < minimum and len(labelbinpairs) > 0: |
|
131 | 1 | label, count = labelbinpairs.pop() |
|
132 | 1 | selected.append(label) |
|
133 | 1 | selected_count += count |
|
134 | return selected, selected_count |
||
135 |
This check looks for invalid names for a range of different identifiers.
You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.
If your project includes a Pylint configuration file, the settings contained in that file take precedence.
To find out more about Pylint, please refer to their site.