Descripción general de detección de objetivos 3D e interpretación de código y papel de VoxelNet (1)--Pillar VFE

Descripción general de la detección de objetos 3D

Las nubes de puntos y las imágenes son tipos de datos de sensores de uso común en la conducción autónoma, y ​​cada uno tiene diferentes fortalezas y debilidades. La detección de objetos de nube de puntos tiene ventajas insustituibles en el campo de la conducción autónoma, especialmente en escenarios que requieren posicionamiento preciso y evitación de obstáculos. La detección de objetos de nube de puntos puede proporcionar capacidades de percepción más precisas y confiables para vehículos autónomos. Las principales ventajas son:

  1. Información rica en 3D : las nubes de puntos pueden proporcionar información en 3D, como la posición, la forma, el tamaño y la distancia de los objetos, lo cual es muy importante para algunas aplicaciones de conducción autónoma que requieren posicionamiento preciso, evitación de obstáculos y planificación.
  2. Alta confiabilidad : en comparación con los datos de imagen, los datos de nube de puntos generalmente son generados por sensores como lidar, que tiene las características de alta estabilidad y fuerte capacidad anti-interferencia, y puede manejar algunos entornos y escenas complejos.
  3. Amplia aplicabilidad : la detección de objetos de nube de puntos se puede aplicar a varios escenarios de aplicaciones de conducción automática, como detección de obstáculos, detección de peatones, detección de vehículos, etc.
  4. Pequeña cantidad de procesamiento de datos sin procesar : en comparación con los métodos de detección de objetivos basados ​​en imágenes, la detección de objetivos de nube de puntos tiene una cantidad relativamente pequeña de procesamiento en términos de preprocesamiento de datos y reducción de ruido, y la eficiencia de procesamiento es alta.
    inserte la descripción de la imagen aquí

Los datos de nube de puntos puros se clasifican según la representación de entrada

De acuerdo con la representación de entrada, se puede dividir aproximadamente en dos categorías, a saber, base de cuadrícula y base de puntos, consulte la siguiente figura:
inserte la descripción de la imagen aquí

Proceso de detección de objetos 3D

  1. Preprocesamiento de datos : convierta los datos de la nube de puntos en un formato que la red pueda manejar, como convertir nubes de puntos en representaciones tridimensionales de vóxel (vóxel) o usar representaciones basadas en puntos (como PointNet).
  2. Extracción de características de la nube de puntos : las redes neuronales convolucionales (CNN) se utilizan para extraer características de las nubes de puntos con el fin de detectar objetos. Los métodos para la extracción de características incluyen redes neuronales convolucionales (CNN) bidimensionales tradicionales, métodos basados ​​en puntos (como PointNet) y métodos basados ​​en vóxeles (como VoxelNet).
  3. Segmentación de nube de puntos : use algoritmos de segmentación para asignar cada punto en la nube de puntos a una categoría de objeto específica. El método de segmentación puede basarse en métodos tradicionales de aprendizaje automático, como la máquina de vectores de soporte (SVM) y el bosque aleatorio, o puede basarse en métodos de aprendizaje profundo, como PointRCNN y métodos basados ​​en pilares.
  4. Detección de objetos : detecta objetos en la nube de puntos segmentados. La detección de objetos puede ser un algoritmo de detección de objetos bidimensional tradicional o un algoritmo basado en 3D, como Frustum PointNet y MV3D.
  5. Posprocesamiento : elimine los resultados de detección duplicados y mejore la precisión de la detección de objetivos a través de métodos de posprocesamiento como el filtrado y la supresión no máxima (NMS).
    Vale la pena señalar que cada paso puede usar diferentes algoritmos y técnicas, y los métodos de implementación específicos varían con las diferentes tareas.

Marco de detección de objetos OpenPCDet3D

Nota: Este artículo se presenta basado en OpenPCDet

Diseño de topología de modelo modular

