PointNet++函数square_distance(src, dst):计算两组点之间的欧式距离(代码详解)

一、计算两组点之间的欧式距离

def square_distance(src, dst):
    """
    Calculate Euclid distance between each two points.

    src^T * dst = xn * xm + yn * ym + zn * zm;
    sum(src^2, dim=-1) = xn*xn + yn*yn + zn*zn;
    sum(dst^2, dim=-1) = xm*xm + ym*ym + zm*zm;
    dist = (xn - xm)^2 + (yn - ym)^2 + (zn - zm)^2
         = sum(src**2,dim=-1)+sum(dst**2,dim=-1)-2*src^T*dst

    Input:
        src: source points, [B, N, C]
        dst: target points, [B, M, C]
    Output:
        dist: per-point square distance, [B, N, M]
    """
    B, N, _ = src.shape
    _, M, _ = dst.shape
    dist = -2 * torch.matmul(src, dst.permute(0, 2, 1))
    dist += torch.sum(src ** 2, -1).view(B, N, 1)
    dist += torch.sum(dst ** 2, -1).view(B, 1, M)
    return dist

🍉解释:

  • B, N, _ = src.shape:获取输入源点和目标点的形状信息,其中 B 表示批量大小,N 表示源点的数量

  • _, M, _ = dst.shape:M 表示目标点的数量,C 表示每个点的维度

  • dist = -2 * torch.matmul(src, dst.permute(0, 2, 1)):这一步计算了两组点之间的叉乘积在这里插入图片描述

    • dst.permute(0, 2, 1):将目标点张量 dst 的第二维和第三维进行交换,以便进行点积
      在这里插入图片描述
      同理,src为N x C,dst为M x C,需要将M x C转置成C x M才可以进行点积(N x C)·(C x M)
    • torch.matmul :计算源点和目标点之间的点积,结果 dist 是一个形状为 [B, N, M] 的张量,表示每对源点和目标点之间的点积
  • dist += torch.sum(src ** 2, -1).view(B, N, 1):计算了源点和目标点的平方和,并将其广播到与 dist 相同的形状在这里插入图片描述

    • torch.sum(src ** 2, -1):计算张量 src 中每个点的平方和,src ^2 将 src 中的每个元素都平方,然后 torch.sum 函数对最后一个维度(即 -1 所代表的维度)进行求和,最后一个维度被求和消除。
    • 假设 src 张量的形状是 [B, N, D],其中 B 表示批量大小,N 表示点的数量,D 表示每个点的维度。那么 torch.sum(src ** 2, -1) 的结果形状将是 [B, N],其中每个元素表示了原张量中相应位置点的平方和
    • view(B, N, 1):对张量调整到[B, N, 1],以便与后续的计算相兼容
  • dist += torch.sum(dst ** 2, -1).view(B, 1, M):将这些平方和加到 dist 上,以完成欧氏距离的计算在这里插入图片描述

  • dist:张量,函数返回每对源点和目标点之间的欧氏距离的平方,形状为 [B, N, M]

计算欧式距离的平方等价于下方等式

( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 + ( z 1 − z 2 ) 2 (x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}+(z_{1}-z_{2})^{2} (x1x2)2+(y1y2)2+(z1z2)2=
x 1 2 + y 1 2 + z 1 2 + x 2 2 + y 2 2 + z 2 2 − 2 x 1 x 2 − 2 y 1 y 2 − 2 z 1 z 2 x_{1}^{2}+y_{1}^{2}+z_{1}^{2}+x_{2}^{2}+y_{2}^{2}+z_{2}^{2}-2x_{1}x_{2}-2y_{1}y_{2}-2z_{1}z_{2} x12+y12+z12+x22+y22+z222x1x22y1y22z1z2

二、举例

假设有两组点,分别是 srcdst

import torch

def square_distance(src, dst):
    B, N, _ = src.shape
    _, M, _ = dst.shape
    dist = -2 * torch.matmul(src, dst.permute(0, 2, 1))
    dist += torch.sum(src ** 2, -1).view(B, N, 1)
    dist += torch.sum(dst ** 2, -1).view(B, 1, M)
    return dist

# 定义源点和目标点
src = torch.tensor([[[1, 2, 3], [4, 5, 6]]])  # shape: [1, 2, 3]
dst = torch.tensor([[[7, 8, 9], [10, 11, 12], [13, 14, 15]]])  # shape: [1, 3, 3]

dist = square_distance(src, dst)
print(dist)

结果
在这里插入图片描述
例如
( 7 − 1 ) 2 + ( 8 − 2 ) 2 + ( 9 − 3 ) 2 = 108 (7-1)^{2}+(8-2)^{2}+(9-3)^{2}=108 (71)2+(82)2+(93)2=108

三、中间结果输出

  • 对于dist = -2 * torch.matmul(src, dst.permute(0, 2, 1))
    在这里插入图片描述
  • 对于torch.sum(src ** 2, -1)
    在这里插入图片描述
    • tensor([[14, 77]]):这是一个形状为 (1, 2) 的张量,表示一个批次中有两个源点,每个源点有两个坐标分量。具体地,它包含了以下信息:第一个源点的坐标是 (14, 77)。
  • 对于torch.sum(src ** 2, -1).view(B, N, 1)
    在这里插入图片描述
    • tensor([[[14], [77]]]):这是一个形状为 (1, 2, 1) 的张量,表示一个批次中有两个目标点,每个目标点有一个坐标分量。具体地,它包含了以下信息:
      第一个目标点的坐标是 (14)
      第二个目标点的坐标是 (77)
  • 对于dist += torch.sum(src ** 2, -1).view(B, N, 1)
    在这里插入图片描述
  • 对于torch.sum(dst ** 2, -1)
    在这里插入图片描述
  • 对于torch.sum(src ** 2, -1).view(B, N, 1)
    在这里插入图片描述
  • 对于dist += torch.sum(src ** 2, -1).view(B, N, 1)
    在这里插入图片描述
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是优化后的代码,主要是添加了一些错误检查和优化了内存拷贝操作: ```cpp void Camera3OutputStream::cropI420(char *src_i420_data, int width, int height, char *dst_i420_data, int dst_width, int dst_height, int left, int top) { if (left < 0 || top < 0 || dst_width <= 0 || dst_height <= 0) { return; } int right = left + dst_width; int bottom = top + dst_height; if (right > width || bottom > height) { return; } int src_y_size = width * height; int src_uv_size = src_y_size / 4; int dst_y_size = dst_width * dst_height; int dst_uv_size = dst_y_size / 4; unsigned char *src_y_data = (unsigned char *)src_i420_data; unsigned char *src_u_data = (unsigned char *)src_i420_data + src_y_size; unsigned char *src_v_data = (unsigned char *)src_i420_data + src_y_size + src_uv_size; unsigned char *dst_y_data = (unsigned char *)dst_i420_data; unsigned char *dst_u_data = (unsigned char *)dst_i420_data + dst_y_size; unsigned char *dst_v_data = (unsigned char *)dst_i420_data + dst_y_size + dst_uv_size; for (int i = 0; i < dst_height; i++) { memcpy(dst_y_data + i * dst_width, src_y_data + (top + i) * width + left, dst_width); } for (int i = 0; i < dst_height / 2; i++) { memcpy(dst_u_data + i * dst_width / 2, src_u_data + ((top + i * 2) * width + left) / 2, dst_width / 2); memcpy(dst_v_data + i * dst_width / 2, src_v_data + ((top + i * 2) * width + left) / 2, dst_width / 2); } } ``` 优化后的代码先进行了一些错误检查,确保裁剪区域在原始图像范围内,同时目标图像的宽度和高度也必须大于0。然后计算出各个数据区域的大小和指针,使用 `memcpy` 函数进行内存拷贝操作。这种方式比使用 libyuv 库中的 `ConvertToI420` 函数更加高效,因为它避免了重复的内存分配和拷贝操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值