import numpy as np
import cv2
import glob
from pathlib import Path
import torch
from pcdet.config import cfg, cfg_from_yaml_file
from pcdet.datasets import DatasetTemplate
from pcdet.models import build_network, load_data_to_gpu
from pcdet.utils import common_utils
import calib_matrix as VM # P2, R0_rect, Tr_velo_to_cam 포함한 모듈
### 추론용 Dataset 클래스
class DemoDataset(DatasetTemplate):
def __init__(self, dataset_cfg, class_names, training=True, root_path=None, logger=None, ext='.bin'):
super().__init__(dataset_cfg=dataset_cfg, class_names=class_names, training=training, root_path=root_path, logger=logger)
self.root_path = root_path
self.ext = ext
data_file_list = glob.glob(str(root_path / f'*{self.ext}')) if self.root_path.is_dir() else [self.root_path]
data_file_list.sort()
self.sample_file_list = data_file_list
def __len__(self):
return len(self.sample_file_list)
def __getitem__(self, index):
if self.ext == '.bin':
points = np.fromfile(self.sample_file_list[index], dtype=np.float32).reshape(-1, 4)
elif self.ext == '.npy':
points = np.load(self.sample_file_list[index])
else:
raise NotImplementedError
input_dict = {'points': points, 'frame_id': index}
return self.prepare_data(input_dict)
### 모델 추론 함수
def model_inference(cfg_file, data_path, ckpt_path):
cfg_from_yaml_file(cfg_file, cfg)
logger = common_utils.create_logger()
dataset = DemoDataset(dataset_cfg=cfg.DATA_CONFIG, class_names=cfg.CLASS_NAMES, training=False, root_path=Path(data_path))
model = build_network(model_cfg=cfg.MODEL, num_class=len(cfg.CLASS_NAMES), dataset=dataset)
model.load_params_from_file(filename=ckpt_path, logger=logger, to_cpu=True)
model.cuda()
model.eval()
with torch.no_grad():
for data_dict in dataset:
data_dict = dataset.collate_batch([data_dict])
load_data_to_gpu(data_dict)
pred_dicts, _ = model.forward(data_dict)
pred_dict = {
'boxes': pred_dicts[0]['pred_boxes'].cpu().numpy(),
'scores': pred_dicts[0]['pred_scores'].cpu().numpy(),
'labels': pred_dicts[0]['pred_labels'].cpu().numpy()
}
return pred_dict
# ### 3D 바운딩 박스 → 8개 꼭짓점 (LiDAR 좌표계)
# def get_box_points(box):
# x, y, z, l, w, h, yaw = box
# R = np.array([
# [np.cos(yaw), 0, np.sin(yaw)],
# [0, 1, 0],
# [-np.sin(yaw), 0, np.cos(yaw)]
# ])
# x_corners = [l/2, l/2, -l/2, -l/2, l/2, l/2, -l/2, -l/2]
# y_corners = [0, 0, 0, 0, -h, -h, -h, -h]
# z_corners = [w/2, -w/2, -w/2, w/2, w/2, -w/2, -w/2, w/2]
# corners = np.dot(R, np.vstack([x_corners, y_corners, z_corners]))
# corners += np.array([[x], [y], [z]])
# return corners # (3,8)
def get_box_points(box):
x, y, z, l, w, h, yaw = box
# LiDAR 기준: yaw는 z축 기준 회전
R = np.array([
[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]
])
# 중심 기준 좌표 (KITTI 기준 중심은 박스의 중간, 즉 z축 기준 ± h/2)
x_corners = [ l/2, l/2, -l/2, -l/2, l/2, l/2, -l/2, -l/2]
y_corners = [ w/2, -w/2, -w/2, w/2, w/2, -w/2, -w/2, w/2]
z_corners = [ h/2, h/2, h/2, h/2, -h/2, -h/2, -h/2, -h/2] # 중앙 기준으로 위/아래 나눔
corners = R @ np.vstack([x_corners, y_corners, z_corners])
corners += np.array([[x], [y], [z]]) # 중심 위치 적용
return corners # shape: (3, 8)
### LiDAR 좌표계 → 이미지 좌표계
def project_lidar_to_image(corners, Tr_velo_to_cam, R0_rect, P2):
# corners: (3, 8)
corners_hom = np.vstack((corners, np.ones((1, corners.shape[1])))) # (4, 8)
# Tr_velo_to_cam: (3x4) or (4x4)
if Tr_velo_to_cam.shape == (4, 4):
Tr = Tr_velo_to_cam[:3, :] # (3x4)로 자름
else:
Tr = Tr_velo_to_cam
# LiDAR → Camera 좌표계
cam_corners = Tr @ corners_hom # (3, 8)
# Rectify
cam_corners = R0_rect @ cam_corners # (3, 8)
# Camera → Image 투영
img_pts = P2 @ np.vstack((cam_corners, np.ones((1, cam_corners.shape[1])))) # (3, 8)
img_pts[:2] /= img_pts[2]
return img_pts[:2] # (2, 8)
### OpenCV 이미지 위에 3D 박스 그리기
def draw_projected_box_on_image(img, corners_2d, color=(0, 255, 0)):
corners_2d = corners_2d.T.astype(int)
lines = [
[0, 1], [1, 2], [2, 3], [3, 0],
[4, 5], [5, 6], [6, 7], [7, 4],
[0, 4], [1, 5], [2, 6], [3, 7]
]
for start, end in lines:
pt1, pt2 = tuple(corners_2d[start]), tuple(corners_2d[end])
cv2.line(img, pt1, pt2, color, 2)
return img
### 메인 실행 코드
if __name__ == '__main__':
cfg_file = "pointpillar.yaml"
data_path = "000000.bin"
ckpt_path = "pointpillar_7728.pth"
image_path = "000000.png"
image = cv2.imread(image_path)
pred_dict = model_inference(cfg_file, data_path, ckpt_path)
P2 = VM.calib_matrix["P2"]
R0_rect = VM.calib_matrix["R0_rect"]
Tr_velo_to_cam = VM.calib_matrix["Tr_velo_to_cam"]
for i, box in enumerate(pred_dict['boxes']):
if pred_dict['scores'][i] >= 0.15:
box_pts = get_box_points(box)
box_img_pts = project_lidar_to_image(box_pts, Tr_velo_to_cam, R0_rect, P2)
image = draw_projected_box_on_image(image, box_img_pts)
cv2.imshow("3D Bounding Boxes", image)
cv2.waitKey(0)
cv2.destroyAllWindows()