Para construir un marco de detección de objetos 3D en PCDet, solo necesita escribir un archivo de configuración para definir claramente los módulos requeridos, y luego PCDet combinará automáticamente los módulos en un marco de detección de objetos 3D de acuerdo con el orden topológico de los módulos para entrenamiento y pruebas.
El diseño modular se muestra en la siguiente figura:
inserte la descripción de la imagen aquíPCDet puede admitir la mayoría de los algoritmos de detección de objetivos 3D existentes para nubes de puntos LiDAR, incluidos los basados ​​en vóxeles, basados ​​en puntos, híbridos de punto-vóxel y de una o dos etapas, etc. Y otros Algoritmos de detección de objetivos 3D, consulte: Haga clic aquí .

Función del módulo principal PCDet

  1. Backbone3D se utiliza para procesar la nube de puntos original y la información de vóxeles.
  2. Backbone2D comprime aún más la información 3D en un plano 2D para extraer características.
  3. DenseHead es un cabezal de detección de una etapa.
  4. RoIHead es un cabezal de detección de dos etapas.

Columna vertebral3D

backbone3D se divide en dos ramas: rama puntual y rama vóxel (en la práctica, una o ambas pueden estar reservadas).

  1. El camino principal es la rama Voxel: Voxel Feature Encoder (VFE) primero agrega características de nube de puntos en voxel y luego usa convolución dispersa (SparseConv) para extraer características 3D. El método VFE (Mean VFE) más simple es dividir el espacio en vóxeles de igual volumen y promediar sus características para el conjunto de nubes de puntos que caen en un determinado vóxel. Otros métodos VFE (Pillar VFE) incluyen: primero describir las características en forma de relaciones locales (f, fcluster, fcenter), donde f es la característica del punto, fcluster es la distancia desde el punto hasta el centro del conjunto de puntos, y fcenter es la distancia desde el punto hasta el centro de la distancia espacial, y luego realiza cálculos MLP en las características de este formulario para obtener resultados VFE.
    Después de obtener el resultado de VFE, puede usar la convolución dispersa (SparseConv) para extraer características 3D. El principio de cálculo de esta convolución es el mismo que el de la convolución 2D, pero para acelerar el cálculo, solo se calcula en el zona con valor. La implementación específica es a través del almacén de código abierto spconv. La estructura principal de la convolución dispersa 3D es agregar y comprimir las características 3D de entrada originales (generalmente 8 veces de reducción de resolución)
  2. La siguiente ruta es la rama Point: realiza principalmente la agregación de características Point Feature Encoding (PFE) en la nube de puntos original, usa la operación PointNet++ para primero MLP agregar la nube de puntos local y luego realiza la propagación en la característica en el cálculo de interpolación para restaurar la escala original.

Backbone2D

Las características 3D se extrajeron en la etapa anterior, pero el cálculo de las características 3D es grande, porque ahora las características 3D se comprimen en el plano BEV de la vista de pájaro 2D y se realiza una mayor agregación de características utilizando el método maduro de convolución 2D. . Esta operación de comprimir 3D a 2D se denomina Map_to_bev. La forma más sencilla (compresión de altura) es remodelar una característica con una dimensión de [H, W, D, C] en [H, W, DxC]. Hay otra forma (Pillar Scatter): el método de almacenamiento del pilar es [N,C], colóquelo en el tensor de [HxW,C] de acuerdo con la posición espacial del pilar y luego remodelelo en [H, WC].
El siguiente paso es realizar operaciones de convolución 2D, como se muestra en la siguiente figura:
inserte la descripción de la imagen aquí
Nota: Backbone2D es la red troncal utilizada para procesar datos de imágenes en el algoritmo PCDet3D. Suele utilizarse en la detección de objetivos 3D basada en aprendizaje profundo, ya que las imágenes 2D puede proporcionar la geometría de los objetos, la textura, el borde y otra información. Backbone2D generalmente usa algoritmos avanzados de detección de objetivos 2D, como Faster R-CNN, RetinaNet, Mask R-CNN, etc. para extraer características de la imagen. Estos algoritmos pueden extraer regiones de ROI relacionadas con objetos y realizar la extracción de características en estas regiones de ROI para obtener representaciones de imágenes de características de alta dimensión. Estas representaciones de características se pueden combinar con representaciones de características de nubes de puntos para el procesamiento conjunto, lo que mejora la precisión del algoritmo de detección.
Cabe señalar que tanto Backbone3D como Backbone2D son redes troncales, que juegan un papel importante en el algoritmo PCDet3D. Mediante el uso conjunto de estas dos redes troncales, el objetivo se puede detectar de manera más completa y precisa.

