1.bev参考算法模型信息
板端或者X86端运行hrt_model_exec model_info命令获取模型输入输出信息,参考命令如下:
hrt_model_exec model_info --model_file model.hbm
使用以上命令获取的bev模型输入信息分别如下所示:
模型
input name
input shape
layout
数据格式
量化scale
IPM
input[0]
(6,3,512,960)
NCHW
NV12
-–
IPM
input[1]
(6,2,128,128)
NCHW
int16
0.0078125
LSS
input[0]
(6,3,256,704)
NCHW
NV12
-–
LSS
input[1]
(10,2,128,128)
NCHW
int16
0.0078125
LSS
input[2]
(10,2,128,128)
NCHW
int16
0.03125
GKT
input[0]
(6,3,512,960)
NCHW
NV12
-–
GKT
input[1]-input[9]
(6,2,64,64)
NCHW
int16
0.00390625
2. 图像输入生成
根据模型输入信息,需要将6张图像转成NV12格式后拼接在一起,然后导出为.bin文件作为板端模型的input[0]输入,图像格式转为NV12代码如下:
import argparse
import os
import cv2
import numpy as np
import torch
from PIL import Image
from torchvision.transforms.functional import pil_to_tensor
from torchvision.transforms.functional_tensor import resize
def process_img(img_path, resize_size, crop_size):
orig_img = cv2.imread(img_path)
orig_img = Image.fromarray(orig_img)
orig_img = pil_to_tensor(orig_img)
resize_hw = (
int(resize_size[0]),
int(resize_size[1]),
)
orig_shape = (orig_img.shape[1], orig_img.shape[2])
resized_img = resize(orig_img, resize_hw).unsqueeze(0)
top = int(resize_hw[0] - crop_size[0])
left = int((resize_hw[1] - crop_size[1]) / 2)
resized_img = resized_img[:, :, top:, left:]
return resized_img, orig_shape
if __name__ == "__main__":
#resize后的图像大小,lss参考算法为(396, 704)
resize_size = (540, 960)
#input_size用于裁剪 resized image
#模型输入的图像大小,lss参考算法为(256, 704)
input_size = (512, 960)
orig_imgs = []
#输入的6V图像路径
inputs = './single_frame/imgs'
#这里可以定义输入图像的顺序,需要与训练时输入的顺序一致
input_list=["01.jpg","02.jpg","03.jpg","04.jpg","05.jpg","06.jpg"]
for i, img in enumerate(input_list):
img = os.path.join(inputs, img)
img, orig_shape = process_img(img, resize_size, input_size)
orig_imgs.append({"name": i, "img": img})
input_imgs = []
for image in orig_imgs:
image=image["img"].permute(0,2,3,1).squeeze(0)
#bgr to nv12
image=image.numpy().astype(np.uint8)
height, width = image.shape[0], image.shape[1]
yuv420p = cv2.cvtColor(image, cv2.COLOR_BGR2YUV_I420)
yuv420p = yuv420p.reshape((height * width * 3 // 2, ))
y = yuv420p[:height * width]
uv_planar = yuv420p[height * width:].reshape((2, height * width // 4))
uv_packed = uv_planar.transpose((1, 0)).reshape((height * width // 2, ))
nv12 = np.zeros_like(yuv420p)
nv12[:height * width] = y
nv12[height * width:] = uv_packed
input_imgs.append(nv12)
input_imgs=np.array(input_imgs)
input_imgs=input_imgs.reshape(6 * 3 * input_size[0] * input_size[1] // 2)
#导出为bin
input_imgs.tofile("inputnv12.bin")
ipm和gkt参考算法的resize_size = (540, 960)和input_size = (512, 960),lss参考算法的resize_size=(396, 704),input_size = (256, 704)。
3. 采样点points输入生成
points输入的生成需要以下4个步骤:
- 相机内外参数获取;
- 生成homography矩阵;
- 根据homography矩阵导出浮点points;
- 对浮点points做int16量化。
3.1 相机内外参数获取
OE开发包的ddk/samples/ai_toolchain/horizon_model_train_sample/scripts/tools
目录下提供了nuscenes数据集相机内外参获取脚本gen_camera_param_nusc.py,运行方式如下:
#进入到OE开发包的scripts路径
cd $workspace$/horizon_model_train_sample/scripts
python3 ./tools/gen_camera_param_nusc.py --data-path $nuscenes_path$ --save-path $save_path$ --save-by-city True --version $nuscene_version$
上述脚本中的参数意义为:
-
--data-path :配置为bev参考算法运行时构建的meta文件夹路径
-
--save-path:生成相机内外参的保存路径;
-
--save-by-city:nuscenes数据集中不同城市的相机内外参可能是不同的,为了便于区分,所以用city 名称命名生成的相机内外参数;
-
--version:使用的nuscenes数据据版本,可选为v1.0-mini和v1.0-trainval,默认为v1.0-mini;-
脚本运行结束后,会在–save-path下生成boston和singapore两个文件夹,文件夹中包含相机内参/外参矩阵,目录结构如下所示:|-- boston
| |-- camera_intrinsic.npy #相机内参
| |-- sensor2ego_rotation.npy #相机外参
|-- sensor2ego_translation.npy # |-- singapore | |-- camera_intrinsic.npy | |-- sensor2ego_rotation.npy
– sensor2ego_translation.npy
这里生成了boston和singapore两个文件夹,是因为nuscenes数据集在boston和singapore location下采集的数据的相机参数不同。
3.2 生成homography矩阵
OE开发包的ddk/samples/ai_toolchain/horizon_model_train_sample/scripts/tools
目录下提供了homography矩阵生成脚本homography_generator.py,运行方式如下:
#导出singapore的homography矩阵
python3 ./tools/homography_generator.py --sensor2ego-translation ./singapore/sensor2ego_translation.npy --sensor2ego-rotation ./singapore/sensor2ego_rotation.npy --camera-intrinsic ./singapore/camera_intrinsic.npy --save-path ./singapore
#导出boston的homography矩阵
python3 ./tools/homography_generator.py --sensor2ego-translation ./boston/sensor2ego_translation.npy --sensor2ego-rotation ./boston/sensor2ego_rotation.npy --camera-intrinsic ./boston/camera_intrinsic.npy --save-path ./boston
上述脚本的参数意义为:
- --sensor2ego-translation:即上节生成的sensor2ego_translation.npy
- --sensor2ego-rotation:即上节生成的sensor2ego_rotation.npy
- --camera-intrinsic: 内参矩阵的路径,即上节生成的camera_intrinsic.npy
- --save-path:保存路径
3.3 导出浮点Points
获取homography矩阵后,使用tools文件夹下的reference_points_generator.py脚本导出浮点类型的points,参考命令如下:
#导出singapore location下lss的points
python3 ./tools/reference_points_generator.py --config ./configs/bev/bev_mt_lss_efficientnetb0_nuscenes.py --homography ./singapore/homography.npy --save_path ./lss/points/singapore
#导出boston location下lss参考算法的points
python3 ./tools/reference_points_generator.py --config ./configs/bev/bev_mt_lss_efficientnetb0_nuscenes.py --homography ./boston/homography.npy --save_path ./lss/points/boston
可以通过修改config文件路径导出ipm、gkt参考算法的Points.
上述脚本的参数意义为:
- --config:config文件的路径 ,脚本中以bev_mt_lss参考算法为例
- --homography:上节中导出homo矩阵的路径
- --save_path:保存路径
输出文件说明:
- ipm有1个points输入,所以会在save_path下生成一个reference_points0.npy;
- lss有2个points输入,所以会在save_path下生成一个reference_points0.npy和reference_points1.npy;
-
- gkt有 9个points,所以会在save_path下生成一个reference_points0.npy到reference_points8.npy等9个npy文件;
3.4 对浮点Points做量化
导出浮点Points后 ,需要对其做int16量化。这里提供了给浮点Points进行int16量化的process_reference_points.py,源代码为:
import numpy as np
import torch
import argparse
import os
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"--model",
"-m",
type=str,
required=True,
help="Selectable as ipm,lss,gkt",
)
parser.add_argument(
"--points_path",
"-p",
type=str,
required=True,
help="path of points(float32)"
)
parser.add_argument(
"--save_path",
type=str,
default=".",
help="path for saving point(int16)",
)
known_args, unknown_args = parser.parse_known_args()
return known_args, unknown_args
def process_reference_points(model, points_path, save_path):
for i, name in enumerate(os.listdir(points_path)):
if model=="ipm":
scale = [0.0078125]
elif model == "gkt":
scale = [
0.00390625, 0.00390625, 0.00390625, 0.00390625, 0.00390625,
0.00390625, 0.00390625, 0.00390625, 0.00390625]
if model == "lss":
scale = [0.0078125, 0.03125]
path= os.path.join(points_path, name)
print(path)
points = np.load(path)
idx=int(path[-5])
points = points.transpose(0, 3, 1, 2) / scale[idx]
print(points.shape)
points = np.floor(points + 0.5)
points = np.clip(points, -32768, 32767)
points = points.astype(np.int16)
save_name= os.path.join(save_path, name)[2:-4]
print("save_name:",save_name)
points.tofile(save_name+".bin")
print("----save points OK----")
if __name__ == "__main__":
args, args_env = parse_args()
if args_env:
setup_args_env(args_env)
process_reference_points(args.model, args.points_path, args.save_path)
参考命令如下:
#对lss参考算法在singapore location下的points做量化
python3 process_reference_points.py --model "lss" --points_path ./lss/points/singapore --save_path ./lss/points/processed_singapore
#对lss参考算法在boston location下的points做量化
python3 process_reference_points.py --model "lss" --points_path ./lss/points/boston --save_path ./lss/points/processed_boston
命令中的参数意义是:
- --model:参考算法类型,可选为lss,ipm,gkt
- --points_path:浮点points路径,注意每个参考算法在singapore和boston location下的浮点points是不同的
- --save_path:保存路径
输出文件说明:
- ipm有1个points输入,所以会在save_path下生成一个reference_points1.bin;
- lss有2个points输入,所以会在save_path下生成一个reference_points0.bin和reference_points1.bin;
- gkt有 9个points,所以会在save_path下生成一个reference_points0.bin到reference_points8.bin等9个bin文件。
至此,bev参考算法板端输入数据的准备完成。