追踪器识别的目标ID在每一帧不连续/希望流式追踪识别

启用persist参数后,追踪器会在不同帧之间保留目标状态。

1
model.track(source=image, tracker="botsort.yaml", show=False,persist=True,verbose=False)

其他参数

  • show:是否显示追踪结果
  • verbose:是否打印详细信息

追踪器因目标丢失导致ID在时间上不连续

  1. 尝试换用botsort追踪器:该追踪器的算法更先进,有更加非线性的轨迹预测
  2. 尝试开启ReID功能:ReID可以在目标丢失后重新识别目标(基于外观相似性&轨迹预测),从而保持ID的连续性。
  3. 为追踪器进行经验调参

重置追踪器状态

当你要使用同一个追踪器串行地处理多个视频时,你可能希望在更换视频源时重置追踪器的状态:

1
2
3
4
# Load your model
model = YOLO('yolov8n.pt')
# When you need to reset the tracker
model.predictor.trackers[0].reset()

获取追踪器的可视化结果:

1
2
3
annotated_frame = frame.copy() # 输入YOLO模型的原图像
for result in results:
annotated_frame = result.plot(img=annotated_frame)#将框框画在图上

这样之后,就可以用opencv对其进行进一步操作了。

将向量图片转换为jpeg格式

实测转换为jpeg的开销比png小得多。质量设为60可以较好地平衡文件质量与大小的关系,可以满足实时传输。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def numpy_array_to_img_bytes(arr):
# 确保数组的数据类型为uint8
arr = arr.astype(np.uint8)
# 创建PIL图像对象
image = Image.fromarray(arr)
# 创建一个内存缓冲区
buffer = io.BytesIO()
# 将图像保存为PNG格式到缓冲区
image.save(buffer, format='JPEG', quality=60)
# 获取缓冲区中的字节数据
img_bytes = buffer.getvalue()
# 关闭缓冲区
buffer.close()
return img_bytes

透视校正

如果摄像头与目标平面之间有透视关系,需要进行透视矫正。以下代码将一个图像中的四边形区域映射到矩形区域。

原理见:透视变换数学原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def perspectiveMap(rectangleApex: list, width: float, height: float, originalPoint: Tuple[float, float]) -> Tuple[float, float]:
"""
将源图像中的点通过透视变换映射到目标长方形坐标系

:param rectangleApex: 源图像中的4个顶点坐标,格式为[[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
:param width: 目标长方形的宽度
:param height: 目标长方形的高度
:param originalPoint: 源图像中需要映射的点坐标 (x, y)
:return: 映射到目标长方形坐标系后的坐标 (u, v)
"""
# 构建目标长方形的4个顶点坐标
pts2 = np.float32([[0, 0], [width, 0], [width, height], [0, height]])
pts1 = np.float32(rectangleApex)
# 构建线性方程组矩阵A
A = []
for i in range(4):
x, y = pts1[i]
u, v = pts2[i]
A.append([x, y, 1, 0, 0, 0, -u * x, -u * y, -u])
A.append([0, 0, 0, x, y, 1, -v * x, -v * y, -v])
A = np.array(A)
# 求解透视变换矩阵H(利用SVD分解)
U, S, Vt = np.linalg.svd(A)
H = Vt[-1].reshape(3, 3)

# 对输入点进行透视变换
x, y = originalPoint
denom = H[2, 0] * x + H[2, 1] * y + H[2, 2]
u = (H[0, 0] * x + H[0, 1] * y + H[0, 2]) / denom
v = (H[1, 0] * x + H[1, 1] * y + H[1, 2]) / denom

return (u, v)

透视变换数学原理

利用透视变换将YOLOv8的检测结果进行矫正

1. 透视变换与线性方程组构建

在透视变换里,源图像点 (x,y)(x,y) 与目标图像点 (u,v)(u,v) 满足以下关系(在齐次坐标下):

[ωuωvω]=[h11h12h13h21h22h23h31h32h33][xy1]\begin{bmatrix} \omega u \\ \omega v \\ \omega \end{bmatrix} = \begin{bmatrix} h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33} \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}

消去 ω\omega 后得到:

{u=h11x+h12y+h13h31x+h32y+h33v=h21x+h22y+h23h31x+h32y+h33\begin{cases} u = \frac{h_{11}x + h_{12}y + h_{13}}{h_{31}x + h_{32}y + h_{33}} \\ v = \frac{h_{21}x + h_{22}y + h_{23}}{h_{31}x + h_{32}y + h_{33}} \end{cases}

设源图像的 4 个顶点坐标为 (xi,yi)(x_i,y_i),目标图像的 4 个顶点坐标为 (ui,vi)(u_i,v_i)i=1,2,3,4i = 1,2,3,4。将其代入并展开,得到线性方程组 Ah=0\mathbf{A}\mathbf{h}=\mathbf{0},其中:

A=[x1y11000u1x1u1y1u1000x1y11v1x1v1y1v1x2y21000u2x2u2y2u2000x2y21v2x2v2y2v2x3y31000u3x3u3y3u3000x3y31v3x3v3y3v3x4y41000u4x4u4y4u4000x4y41v4x4v4y4v4]\mathbf{A} = \begin{bmatrix} x_1 & y_1 & 1 & 0 & 0 & 0 & -u_1x_1 & -u_1y_1 & -u_1 \\ 0 & 0 & 0 & x_1 & y_1 & 1 & -v_1x_1 & -v_1y_1 & -v_1 \\ x_2 & y_2 & 1 & 0 & 0 & 0 & -u_2x_2 & -u_2y_2 & -u_2 \\ 0 & 0 & 0 & x_2 & y_2 & 1 & -v_2x_2 & -v_2y_2 & -v_2 \\ x_3 & y_3 & 1 & 0 & 0 & 0 & -u_3x_3 & -u_3y_3 & -u_3 \\ 0 & 0 & 0 & x_3 & y_3 & 1 & -v_3x_3 & -v_3y_3 & -v_3 \\ x_4 & y_4 & 1 & 0 & 0 & 0 & -u_4x_4 & -u_4y_4 & -u_4 \\ 0 & 0 & 0 & x_4 & y_4 & 1 & -v_4x_4 & -v_4y_4 & -v_4 \\ \end{bmatrix}

h=[h11h12h13h21h22h23h31h32h33]\mathbf{h} = \begin{bmatrix} h_{11} \\ h_{12} \\ h_{13} \\ h_{21} \\ h_{22} \\ h_{23} \\ h_{31} \\ h_{32} \\ h_{33} \end{bmatrix}

2. 对矩阵 A\mathbf{A} 进行奇异值分解

对矩阵 A\mathbf{A} 进行奇异值分解,得到:

A=UΣVT\mathbf{A} = \mathbf{U}\mathbf{\Sigma}\mathbf{V}^T

其中:

  • U\mathbf{U} 是一个 8×88\times8 的正交矩阵,满足 UTU=I8\mathbf{U}^T\mathbf{U}=\mathbf{I}_8
  • Σ\mathbf{\Sigma} 是一个 8×98\times9 的对角矩阵,主对角线元素为奇异值 σ1σ2σ80\sigma_1\geq\sigma_2\geq\cdots\geq\sigma_8\geq0,其余元素为 0,即

Σ=[σ10000000σ20000000σ80000]\mathbf{\Sigma} = \begin{bmatrix} \sigma_1 & 0 & \cdots & 0 & 0 & \cdots & 0 & 0 & 0 \\ 0 & \sigma_2 & \cdots & 0 & 0 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \ddots & \vdots & \vdots & \cdots & \vdots & \vdots & \vdots \\ 0 & 0 & \cdots & \sigma_8 & 0 & \cdots & 0 & 0 & 0 \end{bmatrix}

  • V\mathbf{V} 是一个 9×99\times9 的正交矩阵,满足 VTV=I9\mathbf{V}^T\mathbf{V}=\mathbf{I}_9

3. 求解 h\mathbf{h}

A=UΣVT\mathbf{A}=\mathbf{U}\mathbf{\Sigma}\mathbf{V}^T 代入 Ah=0\mathbf{A}\mathbf{h}=\mathbf{0},由于 U\mathbf{U} 是正交矩阵,左乘 UT\mathbf{U}^T 可得:

ΣVTh=0\mathbf{\Sigma}\mathbf{V}^T\mathbf{h}=\mathbf{0}

z=VTh\mathbf{z}=\mathbf{V}^T\mathbf{h},则方程变为 Σz=0\mathbf{\Sigma}\mathbf{z}=\mathbf{0},即:

[σ10000000σ20000000σ80000][z1z2z8z9]=[000]\begin{bmatrix} \sigma_1 & 0 & \cdots & 0 & 0 & \cdots & 0 & 0 & 0 \\ 0 & \sigma_2 & \cdots & 0 & 0 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \ddots & \vdots & \vdots & \cdots & \vdots & \vdots & \vdots \\ 0 & 0 & \cdots & \sigma_8 & 0 & \cdots & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} z_1 \\ z_2 \\ \vdots \\ z_8 \\ z_9 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \\ \vdots \\ 0 \end{bmatrix}

这等价于:

{σ1z1=0σ2z2=0σ8z8=0\begin{cases} \sigma_1z_1 = 0 \\ \sigma_2z_2 = 0 \\ \vdots \\ \sigma_8z_8 = 0 \end{cases}

通常情况下,若矩阵 A\mathbf{A} 满秩,σ1σ2σ8>0\sigma_1\geq\sigma_2\geq\cdots\geq\sigma_8 > 0,为使方程成立,z1=z2==z8=0z_1 = z_2 = \cdots = z_8 = 0。不妨令 z9=1z_9 = 1,则 z=[0,0,,0,1]T\mathbf{z} = [0, 0, \cdots, 0, 1]^T

因为 z=VTh\mathbf{z}=\mathbf{V}^T\mathbf{h}V\mathbf{V} 是正交矩阵,所以 h=Vz\mathbf{h}=\mathbf{V}\mathbf{z}。根据矩阵乘法规则,h\mathbf{h} 就是 V\mathbf{V} 的最后一列,即:

h=V[0001] \mathbf{h}=\mathbf{V}\begin{bmatrix} 0 \\ 0 \\ \vdots \\ 0 \\ 1 \end{bmatrix}

4. 得到透视变换矩阵 H\mathbf{H}

h\mathbf{h} 重塑为 3×33\times3 的矩阵,就得到了透视变换矩阵 H\mathbf{H}

H=[h11h12h13h21h22h23h31h32h33]\mathbf{H} = \begin{bmatrix} h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33} \end{bmatrix}

综上所述,通过上述一系列数学步骤,利用奇异值分解成功求解出了透视变换矩阵 H\mathbf{H}

参考

https://github.com/ultralytics/ultralytics/issues/4945