cabeza densa

DenseHead es un cabezal de detección de una etapa. Divide principalmente las características extraídas por la red troncal 2D en la división de tareas final. Generalmente, se utilizan tres ramas para realizar tres tareas, que son tareas de clasificación de objetos, regresión de bbox y clasificación de direcciones. Aquí hay una descripción detallada de los pasos de regresión de bbox basados ​​en anclas:

  1. El generador de anclas primero establece diferentes anclas de acuerdo con diferentes categorías;
  2. Asigne objetivos (assign_targets: AxisAligned o ATSS) a cada ancla de acuerdo con gt bbox: obtenga la etiqueta del ancla de acuerdo con IoU, use el codificador de caja para calcular el desplazamiento de bbox y, finalmente, use la cantidad de objetos en este lote como el peso coeficiente para normar el reg_loss del lote.
  3. Tres ramas (como se muestra a continuación) obtienen cls, bbox reg, dir como predicción;
  4. Use predicción y ancla para calcular el resultado de la etapa final en box_decoder.
    inserte la descripción de la imagen aquí
    DenseHead es una red principal en el algoritmo PCDet3D, que se utiliza para predecir densamente la posición y el tamaño de los objetos a partir de datos de nubes de puntos . DenseHead adopta la idea basada en una red neuronal convolucional bidimensional (CNN) para convertir los datos de la nube de puntos en una representación de tensor tridimensional. Luego, se realizan operaciones de convolución y pooling sobre estos tensores 3D para extraer características con alto poder expresivo. A continuación, estas características se procesan aún más a través de múltiples capas completamente conectadas y, finalmente, se obtiene la información de posición y tamaño de cada objeto en la nube de puntos. DenseHead predice densamente objetos en datos de nubes de puntos, lo que puede mejorar la recuperación y la precisión de los algoritmos de detección.

RoIHead

RoIHead es un cabezal de detección de dos etapas. Según los resultados obtenidos en la primera etapa, los pasos de perfeccionamiento de la segunda etapa son los siguientes: (1)
Usar NMS en la capa de propuesta para reducir el número de salida de la primera etapa;
(2) ) asigne objetivos al área de RoI restante, por ejemplo, divida las 512 regiones de RoI en muestras positivas y negativas de acuerdo con IoU, y seleccione aleatoriamente 64 muestras calificadas como muestras positivas y 64 muestras negativas.
(3) Use RoI Grid Pooling y otras operaciones para refinar las características del área de RoI y luego ingrese el MLP para obtener 3 tareas de salida: cls, bbox reg, dir.
(4) Finalmente, el resultado final se obtiene a través del decodificador de caja.
RoIHead es otra red principal en el algoritmo PCDet3D, que se utiliza para procesar regiones candidatas (RoI) en datos de imagen y realizar detección . RoIHead suele utilizar algoritmos avanzados de detección de objetos 2D, como Faster R-CNN, RetinaNet, Mask R-CNN, etc. para extraer las características de la imagen. RoIHead utiliza la red de extracción de características (como ResNet) como red troncal para extraer características de RoI e ingresar estas representaciones de características en la capa totalmente conectada para su posterior procesamiento. Finalmente, RoIHead puede generar la categoría objetivo y la información de ubicación correspondiente a cada RoI.
Cabe señalar que tanto DenseHead como RoIHead son redes principales para procesar diferentes tipos de datos, pero sus salidas se pueden combinar para obtener los resultados finales de detección de objetivos. Específicamente, la salida de información de tamaño y posición del objeto por DenseHead y RoIHead se puede registrar con el área de la imagen correspondiente a RoI a través de un cierto método de registro, para obtener la información precisa de posición y tamaño de cada objeto en el espacio tridimensional, y realizar Detección de objetivos 3D.

