Complex-YOLO是2018年的一篇点云目标检测的文章,发表于arXiv[cs.CV]。

文章链接:An Euler-Region-Proposal for Real-time 3D Object Detection on Point Clouds

核心思想:

本文基于YOLOv2版本的变异,通过把3D点云降维到2D鸟瞰图的方式,将图像检测的网络用于点云的目标检测中。出于降维后信息的损失考虑,本文采用点云多种特征综合起来填充输入通道,以达到目标信息的弥补。

框架结构:

网络仍然以三通道作为输出,区别与图片中的RGB色系不同,这里先将三维点云进行栅格化,将点集分布到鸟瞰图空间的网格中,然后编码网格内点集的最大高度,最大强度,点云密度三种信息归一化后分别填充到R,G,B三个通道中形成RGB-Map,然后采用YOLOv2的Darknet19进行特征提取并回归出目标的相对中心点

,
;相对宽高
,
;复角
,
,以及类别
。如下图所示:

实现细节:

点云预处理:本文将单帧三维点云转换成一张俯视的三通道图片,帅选出传感器正前方ROI区域(80米x 40米)高度限定3米以内,并将点云栅格化到网格分辨率为8cm的二维网格图中:

三通道分别由点云高度信息、点云强度信息、点云密度信息编码所得,编码方式如下:

其中

表示最大高度,
表示最大强度,
表示网格内归一化的密度,
每个网格内的点云映射函数,
表示感兴趣区域范围内的所有点集。

网络结构:本文的网络结构在YOLOv2版本的基础上使用E-RPN进行扩展。基本同Darket-19,只是在最后的输出层增加了两个复数角度的回归,如下图所示:

将网络输出的特征图解码成目标的3D位置,大小,类别概率和朝向角:

其中预测的中心点

,
通过sigmoid函数归一化到每个网格的相对位置,
,
为输出特征图上网格索引位置,预测的长宽
,
通过对数函数表征的是相对于anchor长宽
,
的偏移,预测的复数实部和虚部通过反正切求得朝向角。

锚点设计:考虑在鸟瞰图视角下,同一类目标的长宽尺寸变化不大,但是目标存在方向信息,所以在设计锚点的时候,本文根据数据集内的外接框分布,定义了三种不同尺寸和两个角度方向:i)车辆尺寸(朝上);i i)车辆尺寸(朝下);i i i)自行车尺寸(朝上);i v)自行车尺寸(朝下);v)行人尺寸(朝左)。

复角回归:目标的朝向角

可以通过相应的回归参数
计算得出,他们对应于复数的相位,角度只需使用
即可求出。采用复数的方式主要考虑:
  • 避免奇异性;
  • 在一个封闭的数学空间,能对模型的推广产生有利影响;

损失函数:本文的损失函数在YOLO的基础上增加了欧拉角度回归损失,使用复数进行角度回归,总体上来说叠加的比较生硬,我们知道后面的版本将中心点和宽高放在一起保留bbox完整性然后计算损失,如果能把旋转角也和bbox其他属性整合在一个定位损失中会更好,下面是本文的损失函数:

YOLO自身的损失如下:

Euler角度损失如下:

要点分析:

  1. 将图像检测网络YOLOv2应用到点云检测中,把三维点云转换成鸟瞰图的形式作为输入;
  2. 编码点云的高度,强度,密度信息到输入通道中;
  3. 在网络输出的位置信息,尺度信息,类别信息后增加了角度信息的输出;
  4. 采用复角的方式表征朝向角避免了单纯回归一个值所存在的奇异值问题(0°突变360°);

实验结果:

从下表中可以明显的看出该方法的速度确实快,毕竟是基于单阶段的YOLO系列,但是这个速度应该是纯网络预测耗时,其点云转换成鸟瞰图部分还会消耗一部分时间:

