Skip to content

Commit a296593

Browse files
committed
merged bayesian stuff
2 parents 0af8da0 + bbd355f commit a296593

File tree

9 files changed

+174
-20
lines changed

9 files changed

+174
-20
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
.run/
77
docs/build
88
_build
9+

elevation_mapping_cupy/script/elevation_mapping_cupy/custom_kernels.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,50 @@ def sum_kernel(
694694
)
695695
return sum_kernel
696696

697+
def alpha_kernel(
698+
resolution,
699+
width,
700+
height,
701+
):
702+
# input the list of layers, amount of channels can slo be input through kernel
703+
alpha_kernel = cp.ElementwiseKernel(
704+
in_params="raw U p, raw W pcl_chan, raw W map_lay, raw W pcl_channels",
705+
out_params="raw U newmap",
706+
preamble=string.Template(
707+
"""
708+
__device__ int get_map_idx(int idx, int layer_n) {
709+
const int layer = ${width} * ${height};
710+
return layer * layer_n + idx;
711+
}
712+
"""
713+
).substitute(resolution=resolution,width=width, height=height),
714+
operation=string.Template(
715+
"""
716+
U idx = p[i * pcl_channels[0]];
717+
U valid = p[i * pcl_channels[0] + 1];
718+
U inside = p[i * pcl_channels[0] + 2];
719+
if (valid) {
720+
if (inside) {
721+
U theta_max = 0;
722+
W arg_max = 0;
723+
for ( W it=0;it<pcl_channels[1];it++){
724+
U theta = p[i * pcl_channels[0] + pcl_chan[it]];
725+
if (theta >=theta_max){
726+
arg_max = map_lay[it];
727+
theta_max = theta;
728+
}
729+
}
730+
atomicAdd(&newmap[get_map_idx(idx, arg_max)], 1.0);
731+
}
732+
}
733+
"""
734+
).substitute(
735+
),
736+
name="alpha_kernel",
737+
)
738+
return alpha_kernel
739+
740+
697741

698742
def average_kernel(
699743
width,
@@ -875,7 +919,6 @@ def color_average_kernel(
875919
)
876920
return color_average_kernel
877921

878-
# TODO: in future add more kernels e.g. bayesian kernel
879922

880923
if __name__ == "__main__":
881924
for i in range(10):

elevation_mapping_cupy/script/elevation_mapping_cupy/plugins/semantic_filter.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,35 @@ def __init__(
2424
self,
2525
cell_n: int = 100,
2626
classes: list = ["person", "grass"],
27-
colors: list = [
28-
[0, 255, 0],
29-
[0, 255, 255],
30-
[0, 0, 255],
31-
[255, 255, 0],
32-
[255, 0, 0],
33-
],
3427
**kwargs,
3528
):
3629
super().__init__()
3730
self.indices = []
3831
self.classes = classes
39-
color_classes = np.array(colors)
40-
self.color_encoding = self.transform_color(color_classes)
32+
self.color_encoding = self.transform_color()
4133

42-
def transform_color(self, color_classes):
34+
def color_map(self, N=256, normalized=False):
35+
def bitget(byteval, idx):
36+
return (byteval & (1 << idx)) != 0
37+
38+
dtype = "float32" if normalized else "uint8"
39+
cmap = np.zeros((N+1, 3), dtype=dtype)
40+
for i in range(N+1):
41+
r = g = b = 0
42+
c = i
43+
for j in range(8):
44+
r = r | (bitget(c, 0) << 7 - j)
45+
g = g | (bitget(c, 1) << 7 - j)
46+
b = b | (bitget(c, 2) << 7 - j)
47+
c = c >> 3
48+
49+
cmap[i] = np.array([r, g, b])
50+
51+
cmap = cmap / 255 if normalized else cmap
52+
return cmap[1:]
53+
54+
def transform_color(self):
55+
color_classes = self.color_map(len(self.classes))
4356
r = np.asarray(color_classes[:, 0], dtype=np.uint32)
4457
g = np.asarray(color_classes[:, 1], dtype=np.uint32)
4558
b = np.asarray(color_classes[:, 2], dtype=np.uint32)
@@ -59,9 +72,7 @@ def __call__(
5972
# get indices of all layers that
6073
layer_indices = cp.array([], dtype=cp.int32)
6174
for it, fusion_alg in enumerate(semantic_map.param.fusion_algorithms):
62-
if fusion_alg == "class_average" and (
63-
semantic_map.param.additional_layers[it] in self.classes
64-
):
75+
if (fusion_alg in ["class_bayesian","class_average"]):
6576
layer_indices = cp.append(layer_indices, it).astype(cp.int32)
6677

6778
# check which has the highest value

elevation_mapping_cupy/script/elevation_mapping_cupy/semantic_map.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
color_average_kernel,
99
average_kernel,
1010
class_average_kernel,
11+
alpha_kernel,
1112
)
1213

1314
xp = cp
@@ -74,7 +75,7 @@ def compile_kernels(self) -> None:
7475
self.param.cell_n, self.param.cell_n
7576
)
7677
if "class_average" in self.unique_fusion:
77-
print("Initialize fusion kernel")
78+
print("Initialize class average kernel")
7879
self.sum_kernel = sum_kernel(
7980
self.param.resolution,
8081
self.param.cell_n,
@@ -84,6 +85,13 @@ def compile_kernels(self) -> None:
8485
self.param.cell_n,
8586
self.param.cell_n,
8687
)
88+
if "class_bayesian" in self.unique_fusion:
89+
print("Initialize class bayesian kernel")
90+
self.alpha_kernel = alpha_kernel(
91+
self.param.resolution,
92+
self.param.cell_n,
93+
self.param.cell_n,
94+
)
8795