Interpretación del Algoritmo VoxelNet

Descripción general de VoxelNet

VoxelNet es un método de extracción de características basado en vóxeles, que fusiona la información de puntos de la nube de puntos con la información local representada por el vóxel, y finalmente obtiene una característica más representativa, que se utiliza en la red RPN para obtener el objetivo después de la convolución 3D. El resultado de la prueba. Enlace original: Haga clic aquí .
motivación:

  1. Los métodos anteriores basados ​​en vóxeles diseñaron manualmente las características de los vóxeles y luego usaron el procesamiento de convolución 3D.Este documento se basa en el método de PointNet y propone un método de codificación de características de vóxeles VFE para lograr un extremo a extremo.
  2. Se adopta un método de codificación de ángulo de guiñada simple, es decir, el ángulo de guiñada se devuelve directamente sin una codificación de ángulo de guiñada compleja.

inserte la descripción de la imagen aquí

estructura de red

Como se muestra abajo:
inserte la descripción de la imagen aquí

Análisis general del código

Procesamiento de datos
Los datos de entrada de VoxelNet son una nube de puntos, y los datos de la nube de puntos deben procesarse primero en forma de vóxel. En el código, VoxelNet divide los datos de la nube de puntos en una serie de vóxeles, cada vóxel representa un espacio tridimensional. Luego, se realiza la extracción de características en la nube de puntos en cada vóxel para obtener la representación característica de la nube de puntos en el vóxel. La extracción de características adopta un método basado en una red neuronal convolucional bidimensional, que convierte la nube de puntos en cada vóxel en un tensor tridimensional y luego extrae la representación de características a través de una serie de operaciones como convolución y agrupación.

Red troncal
La red troncal de VoxelNet es una red neuronal convolucional tridimensional basada en una red neuronal convolucional bidimensional. La red se utiliza para extraer la representación de características de los datos de la nube de puntos, lo que proporciona una entrada de características para la detección de objetos. En el código, VoxelNet usa ResNet como red troncal para la extracción de características de nubes de puntos dentro de vóxeles.

Red de propuestas
VoxelNet utiliza la red de propuestas para generar cuadros de candidatos en el mapa de características. Según la representación de características de cada vóxel en el mapa de características, la red predice si hay un objeto en cada vóxel, generando así una serie de casillas candidatas. En el código, la red de propuestas se implementa mediante un método basado en un perceptrón multicapa (MLP).

Red de propuesta de región
VoxelNet utiliza la red de propuesta de región para filtrar las casillas de candidatos generadas por la red de propuesta. La red Region Proposal utiliza un método de clasificación binaria para dividir los cuadros de candidatos en dos categorías: objetivo y no objetivo. En el código, la red Region Proposal se implementa utilizando un método basado en MLP.

Red de cabezales de detección
VoxelNet utiliza la red de cabezales de detección para realizar la detección de objetivos en los cuadros de candidatos filtrados. La red predice la categoría y la información de ubicación del objetivo de acuerdo con la representación de características en el cuadro de candidatos. En el código, la red de cabezales de detección se implementa mediante un método basado en MLP.

Función de pérdida
La función de pérdida de VoxelNet consta de dos partes: pérdida de clasificación y pérdida de regresión. La pérdida de clasificación se usa para medir la precisión de la clasificación de objetos, y la pérdida de regresión se usa para medir la precisión de la ubicación de los objetos. En el código, la pérdida de clasificación usa la función de pérdida de entropía cruzada, y la pérdida de regresión usa la función de pérdida suave L1.