思考与展望:

  1. 很多点云检测网络在其预处理部分需要消耗大量精力时间,本文也不例外,虽然网络的前向传播时效性比较好(或者升级到v5版本),但是对点云的预处理部分仍然拖累整体耗时;
  2. 采用鸟瞰图形式的检测,由于点云近密远稀的特征,限制了其有效检测距离,所以本文只在40M以内的效果比较好;

代码复现:

由于采用的YOLOv2框架,所以大致代码与之前的v2实践篇基本相同,这里贴出点云数据预处理的部分:

def load_pcd(self, pcd_path):pts = []f = open(pcd_path, 'r')data = f.readlines()f.close()line = data[9].strip('\n')pts_num = eval(line.split(' ')[-1])for line in data[11:]:line = line.strip('\n')xyzi = line.split(' ')x, y, z, i = [eval(i) for i in xyzi[:4]]pts.append([x, y, z, i])assert len(pts) == pts_numres = np.zeros((pts_num, len(pts[0])), dtype=np.float)for i in range(pts_num):res[i] = pts[i]return resdef scale_to_255(self, a, min, max, dtype=np.uint8):return (((a - min) / float(max - min)) * 255).astype(dtype)def calc_xyz(self, data):center_x = (data[16] + data[19] + data[22] + data[25]) / 4.0center_y = (data[17] + data[20] + data[23] + data[26]) / 4.0center_z = (data[18] + data[21] + data[24] + data[27]) / 4.0return center_x, center_y, center_zdef calc_hwl(self, data):height = (data[15] - data[27])width = math.sqrt(math.pow((data[17] - data[26]), 2) + math.pow((data[16] - data[25]), 2))length = math.sqrt(math.pow((data[17] - data[20]), 2) + math.pow((data[16] - data[19]), 2))return height, width, lengthdef calc_yaw(self, data):angle = math.atan2(data[17] - data[26], data[16] - data[25])if (angle < -1.57):return angle + 3.14 * 1.5else:return angle - 1.57def cls_type_to_id(self, data):type = data[1]if type not in model_params['classes']:return -1return model_params['classes'].index(type)def calc_angle(self, im, re):"""
    param: im(float): imaginary parts of the plural
    param: re(float): real parts of the plural
    return: The angle at which the objects rotate
    around the Z axis in the velodyne coordinate system
    """if re > 0:return np.arctan(im / re)elif im < 0:return -np.pi + np.arctan(im / re)else:return np.pi + np.arctan(im / re)def load_label(self, label_path):lines = [line.rstrip() for line in open(label_path)]label_list = []for line in lines:data = line.split(' ')data[4:] = [float(t) for t in data[4:]]type = data[1]if type not in model_params['classes']:continuelabel = np.zeros([8], dtype=np.float32)label[0], label[1], label[2] = self.calc_xyz(data)label[3], label[4], label[5] = self.calc_hwl(data)label[6] = self.calc_yaw(data)label[7] = self.cls_type_to_id(data)label_list.append(label)return np.array(label_list)def transform_bev_label(self, label):image_width = (self.y_max - self.y_min) / self.voxel_sizeimage_height = (self.x_max - self.x_min) / self.voxel_sizeboxes_list = []boxes_num = label.shape[0]for i in range(boxes_num):center_x = (-label[i][1] / self.voxel_size).astype(np.int32) - int(np.floor(self.y_min / self.voxel_size))center_y = (-label[i][0] / self.voxel_size).astype(np.int32) + int(np.ceil(self.x_max / self.voxel_size))width = label[i][4] / self.voxel_sizeheight = label[i][5] / self.voxel_sizeleft = center_x - width / 2right = center_x + width / 2top = center_y - height / 2bottom = center_y + height / 2if ((left > image_width) or right < 0 or (top > image_height) or bottom < 0):continueif (left < 0):center_x = (0 + right) / 2width = 0 + rightif (right > image_width):center_x = (image_width + left) / 2width = image_width - leftif (top < 0):center_y = (0 + bottom) / 2height = 0 + bottomif (bottom > image_height):center_y = (top + image_height) / 2height = image_height - topbox = [center_x, center_y, width, height, label[i][6], label[i][7]]boxes_list.append(box)while len(boxes_list) < 300:boxes_list.append([0.0, 0.0, 0.0, 0.0, 0.0, 0.0])return np.array(boxes_list, dtype=np.float32)def transform_bev_image(self, pts):x_points = pts[:, 0]y_points = pts[:, 1]z_points = pts[:, 2]i_points = pts[:, 3]# convert to pixel position valuesx_img = (-y_points / self.voxel_size).astype(np.int32)  # x axis is -y in LIDARy_img = (-x_points / self.voxel_size).astype(np.int32)  # y axis is -x in LIDAR# shift pixels to (0, 0)x_img -= int(np.floor(self.y_min / self.voxel_size))y_img += int(np.floor(self.x_max / self.voxel_size))# clip height valuepixel_values = np.clip(a=z_points, a_min=self.z_min, a_max=self.z_max)# rescale the height valuespixel_values = self.scale_to_255(pixel_values, min=self.z_min, max=self.z_max)# initalize empty arrayx_max = math.ceil((self.y_max - self.y_min) / self.voxel_size)y_max = math.ceil((self.x_max - self.x_min) / self.voxel_size)# Height Map & Intensity Map & Density Mapheight_map = np.zeros((y_max, x_max), dtype=np.float32)intensity_map = np.zeros((y_max, x_max), dtype=np.float32)density_map = np.zeros((y_max, x_max), dtype=np.float32)for k in range(0, len(pixel_values)):if pixel_values[k] > height_map[y_img[k], x_img[k]]:height_map[y_img[k], x_img[k]] = pixel_values[k]if i_points[k] > intensity_map[y_img[k], x_img[k]]:intensity_map[y_img[k], x_img[k]] = i_points[k]density_map[y_img[k], x_img[k]] += 1for j in range(0, y_max):for i in range(0, x_max):if density_map[j, i] > 0:density_map[j, i] = np.minimum(1.0, np.log(density_map[j, i] + 1) / np.log(64))height_map /= 255.0intensity_map /= 255.0rgb_map = np.zeros((y_max, x_max, 3), dtype=np.float32)rgb_map[:, :, 0] = density_map      # r_maprgb_map[:, :, 1] = height_map       # g_maprgb_map[:, :, 2] = intensity_map    # b_mapreturn rgb_mapdef filter_roi(self, pts):mask = np.where((pts[:, 0] >= self.x_min) & (pts[:, 0] <= self.x_max) &(pts[:, 1] >= self.y_min) & (pts[:, 1] <= self.y_max) &(pts[:, 2] >= self.z_min) & (pts[:, 2] <= self.z_max))pts = pts[mask]return pts