8896
def get_fusion_of_pcl(self, channels: List[str]) -> List[str]:
8997
"""Get all fusion algorithms that need to be applied to a specific pointcloud
@@ -99,7 +107,7 @@ def get_fusion_of_pcl(self, channels: List[str]) -> List[str]:
99107
return fusion_list
100108

101109
def get_indices_fusion(self, pcl_channels: List[str], fusion_alg: str):
102-
"""Computes the indices that are used for the additional kernel update of the pcl and the elmap.
110+
"""Computes the indices that are used for the additional kernel update of the popintcloud and the semantic map.
103111
104112
Args:
105113
pcl_channels (List[str]):
@@ -172,6 +180,25 @@ def update_layers(self, points_all, channels, R, t, elevation_map):
172180
size=(self.param.cell_n * self.param.cell_n),
173181
)
174182

183+
if "class_bayesian" in additional_fusion:
184+
pcl_ids, layer_ids = self.get_indices_fusion(channels, "class_bayesian")
185+
# alpha sum get points as input and calculate for each point to what cell it belongs and then
186+
# adds to the right channel a one
187+
self.alpha_kernel(
188+
points_all,
189+
pcl_ids,
190+
layer_ids,
191+
cp.array([points_all.shape[1], pcl_ids.shape[0]], dtype=np.int32),
192+
self.new_map,
193+
size=(points_all.shape[0]),
194+
)
195+
# calculate new thetas
196+
sum_alpha = cp.sum(self.new_map[layer_ids], axis=0)
197+
self.map[layer_ids] = self.new_map[layer_ids] / cp.expand_dims(
198+
sum_alpha, axis=0
199+
)
200+
# assert cp.unique(cp.sum(self.map[layer_ids], axis=0)) equal to zero or to nan
201+
175202
if "color" in additional_fusion:
176203
pcl_ids, layer_ids = self.get_indices_fusion(channels, "color")
177204
if self.color_map is None:

elevation_mapping_cupy/script/elevation_mapping_cupy/tests/test_elevation_mapping.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def elmap_ex(add_lay, fusion_alg):
2525
[
2626
(["feat_0", "feat_1", "rgb"], ["average", "average", "color"]),
2727
(["feat_0", "feat_1"], ["average", "average"]),
28+
(["feat_0", "feat_1"], ["class_average", "class_average"]),
29+
(["feat_0", "feat_1"], ["class_bayesian", "class_bayesian"]),
2830
],
2931
)
3032
class TestElevationMap:

elevation_mapping_cupy/script/elevation_mapping_cupy/tests/test_semantic_map.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ def semmap_ex(add_lay, fusion_alg):
2626
[(["feat_0", "feat_1"], ["average", "average"],["feat_0"]),
2727
(["feat_0", "feat_1"], ["average", "average"],[]),
2828
(["feat_0", "feat_1", "rgb"], ["average", "average", "color"],["rgb", "feat_0"]),
29+
(["feat_0", "feat_1", "rgb"], ["class_average", "average", "color"], ["rgb", "feat_0"]),
30+
(["feat_0", "feat_1", "rgb"], ["class_bayesian", "average", "color"], ["rgb", "feat_0"]),
31+
(["feat_0", "feat_1", "rgb"], ["class_bayesian", "average", "color"], ["rgb", "feat_0", "feat_1"]),
2932
],
3033
)
3134
def test_fusion_of_pcl(semmap_ex, channels):
@@ -42,4 +45,4 @@ def test_fusion_of_pcl(semmap_ex, channels):
4245
)
4346
@pytest.mark.parametrize("channels", [["rgb"], ["rgb", "feat_0"], []])
4447
def test_indices_fusion(semmap_ex, channels,fusion_alg):
45-
pcl_indices, layer_indices = semmap_ex.get_indices_fusion(pcl_channels=channels,fusion_alg=fusion_alg[0])
48+
pcl_indices, layer_indices = semmap_ex.get_indices_fusion(pcl_channels=channels, fusion_alg=fusion_alg[0])

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ scipy
44
dataclasses
55
ruamel.yaml
66
opencv-python
7-
simple-parsing
7+
simple_parsing
8+
scikit-image
89

910
###### Requirements with Version Specifiers ######`
1011
shapely==1.7.1

sensor_processing/semantic_pointcloud/script/semantic_pointcloud/pointcloud_node.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import numpy as np
55
import cupy as cp
66
import ros_numpy
7+
import matplotlib.pyplot as plt
8+
from skimage.io import imshow
79

810
import message_filters
911
from sensor_msgs.msg import Image, CameraInfo
10-
from sensor_msgs.msg import PointCloud2
12+
from sensor_msgs.msg import PointCloud2, Image
1113
from cv_bridge import CvBridge
1214

1315
from semantic_pointcloud.pointcloud_parameters import PointcloudParameter
@@ -67,6 +69,52 @@ def __init__(self, sensor_name):
6769
ts.registerCallback(self.image_callback)
6870

6971
self.pcl_pub = rospy.Publisher(self.param.topic_name, PointCloud2, queue_size=2)
72+
if self.param.publish_segmentation_image:
73+
self.seg_pub = rospy.Publisher(
74+
self.param.segmentation_image_topic, Image, queue_size=2
75+
)
76+
self.semseg_color_map = self.color_map(
77+
len(list(self.segmentation_channels.keys()))
78+
)
79+
self.color_map_viz()
80+
81+
def color_map(self, N=256, normalized=False):
82+
def bitget(byteval, idx):
83+
return (byteval & (1 << idx)) != 0
84+
85+
dtype = "float32" if normalized else "uint8"
86+
cmap = np.zeros((N + 1, 3), dtype=dtype)
87+
for i in range(N + 1):
88+
r = g = b = 0
89+
c = i
90+
for j in range(8):
91+
r = r | (bitget(c, 0) << 7 - j)
92+
g = g | (bitget(c, 1) << 7 - j)
93+
b = b | (bitget(c, 2) << 7 - j)
94+
c = c >> 3
95+
96+
cmap[i] = np.array([r, g, b])
97+
98+
cmap = cmap / 255 if normalized else cmap
99+
return cmap[1:]
100+
101+
def color_map_viz(self):
102+
# labels = self.semantic_model["model"].get_classes()
103+
labels = list(self.segmentation_channels.keys())
104+
nclasses = len(labels)
105+
row_size = 50
106+
col_size = 500
107+
cmap = self.semseg_color_map
108+
array = np.empty(
109+
(row_size * (nclasses), col_size, cmap.shape[1]), dtype=cmap.dtype
110+
)
111+
for i in range(nclasses):
112+
array[i * row_size : i * row_size + row_size, :] = cmap[i]
113+
imshow(array)
114+
plt.yticks([row_size * i + row_size / 2 for i in range(nclasses)], labels)
115+
plt.xticks([])
116+
plt.show()
117+
# plt.savefig("./labels.png")
70118

71119
def initialize_semantics(self):
72120
if self.param.semantic_segmentation:
@@ -81,10 +129,12 @@ def initialize_semantics(self):
81129
)
82130
indices = []
83131
channels = []
84-
for chan in self.param.channels:
132+
for it, chan in enumerate(self.param.channels):
85133
if chan in [cls for cls in list(class_to_idx.keys())]:
86134
indices.append(class_to_idx[chan])
87135
channels.append(chan)
136+
elif self.param.fusion[it] in ["class_average", "class_bayesian"]:
137+
print(chan, " is not in the semantic segmentation model.")
88138
self.segmentation_channels = dict(zip(channels, indices))
89139
if self.param.feature_extractor:
90140
self.feature_channels = []
@@ -111,6 +161,7 @@ def cam_info_callback(self, msg):
111161
self.P = cp.resize(a, (3, 4))
112162
self.height = msg.height
113163
self.width = msg.width
164+
self.header = msg.header
114165

115166
def image_callback(self, rgb_msg, depth_msg, confidence_msg=None):
116167
confidence = None
@@ -190,13 +241,26 @@ def perform_segmentation(self, image, points, u, v):
190241
values = mask[:, v.get(), u.get()].cpu().detach().numpy()
191242
for it, channel in enumerate(self.segmentation_channels.keys()):
192243
points[channel] = values[it]
244+
if self.param.publish_segmentation_image:
245+
self.publish_segmentation_image(mask)
193246

194247
def extract_features(self, image, points, u, v):
195248
prediction = self.feature_extractor["model"](image)
196249
values = prediction[:, v.get(), u.get()].cpu().detach().numpy()
197250
for it, channel in enumerate(self.feature_channels):
198251
points[channel] = values[it]
199252

253+
def publish_segmentation_image(self, probabilities):
254+
probabilities = cp.asarray(probabilities)
255+
colors = cp.asarray(self.semseg_color_map)
256+
assert colors.ndim == 2 and colors.shape[1] == 3
257+
img = cp.argmax(probabilities, axis=0)
258+
img = colors[img].astype(cp.uint8) # N x H x W x 3
259+
img = img.get()
260+
seg_msg = self.cv_bridge.cv2_to_imgmsg(img, encoding="rgb8")
261+
seg_msg.header.frame_id = self.header.frame_id
262+
self.seg_pub.publish(seg_msg)
263+
200264

201265
if __name__ == "__main__":
202266
sensor_name = sys.argv[1]

sensor_processing/semantic_pointcloud/script/semantic_pointcloud/pointcloud_parameters.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class PointcloudParameter(Serializable):
2525

2626
semantic_segmentation: bool = True
2727
segmentation_model: str = "detectron_coco_panoptic_fpn_R_101_3x"
28+
publish_segmentation_image: bool = True
29+
segmentation_image_topic: str = "/semantic_pointcloud/sem_seg"
2830

2931
cam_info_topic: str = "/zed2i/zed_node/depth/camera_info"
3032
image_topic: str = "/zed2i/zed_node/left/image_rect_color"

0 commit comments

Comments
 (0)