Estructura general del código de red de VoxelNet

  1. El módulo Pillar VFE convierte los datos de la nube de puntos del sistema de coordenadas original al sistema de coordenadas de vóxel y agrega cada vóxel para generar la representación de características del vóxel.
  2. La red primero aprende la información de posición de cada vóxel a través de una capa completamente conectada y empalma la característica del pilar y la información de posición para obtener la característica del pilar después de agregar la información de posición.
  3. Luego, las características del pilar después de agregar la información de posición se pasan al módulo del pilar SA para su procesamiento.Este módulo divide todos los pilares en diferentes grupos por agrupación, realiza un cálculo de autoatención en las características del pilar en cada grupo y finalmente obtiene cada A nuevo representación de características para pilares.
  4. Luego, las características del pilar procesadas por el módulo SA del pilar se pasan al cabezal de detección para la tarea de detección de objetivos.
  5. El cabezal de detección procesa aún más las características del pilar, incluida la proyección de las características del pilar en un plano bidimensional a través de ROI Pooling, y luego a través de una serie de operaciones de convolución y capas completamente conectadas para obtener los resultados finales de detección de objetivos, incluida la categoría del objetivo, ubicación, y confianza. .
    En general, la red general de VoxelNet se puede dividir en cuatro módulos, incluido el módulo VFE de pilar, el módulo de codificación de posición, el módulo SA de pilar y el módulo de cabezal de detección, como se muestra en la siguiente figura. Entre ellos, el módulo pilar VFE se usa para extraer las características locales de la nube de puntos, el módulo de codificación de posición se usa para agregar información de posición, el módulo pilar SA se usa para aprender la relación global de la nube de puntos y el cabezal de detección El módulo se utiliza para implementar la tarea de detección de objetivos.

Análisis de código Pilar VFE

"""
Pillar VFE, credits to OpenPCDet.
"""

import torch
import torch.nn as nn
import torch.nn.functional as F

# Point Feature Net Layer 提取点云数据中的密集特征  PointNer操作
class PFNLayer(nn.Module):
    def __init__(self,
                 in_channels,
                 out_channels,
                 use_norm=True,
                 last_layer=False):
        super().__init__()

        self.last_vfe = last_layer   # 将密集特征提取网络中的最后一层保存到类成员变量self.last_vfe中
        self.use_norm = use_norm    # 归一化
        if not self.last_vfe:          # 如果last_vfe已经被赋值,则说明已经处理过了,这个if语句就不会执行
            out_channels = out_channels // 2   # out_channels是密集特征提取网络中每个体素(pillar)输出的特征通道数
                                               # 输出通道数减半,以适应后续网络模块的需求,2是经验值
        if self.use_norm:
            self.linear = nn.Linear(in_channels, out_channels, bias=False)   # 初始化无偏置项的线性层
            self.norm = nn.BatchNorm1d(out_channels, eps=1e-3, momentum=0.01) # 初始化批归一化层(标准化)
        else:
            self.linear = nn.Linear(in_channels, out_channels, bias=True)

        self.part = 50000  # 分批处理的参数,每个批次包含50000个样本

    def forward(self, inputs):
        if inputs.shape[0] > self.part:    # 判断是否分批处理
            # nn.Linear performs randomly when batch size is too large
            num_parts = inputs.shape[0] // self.part   # 处理批次数  //为整数除法
            part_linear_out = [self.linear(
                inputs[num_part * self.part : (num_part + 1) * self.part])  
                for num_part in range(num_parts + 1)] 
            # 列表推导式 , 该处的+1只为避免从0计数
            # 使用nn.Linear对每个批次的数据进行线性变换,得到相应的特征表示
            # inputs[num_part * self.part:(num_part + 1) * self.part] 切片操作
            # 如 [0:50000] [50000:100000]
            x = torch.cat(part_linear_out, dim=0)   # 特征拼接
        else:
            x = self.linear(inputs)
        torch.backends.cudnn.enabled = False
        x = self.norm(x.permute(0, 2, 1)).permute(0, 2,
                                                  1) if self.use_norm else x   # 如果标准化,使用permute改变张量维度, 如:(3,4,5)-->(3,5,4)
        torch.backends.cudnn.enabled = True
        x = F.relu(x)   # 激活函数
        x_max = torch.max(x, dim=1, keepdim=True)[0]    # 沿着第二个维度(即channel 维度)取最大值
                                                        # keepdim=True 参数表示结果张量与输入张量 x 的维度相同,只是第二个维度变为了 1
        if self.last_vfe:
            return x_max
        else:
            x_repeat = x_max.repeat(1, inputs.shape[1], 1)  # 如果是最后一层 VFE,只返回每个 pillar 对应的最大值,
            x_concatenated = torch.cat([x, x_repeat], dim=2) #如果不是最后一层 VFE,还需要将这个最大值张量进行复制和拼接,以得到更多信息。
            return x_concatenated