大致流程是采用load_pcd函数加载待训练的pcd数据,通过calc_xyz,calc_hwl,calc_yaw等函数从txt文件中读取标签信息,然后调用transform_bev_image将三维点云编码成鸟瞰图空间高度,强度,密度三通道,调用transform_bev_label将标签转换到鸟瞰图中待输入的标签。鸟瞰图预测如下:

YOLO系列专题——Complex-YOLO相关推荐

  1. YOLO系列专题——YOLOv1理论篇

    近期工作上基于YOLOv3做项目并且需要测试TI开发板对OD模型的友好程度,所以想写个YOLO系列的专题,本文就从2015年的v1版本<You Only Look Once: Unified, ...

  2. YOLO系列专题——YOLOv1实践篇

    YOLOv1理论篇 YOLOv1实践篇 工程框架: 在YOLOv1理论篇中我们简要介绍了YOLO的基本原理,本篇从代码角度进一步给出解析.工程结构如下: config作为参数文件用于保存训练参数.测试 ...

  3. YOLO系列专题——YOLOv2实践篇

    YOLOv2理论篇 YOLOv2实践篇 工程框架: YOLOv2引入anchor机制后,在目标的回归上比v1版本效果好了很多.下面是一些代码实现,因为自己建的工程,所以这个系列的整体代码结构会比较相似 ...

  4. YOLO系列专题——YOLOv3理论篇

    YOLOv3理论篇 YOLOv3实践篇 背景介绍: YOLOv3的基本思想与YOLOv2大致相同,过程如下: 将输入图像分成S*S个格子,每个格子负责预测中心在此格子中的物体: 每个格子预测B个bou ...

  5. YOLO系列专题——YOLOv3实践篇

    YOLOv3理论篇 YOLOv3实践篇 工程框架: 本文基于YOLOv3大体结构进行实现,采用VOC2028数据集进行测试,一份安全帽和人两个类别的检测数据集,数据总共7581帧图片.工程框架结构如下 ...

  6. YOLO系列专题——YOLOv2理论篇

    YOLOv2理论篇 YOLOv2实践篇 背景介绍: YOLOv1和同时期的SSD属于两个单阶段检测模型,以速度快著称.但是YOLOv1的诸多缺陷导致精准度较差.召回率低.估计YOLOv2参考了SSD或 ...

  7. 目标检测YOLO系列------YOLO简介

    目标检测YOLO系列------YOLO简介 1.为什么会出现YOLO算法 2.YOLO算法会逐渐成为目标检测的主流吗     YOLO以及各种变体已经广泛应用于目标检测算法所涉及到的方方面面,为了梳 ...

  8. YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上)

    YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上) YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(中) YOLO系列(v1~v3)的学习及Y ...

  9. 【目标检测】yolo系列:从yolov1到yolov5之YOLOv1详解及复现

    检测器通常能够被分为两类,一类是two-stage检测器,最具代表的为faster R-CNN:另一类是one-stage检测器,包括YOLO,SSD等.一般来说,two-stage检测器具有高定位和 ...

