bev-project/tools/convert_bevfusion_output_to...

256 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
将BEVFusion实时输出转换为CARLA可用格式
用于在CARLA仿真中实时显示BEVFusion的检测和地图结果
使用方法:
python tools/convert_bevfusion_output_to_carla.py \
--bevfusion-output results/predictions.pkl \
--output carla_visualization.json
"""
import pickle
import argparse
import json
import numpy as np
class BEVFusionToCarlaConverter:
"""BEVFusion输出转CARLA可视化格式"""
def __init__(self):
pass
def convert_detection_results(self, bevfusion_output):
"""
转换3D检测结果
BEVFusion格式:
{
'boxes_3d': (N, 9), # x,y,z,dx,dy,dz,yaw,vx,vy
'scores_3d': (N,),
'labels_3d': (N,)
}
CARLA格式:
[
{
'type': 'vehicle',
'location': {'x': ..., 'y': ..., 'z': ...},
'extent': {'x': ..., 'y': ..., 'z': ...},
'rotation': {'pitch': 0, 'yaw': ..., 'roll': 0},
'velocity': {'x': ..., 'y': ...},
'confidence': 0.95
},
...
]
"""
carla_objects = []
boxes = bevfusion_output['boxes_3d']
scores = bevfusion_output['scores_3d']
labels = bevfusion_output['labels_3d']
class_names = ['car', 'truck', 'construction_vehicle', 'bus', 'trailer',
'barrier', 'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone']
for i in range(len(boxes)):
x, y, z, dx, dy, dz, yaw, vx, vy = boxes[i]
carla_obj = {
'type': class_names[labels[i]],
'location': {
'x': float(x),
'y': float(y),
'z': float(z)
},
'extent': {
'x': float(dx / 2),
'y': float(dy / 2),
'z': float(dz / 2)
},
'rotation': {
'pitch': 0.0,
'yaw': float(np.degrees(yaw)),
'roll': 0.0
},
'velocity': {
'x': float(vx),
'y': float(vy)
},
'confidence': float(scores[i])
}
carla_objects.append(carla_obj)
return carla_objects
def convert_segmentation_results(self, bevfusion_output):
"""
转换BEV分割结果
BEVFusion格式:
{
'masks_bev': (H, W, C) # 每个类别的mask
}
CARLA格式:
{
'drivable_area': [[x,y], ...], # 多边形顶点
'lanes': [[[x,y], ...], ...], # 车道线列表
...
}
"""
masks = bevfusion_output['masks_bev']
# 从mask提取轮廓
import cv2
carla_segmentation = {}
class_names = ['drivable_area', 'ped_crossing', 'walkway',
'stop_line', 'carpark_area', 'divider']
for i, class_name in enumerate(class_names):
mask = masks[:, :, i]
# 提取轮廓
contours, _ = cv2.findContours(
mask.astype(np.uint8),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
# 转为坐标点列表
polygons = []
for contour in contours:
if len(contour) > 10: # 过滤小轮廓
# 从像素坐标转为米坐标
points = contour.squeeze()
points_meter = self.pixel_to_meter(points, masks.shape)
polygons.append(points_meter.tolist())
carla_segmentation[class_name] = polygons
return carla_segmentation
def convert_vector_map_results(self, bevfusion_output):
"""
转换矢量地图结果(MapTR输出)
BEVFusion/MapTR格式:
{
'vectors': [
{
'class': 0, # divider
'points': [[x,y], ...],
'score': 0.95
},
...
]
}
CARLA格式:
{
'lanes': [
{
'points': [[x,y,z], ...],
'type': 'divider',
'confidence': 0.95
},
...
]
}
"""
vectors = bevfusion_output.get('vectors', [])
carla_vectors = []
class_map = {0: 'divider', 1: 'boundary', 2: 'ped_crossing'}
for vec in vectors:
points_2d = np.array(vec['points'])
# 添加z坐标假设在地面
points_3d = np.column_stack([
points_2d,
np.zeros(len(points_2d))
])
carla_vec = {
'points': points_3d.tolist(),
'type': class_map.get(vec['class'], 'unknown'),
'confidence': vec.get('score', 1.0)
}
carla_vectors.append(carla_vec)
return {'lanes': carla_vectors}
def pixel_to_meter(self, points_pixel, shape):
"""像素坐标转米坐标"""
H, W = shape[:2]
# 假设BEV范围 [-50, 50] x [-50, 50]
points_meter = points_pixel.copy().astype(float)
points_meter[:, 0] = (points_pixel[:, 0] / W) * 100 - 50
points_meter[:, 1] = (points_pixel[:, 1] / H) * 100 - 50
return points_meter
def convert_full_output(self, bevfusion_output, output_path):
"""转换完整的BEVFusion输出"""
carla_format = {}
# 1. 检测结果
if 'boxes_3d' in bevfusion_output:
carla_format['objects'] = self.convert_detection_results(bevfusion_output)
# 2. 分割结果
if 'masks_bev' in bevfusion_output:
carla_format['segmentation'] = self.convert_segmentation_results(bevfusion_output)
# 3. 矢量地图
if 'vectors' in bevfusion_output or 'vector_map' in bevfusion_output:
vec_output = bevfusion_output.get('vector_map', bevfusion_output)
carla_format['vector_map'] = self.convert_vector_map_results(vec_output)
# 保存JSON
with open(output_path, 'w') as f:
json.dump(carla_format, f, indent=2)
print(f"✅ CARLA格式文件已保存: {output_path}")
return carla_format
def main():
parser = argparse.ArgumentParser(description='BEVFusion输出转CARLA格式')
parser.add_argument('--bevfusion-output', type=str, required=True,
help='BEVFusion预测结果文件(.pkl)')
parser.add_argument('--output', type=str, required=True,
help='输出CARLA格式文件(.json)')
args = parser.parse_args()
# 加载BEVFusion输出
print(f"加载BEVFusion输出: {args.bevfusion_output}")
with open(args.bevfusion_output, 'rb') as f:
bevfusion_output = pickle.load(f)
# 转换
converter = BEVFusionToCarlaConverter()
converter.convert_full_output(bevfusion_output, args.output)
print(f"\n在CARLA中使用:")
print(f" import carla")
print(f" # 加载JSON并可视化")
print(f" with open('{args.output}') as f:")
print(f" data = json.load(f)")
print(f" # 在CARLA中绘制...")
if __name__ == '__main__':
main()