# 提取点云数据中的密集特征

class PillarVFE(nn.Module):
    def __init__(self, model_cfg, num_point_features, voxel_size,
                 point_cloud_range):
        super().__init__()
        self.model_cfg = model_cfg   # 模型配置,保存在类中,为后边取值

        self.use_norm = self.model_cfg['use_norm']   # 是否使用Batch Normalization,默认为True
        self.with_distance = self.model_cfg['with_distance']  # 距离信息

        self.use_absolute_xyz = self.model_cfg['use_absolute_xyz']   # 绝对坐标
        num_point_features += 6 if self.use_absolute_xyz else 3   # 使用绝对坐标6,6特征表示xyz + roll,yaw,pitch
        if self.with_distance:
            num_point_features += 1   # 使用距离信息特征维度+1

        self.num_filters = self.model_cfg['num_filters']   # num_filters表示卷积核数量
        assert len(self.num_filters) > 0
        num_filters = [num_point_features] + list(self.num_filters)

        pfn_layers = []
        for i in range(len(num_filters) - 1):
            in_filters = num_filters[i]
            out_filters = num_filters[i + 1]    # +1 可循环至最后一层
            pfn_layers.append(
                PFNLayer(in_filters, out_filters, self.use_norm,
                         last_layer=(i >= len(num_filters) - 2)) # 判断是否为最后一层
            )
        self.pfn_layers = nn.ModuleList(pfn_layers)  # self.pfn_layers是一系列PFNLayer层的列表,用于后续的前向计算

        self.voxel_x = voxel_size[0]
        self.voxel_y = voxel_size[1]  # 体素的长宽高
        self.voxel_z = voxel_size[2]
        self.x_offset = self.voxel_x / 2 + point_cloud_range[0]  # 点云数据的范围和体素的大小之间的偏移量,
        self.y_offset = self.voxel_y / 2 + point_cloud_range[1]  # 用于后续的点云坐标转换操作
        self.z_offset = self.voxel_z / 2 + point_cloud_range[2]

    def get_output_feature_dim(self):
        return self.num_filters[-1]   # [-1] 最后一层特征维度

    @staticmethod   # 静态方法是一种不需要创建类实例即可访问的方法。与实例方法和类方法不同,静态方法不接收额外的参数(即self或cls)
    def get_paddings_indicator(actual_num, max_num, axis=0):
        actual_num = torch.unsqueeze(actual_num, axis + 1)  # torch.unsqueeze 用于在指定维度上增加一个新的维度,eg:torch.Size([3, 4])-->torch.Size([3, 1, 4])
        max_num_shape = [1] * len(actual_num.shape)   # 增加一个新的维度以便与 max_num 进行比较
        max_num_shape[axis + 1] = -1
        max_num = torch.arange(max_num,
                               dtype=torch.int,
                               device=actual_num.device).view(max_num_shape)  # 使用 torch.arange 生成一个张量 max_num,其形状与 actual_num 相同
        paddings_indicator = actual_num.int() > max_num  # paddings_indicator(布尔型张量),表示哪些位置需要填充
        return paddings_indicator
    # get_paddings_indicator函数的作用就是生成一个与输入张量的shape相同的张量,用于表示哪些位置是填充的。
    # 例如,在PointNet++中,假设我们的输入张量为x,shape为(batch_size, num_points, num_features),
    # 其中num_points是点的数量,我们需要将它填充到一个最大的点数max_points,
    # 则可以使用get_paddings_indicator函数生成一个shape为(batch_size, max_points)的张量,表示哪些位置是填充的。

    def forward(self, batch_dict):
        # 先将特征描述成局部关系的形式(f, fcluster, fcenter),
        # 其中f是point特征,fcluster 是point到点集合中心的距离,
        # fcenter是point到空间中心的距离,然后对该形式的特征做MLP计算得到VFE结果
        voxel_features, voxel_num_points, coords = \
            batch_dict['voxel_features'], batch_dict['voxel_num_points'], \
            batch_dict['voxel_coords']  # 取出体素特征、体素内点的数量和体素坐标,模型的输入数据
        # voxel_features张量的形状为 (N,T,C),其中 N 表示非空体素网格的数量,T 表示每个体素点个数,C 表示每点特征
        # voxel_coord是(N, 3)的二维张量,包括三列体素坐标(x, y, z)
        points_mean = \
            voxel_features[:, :, :3].sum(dim=1, keepdim=True) / \
            voxel_num_points.type_as(voxel_features).view(-1, 1, 1)   # 根据每个体素的特征和点数,计算每个体素中所有点特征的平均值,用于后续计算聚类特征
        # 该处的voxel_features[:, :, :3]表示的就是体素的中心在x轴,y轴和z轴上的坐标
        f_cluster = voxel_features[:, :, :3] - points_mean   # point到点集合中心的距离,得到聚类特征
        # 先获取对应体素的坐标,然后根据self.voxel_x、self.voxel_y、self.voxel_z和x_offset、y_offset、z_offset计算出每个坐标轴上的位置,
        # 并将其与voxel_features[:, :, :3]相减得到中心相对位置。
        # 体素坐标通常用一个四元组 (batch_idx, z_idx, y_idx, x_idx) 来表示,对于一个体素在对应坐标轴上的位置可以通过x = x_idx * voxel_x + x_offset计算
        f_center = torch.zeros_like(voxel_features[:, :, :3])   # 创建一个和voxel_features[:, :, :3]形状一样的全零张量f_center,这个张量是用来存储中心相对位置
        f_center[:, :, 0] = voxel_features[:, :, 0] - (
                coords[:, 3].to(voxel_features.dtype).unsqueeze(
                    1) * self.voxel_x + self.x_offset)  # coords[:, 3]、coords[:, 2]和coords[:, 1]是获取每个体素的x、y、z坐标
        # unsqueeze(1)是将它们的形状从(N,)变为(N,1),方便进行广播运算
        f_center[:, :, 1] = voxel_features[:, :, 1] - (
                coords[:, 2].to(voxel_features.dtype).unsqueeze(
                    1) * self.voxel_y + self.y_offset)
        f_center[:, :, 2] = voxel_features[:, :, 2] - (
                coords[:, 1].to(voxel_features.dtype).unsqueeze(
                    1) * self.voxel_z + self.z_offset)

        if self.use_absolute_xyz:
            features = [voxel_features, f_cluster, f_center]
        else:
            features = [voxel_features[..., 3:], f_cluster, f_center]

        if self.with_distance:
            points_dist = torch.norm(voxel_features[:, :, :3], 2, 2,
                                     keepdim=True)
            features.append(points_dist)
        features = torch.cat(features, dim=-1)    # 拼接特征:将聚类特征、中心特征和其他特征(如法向量)进行拼接,得到每个体素的最终特征。

        voxel_count = features.shape[1]
        mask = self.get_paddings_indicator(voxel_num_points, voxel_count,
                                           axis=0)
        mask = torch.unsqueeze(mask, -1).type_as(voxel_features)
        features *= mask   # 掩码矩阵mask中的值为0或1,用于将无效的体素特征(即padding的体素)置为0,有效的体素特征不变

        for pfn in self.pfn_layers:
            features = pfn(features)
        features = features.squeeze()
        batch_dict['pillar_features'] = features
        return batch_dict
        # 首先通过一个for循环遍历self.pfn_layers列表中的所有PFN层,对输入的features进行多次处理和转换。在每个PFN层中,
        # features会被送入多个卷积和归约操作,以得到更加高维和抽象的特征表示。
        # 最终处理完的features被进行squeeze操作,将维度为1的维度去除掉,
        # 然后将结果赋值给batch_dict字典的pillar_features键。最后将batch_dict返回

Supongo que te gusta

Origin blog.csdn.net/weixin_45080292/article/details/129880756
Recomendado
Clasificación