如何对齐 Re-ID 与 IoU 距离矩阵以计算 MOT 匹配代价?
结论
错误是两个距离数组的形状不一致造成的,和标量 alpha 的乘法无关。MRE 中,reid_dist 的形状是 (5, 3),而示例特意将 iou_dist 生成为 (5,)。NumPy 从末尾维度开始进行广播,3 和 5 无法对齐,因此相加时报错。
每个轨迹和每个检测框都需要一个配对代价,因此 Re-ID 距离、IoU 距离和最终代价矩阵都应为 (N, M)。第一段代码没有提供 compute_iou 的实现,所以无法仅凭现有材料确定它实际返回的形状。需要检查它的输出是否为二维配对矩阵。
计算成对 IoU 并组合代价
下面的实现假设边界框格式为 [x1, y1, x2, y2],每一行对应一个轨迹框或检测框。它会计算每个轨迹框与每个检测框之间的 IoU,输出形状为 (N, M)。任一输入为空时,也会返回形状对应的空矩阵。
import numpy as np
from scipy.spatial.distance import cdist
def compute_iou(track_boxes, det_boxes):
track_boxes = np.asarray(track_boxes, dtype=float)
det_boxes = np.asarray(det_boxes, dtype=float)
if track_boxes.ndim != 2 or track_boxes.shape[1] != 4:
raise ValueError("track_boxes 的形状应为 (N, 4)")
if det_boxes.ndim != 2 or det_boxes.shape[1] != 4:
raise ValueError("det_boxes 的形状应为 (M, 4)")
n, m = len(track_boxes), len(det_boxes)
if n == 0 or m == 0:
return np.empty((n, m), dtype=float)
left_top = np.maximum(track_boxes[:, None, :2], det_boxes[None, :, :2])
right_bottom = np.minimum(track_boxes[:, None, 2:], det_boxes[None, :, 2:])
intersection_wh = np.maximum(right_bottom - left_top, 0.0)
intersection = intersection_wh[..., 0] * intersection_wh[..., 1]
track_wh = np.maximum(track_boxes[:, 2:] - track_boxes[:, :2], 0.0)
det_wh = np.maximum(det_boxes[:, 2:] - det_boxes[:, :2], 0.0)
track_area = (track_wh[:, 0] * track_wh[:, 1])[:, None]
det_area = (det_wh[:, 0] * det_wh[:, 1])[None, :]
union = track_area + det_area - intersection
iou = np.zeros((n, m), dtype=float)
np.divide(intersection, union, out=iou, where=union > 0)
return iou
def compute_cost_matrix(track_features, det_features, track_boxes, det_boxes, alpha=0.5):
track_features = np.asarray(track_features)
if hasattr(det_features, "numpy"):
det_features = det_features.numpy()
det_features = np.asarray(det_features)
if track_features.ndim != 2 or det_features.ndim != 2:
raise ValueError("特征数组应为二维,形状分别为 (N, D) 和 (M, D)")
if track_features.shape[0] != len(track_boxes):
raise ValueError("轨迹特征数必须与轨迹框数一致")
if det_features.shape[0] != len(det_boxes):
raise ValueError("检测特征数必须与检测框数一致")
if track_features.shape[1] != det_features.shape[1]:
raise ValueError("轨迹特征与检测特征的维度必须一致")
reid_dist = cdist(track_features, det_features, metric="cosine")
iou_dist = 1.0 - compute_iou(track_boxes, det_boxes)
if reid_dist.shape != iou_dist.shape:
raise ValueError(
f"距离矩阵形状不一致:reid={reid_dist.shape}, iou={iou_dist.shape}"
)
return alpha * reid_dist + (1.0 - alpha) * iou_dist
代码用 [:, None, :] 和 [None, :, :] 为轨迹框与检测框增加配对维度,从而计算每一对框的交集和面积。因此,compute_iou 返回 (N, M),iou_dist = 1.0 - iou 的形状也保持为 (N, M)。
排查现有实现
在原函数中,可以先打印组合前的形状:
print("reid_dist:", reid_dist.shape)
print("iou_matrix:", iou_matrix.shape)
如果实际的 compute_iou 返回 (N,),先确认这个数组表示什么。如果它只为每条轨迹计算一个值,就需要调整计算逻辑,让它分别计算每个轨迹和每个检测之间的 IoU。只有在有意让同一条轨迹的所有检测复用该值时,才可以用 iou_dist[:, None] 将它扩展为 (N, 1) 并进行广播。这样每行的 IoU 距离会在所有检测列重复,不等同于成对计算 IoU。
当 N=0 或 M=0 时,配对矩阵的形状分别为 (0, M) 或 (N, 0),表示没有可匹配的轨迹与检测。调用方应根据空矩阵跳过匹配步骤。如果输入边界框为空,应保留 (0, 4) 形状。
注意事项
- 示例使用
[x1, y1, x2, y2]格式。如果你的框使用[x, y, width, height],需要先转换,否则 IoU 计算不适用。 - TensorFlow 张量调用
.numpy()适用于可访问 NumPy 值的执行环境。如果在图模式下运行,应在进入 SciPy 计算前,将特征转换到可用的 NumPy 执行流程中。 - 特征向量若包含零向量,cosine distance 可能产生非有限值。如果结果出现
NaN,应检查特征提取输出,并在匹配前处理这类向量。 alpha决定两种距离的权重。确认两种距离的数值范围和含义适合直接加权;调整alpha无法修复形状错误。
备注:内容仅供参考。