最新文章

  1. SPU解析优化:模块设计与实现,SKU优化
  2. android java 指针,opencv android:向我的代码中添加cascade分类器后出现空指针异常
  3. C++ stringstream输入方式
  4. 获取文件的MIME类型
  5. java使用ajax异步刷新_2018.6.27 Ajax实现异步刷新
  6. 商户定价模型—风控人又一经典必学知识
  7. Using mysqldump for Backups(备份还原数据库实例及参数详细说明)
  8. js中的$符号代表什么
  9. 线性回归模型的评价指标
  10. 柱状图误差线 matlab,matlab柱状图加误差线
  11. 遇到问题--HttpClient默认重试策略不处理SocketTimeoutException
  12. CI和Smarty整合并且前后台加载不同配置文件使前台应用Smarty缓存后台不应用
  13. TMS运输管理系统,对车辆管理、物流运输、效率提升有哪些优势?
  14. Laravel5.4中文分词搜索-使用 Laravel Scout,Elasticsearch,ik 分词(二)
  15. vue使用高德地图的搜索地址和拖拽选址
  16. QT的安装------QT
  17. SD卡SPI模式入门教程
  18. CSS教程:vlink,alink,link和a:link
  19. 干货 | 因果推断在项目价值评估中的应用
  20. 【操作系统】——PV操作

热门文章

  1. 2018ACM-ICPC南京赛区网络赛: J. Sum(积性函数前缀和)
  2. SQL语言概述、MySQL Workbench安装
  3. 2017 ACM-ICPC南宁网络赛: G. Finding the Radius for an Inserted Circle
  4. python基础系列教程——数据结构(列表、元组、字典、集合、链表)
  5. jquery系列教程8-jquery插件大全
  6. PHP 数据库中的模糊查询
  7. 杭电------2097 Sky数(C语言写)
  8. Nginx(八)-- 负载均衡
  9. C#多线程之线程池篇1
  10. O - Can you find it?