-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcar_fuser.py
More file actions
137 lines (112 loc) · 5.17 KB
/
car_fuser.py
File metadata and controls
137 lines (112 loc) · 5.17 KB
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
# Urban_PointCloud_Processing by Amsterdam Intelligence, GPL-3.0 license
"""Car Fuser"""
import numpy as np
import logging
from shapely.geometry import Polygon
from ..abstract_processor import AbstractProcessor
from ..region_growing.label_connected_comp import LabelConnectedComp
from ..utils.math_utils import minimum_bounding_rectangle
from ..utils.clip_utils import poly_box_clip
from ..labels import Labels
logger = logging.getLogger(__name__)
class CarFuser(AbstractProcessor):
"""
Parameters
----------
label : int
Class label to use for this fuser.
ahn_reader : AHNReader object
Elevation data reader.
bgt_reader : BGTPolyReader object
Used to load road part polygons.
"""
def __init__(self, label, ahn_reader, bgt_reader,
grid_size=0.05, min_component_size=5000,
overlap_perc=20, params={},
exclude_types=["fietspad", "voetpad"]):
super().__init__(label)
self.ahn_reader = ahn_reader
self.bgt_reader = bgt_reader
self.grid_size = grid_size
self.min_component_size = min_component_size
self.overlap_perc = overlap_perc
self.params = params
# TODO: work with include list instead of exclude
self.exclude_types = exclude_types
def _label_car_like_components(self, points, ground_z, point_components,
road_polygons, min_height, max_height,
min_width, max_width, min_length,
max_length):
""" Based on certain properties of a car we label clusters. """
car_mask = np.zeros(len(points), dtype=bool)
car_count = 0
cc_labels = np.unique(point_components)
cc_labels = set(cc_labels).difference((-1,))
for cc in cc_labels:
# select points that belong to the cluster
cc_mask = (point_components == cc)
target_z = ground_z[cc_mask]
valid_values = target_z[np.isfinite(target_z)]
if valid_values.size != 0:
cc_z = np.mean(valid_values)
min_z = cc_z + min_height
max_z = cc_z + max_height
cluster_height = np.amax(points[cc_mask][:, 2])
if min_z <= cluster_height <= max_z:
mbrect, _, mbr_width, mbr_length, _ =\
minimum_bounding_rectangle(points[cc_mask][:, :2])
poly = np.vstack((mbrect, mbrect[0]))
if (min_width < mbr_width < max_width and
min_length < mbr_length < max_length):
p1 = Polygon(poly)
for p2 in road_polygons:
do_overlap = p1.intersects(p2)
if do_overlap:
intersection_perc = (p1.intersection(p2).area
/ p1.area) * 100
if intersection_perc > self.overlap_perc:
car_mask = car_mask | poly_box_clip(
points, p1, bottom=cc_z, top=max_z)
car_count += 1
break
logger.debug(f'{car_count} cars labelled.')
return car_mask
def get_labels(self, points, labels, mask, tilecode):
"""
Returns the labels for the given pointcloud.
Parameters
----------
points : array of shape (n_points, 3)
The point cloud <x, y, z>.
labels : array of shape (n_points,)
Ignored by this class.
mask : array of shape (n_points,) with dtype=bool
Pre-mask used to label only a subset of the points.
tilecode : str
The CycloMedia tile-code for the given pointcloud.
Returns
-------
An array of shape (n_points,) with the updated labels.
"""
logger.info('Car fuser ' +
f'(label={self.label}, {Labels.get_str(self.label)}).')
label_mask = np.zeros((len(points),), dtype=bool)
road_polygons = self.bgt_reader.filter_tile(
tilecode, exclude_types=self.exclude_types, merge=False)
if len(road_polygons) == 0:
logger.debug('No road parts found for tile, skipping.')
return labels
# Get the interpolated ground points of the tile
ground_z = self.ahn_reader.interpolate(
tilecode, points[mask], mask, 'ground_surface')
lcc = LabelConnectedComp(self.label, grid_size=self.grid_size,
min_component_size=self.min_component_size)
point_components = lcc.get_components(points[mask])
# Label car like clusters
car_mask = self._label_car_like_components(points[mask], ground_z,
point_components,
road_polygons,
**self.params)
label_mask[mask] = car_mask
labels[label_mask] = self.label
return labels