【Deep-OC-SORT 03】多轨迹算法Deep-OC-SORT-卡尔曼滤波预测过程

卡尔曼滤波预测过程(7 维与 8 维)

Deep-OC-SORT 的每条轨迹由一个卡尔曼滤波器驱动:预测(predict)外推目标运动,修正(update)用检测框拉回。滤波器有两种规格:

规格 开关 状态向量 特点
8 维(默认) 不传 --new_kf_off x,y,w,h,vx,vy,vw,vh 宽高直接建模,噪声随目标尺寸自适应
7 维 传 --new_kf_off cx,cy,s,r,vcx,vcy,vs 面积+宽高比间接表示尺寸,固定噪声

两种规格共用同一个滤波器本体 KalmanFilterNew(predict / update / freeze / unfreeze / apply_affine_correction),差异在状态定义、噪声参数与保护逻辑。本文先讲 7 维------结构简单,且带建档、预测、修正、冻结、重放的逐步数字实算,是理解卡尔曼部分的最短路径;再讲 8 维(默认规格);最后给两者速查对照。

代码位置:deep_ocsort/kalmanfilter.py(滤波器本体)、deep_ocsort/ocsort.py(KalmanBoxTracker 封装:矩阵定义、噪声函数、保护逻辑、坐标转换)。


一、7 维 KF(--new_kf_off 时的规格)

1.1 状态与观测

复制代码
状态 x = [cx, cy, s, r, vcx, vcy, vs]ᵀ      (7x1 列向量)
         └─中心─┘ └尺寸┘  └── 速度 ──┘

观测 z = [cx, cy, s, r]ᵀ                     (4x1)
分量 含义
cx, cy 检测框中心坐标
s 面积(scale)= w × h------尺寸不直接记 w、h,记它们的乘积
r 宽高比 = w / h------面积 + 宽高比可唯一还原宽高
vcx, vcy 中心速度
vs 面积变化速度------r 没有速度项,宽高比被认为帧间不变

坐标转换(convert_bbox_to_z / convert_x_to_bbox):

复制代码
转观测:w = x2−x1, h = y2−y1, cx = x1+w/2, cy = y1+h/2, s = w·h, r = w/(h+1e-6)
转回框:w = √(s·r), h = s/w, x1 = cx−w/2, y1 = cy−h/2, x2 = cx+w/2, y2 = cy+h/2

转移矩阵 F 与观测映射 H

python 复制代码
# 7x7:cx += vcx、cy += vcy、s += vs 各前进一步;r 无速度保持不变
F = [[1,0,0,0, 1,0,0],
     [0,1,0,0, 0,1,0],
     [0,0,1,0, 0,0,1],
     [0,0,0,1, 0,0,0],
     [0,0,0,0, 1,0,0],
     [0,0,0,0, 0,1,0],
     [0,0,0,0, 0,0,1]]

# 4x7:只观测到中心与尺寸(s、r),速度隐含
H = [[1,0,0,0, 0,0,0],
     [0,1,0,0, 0,0,0],
     [0,0,1,0, 0,0,0],
     [0,0,0,1, 0,0,0]]

1.2 初始化与建档实算

构造(KalmanBoxTracker.__init__ 的 7 维分支):F、H 如上,P、Q、R 从单位阵出发三步缩放:

python 复制代码
self.kf.R[2:, 2:] *= 10.0     # s、r 的观测噪声 ×10:面积/宽高比的检测抖动远比中心坐标大
self.kf.P[4:, 4:] *= 1000.0   # 初速度方差 ×1000
self.kf.P *= 10.0             # 全体方差再 ×10
self.kf.Q[-1, -1] *= 0.01     # vs 的过程噪声 ×0.01
self.kf.Q[4:, 4:] *= 0.01     # 三个速度的过程噪声 ×0.01(vs 被缩两次 → 最小)
self.kf.x[:4] = convert_bbox_to_z(bbox)   # 初速度全 0

得到的三个矩阵(全部是对角阵,全局常数,与目标大小无关):

矩阵 值 含义
P₀(状态协方差) diag(10, 10, 10, 10, 10000, 10000, 10000) 初速度高度不可信(×1000×10 = 万倍)
R(观测噪声) diag(1, 1, 10, 10) s、r 的观测噪声 ×10
Q(过程噪声) diag(1, 1, 1, 1, 0.01, 0.01, 0.0001) 速度几乎不变,vs 最稳

建档实算------检测框 x1,y1,x2,y2 = 100, 200, 180, 250:

复制代码
① 转观测:w = 180−100 = 80, h = 250−200 = 50
         cx = 100+40 = 140, cy = 200+25 = 225
         s = 80×50 = 4000, r = 80/50 = 1.6
         z = [140, 225, 4000, 1.6]ᵀ
② 装状态:x = [140, 225, 4000, 1.6, 0, 0, 0]ᵀ
③ 装矩阵:P = P₀、R、Q、F、H(上表)

反算验证:w = √(s·r) = √6400 = 80,h = 4000/80 = 50 ✓------观测和状态能无损还原出宽高。

1.3 predict:盲推一步(实算)

复制代码
x = F·x              位置外推:cx+=vcx、cy+=vcy、s+=vs(r 不动)
P = F·P·Fᵀ + Q       协方差外推:不确定性随时间膨胀

实算------设跟踪若干帧后 x = 100, 200, 4000, 1.6, 20, 5, −10ᵀ(cx 每帧 +20、cy +5、面积每帧 −10),P = diag(9, 9, 25, 1, 16, 16, 4):

复制代码
x' = F·x = [100+20, 200+5, 4000−10, 1.6, 20, 5, −10]ᵀ
         = [120, 205, 3990, 1.6, 20, 5, −10]ᵀ

P' 的非零项(P 是对角阵,P' 只在「量与同轴速度」之间长出交叉项):
  P'cx,cx   = Pcx + 2P(cx,vcx) + Pvcx + Qcx = 9 + 0 + 16 + 1 = 26   ← 位置不确定性被速度不确定性撑大
  P'cx,vcx  = P(cx,vcx) + Pvcx              = 0 + 16 = 16           ← 交叉项:见位置误差可反推速度误差
  P'cy,cy / P'cy,vcy = 26 / 16              (与 cx 同构)
  P's,s     = 25 + 0 + 4 + 1 = 30
  P's,vs    = 0 + 4 = 4
  P'r,r     = 1 + Qr = 2                    ← r 没有速度,只加过程噪声
  P'vcx,vcx = 16 + 0.01 = 16.01             (vcy 同构)
  P'vs,vs   = 4 + 0.0001 = 4.0001

交叉项 P(cx,vcx) = 16 是下一步 update 能"看到位置误差就修速度"的关键。

KalmanBoxTracker.predict 的保护与状态维护(7 维分支):

  1. 面积保护:if s + vs ≤ 0: vs = 0------外推面积不能是负的;
  2. Q = None:不传新 Q,KF 用建好的固定 Q;
  3. 状态维护:age += 1(帧年龄,观测字典的键);上帧漏检(time_since_update > 0)则 hit_streak = 0;time_since_update += 1。

1.4 update:观测修正(实算)

复制代码
y = z − H·x            残差:观测与预测的差
S = H·P·Hᵀ + R         系统不确定度
K = P·Hᵀ·S⁻¹           卡尔曼增益:在 0~1 之间调配"信预测还是信观测"
x = x + K·y            状态修正:按增益分配残差
P = (I−K·H)·P·(I−K·H)ᵀ + K·R·Kᵀ    协方差收缩(Joseph 形式,数值稳定)

实算------设预测后 x = 150, 250, 5000, 0.5, 25, 0, 0ᵀ,P(含上一轮长出的位置-速度交叉项):

复制代码
      cx   cy   s    r    vcx  vcy  vs
P = [ 12    0    0    0   11    0    0 ]
    [  0   12    0    0    0   11    0 ]
    [  0    0   21    0    0    0   11 ]
    [  0    0    0    6    0    0    0 ]
    [ 11    0    0    0   11    0    0 ]
    [  0   11    0    0    0   11    0 ]
    [  0    0   11    0    0    0   11 ]

检测观测 z = 152, 250, 5100, 0.48ᵀ,R = diag(1, 1, 10, 10)。五步:

复制代码
① y = z − H·x = [152−150, 250−250, 5100−5000, 0.48−0.5]ᵀ = [2, 0, 100, −0.02]ᵀ
② S = H·P·Hᵀ + R = diag(12+1, 12+1, 21+10, 6+10) = diag(13, 13, 31, 16)
③ K = P·Hᵀ·S⁻¹(7×4,交叉项让速度行也非零):
      cx  行 [0.923, 0,     0, 0    ]     ← 12/13
      cy  行 [0,     0.923, 0, 0    ]
      s   行 [0,     0,     0.677, 0]     ← 21/31(s 的 R=10,增益低一截)
      r   行 [0,     0,     0, 0.375]     ← 6/16
      vcx 行 [0.846, 0,     0, 0    ]     ← 11/13:位置残差直接驱动速度修正
      vcy 行 [0,     0.846, 0, 0    ]
      vs  行 [0,     0,     0.355, 0]     ← 11/31
④ x' = x + K·y = [150+1.85, 250+0, 5000+67.7, 0.5−0.008, 25+1.69, 0, 0+35.48]ᵀ
              = [151.85, 250, 5067.7, 0.492, 26.69, 0, 35.48]ᵀ
⑤ P' 收缩(Joseph 形式):
      P'cx,cx = 0.923    ← 12 → 0.923:一次观测把位置方差压掉 92%
      P'cx,vcx = 0.846   P'vcx,vcx = 1.692  ← 11 → 1.69:速度方差大幅收缩
      P's,vs = 3.548     P'vs,vs = 7.097    P'r,r = 3.75

注意 ④ 的 vs:单次面积残差(+100)经由交叉项直接推出"面积正以 ~35/帧 增长";而 r 无速度建模,宽高比残差(−0.02)只能修 r 自己------这正是 7 维对"目标侧转/宽高渐变"响应慢的根源(8 维的对策见第二章)。

KalmanBoxTracker.update 的观测登记与状态维护(两种规格一致):

复制代码
1. frozen = False(解除漏检态)
2. 从 observations 找 Δt(3) 帧前的观测,与本次观测算 speed_direction → self.velocity(VDC 惯性方向)
3. last_observation = 本次检测框(OCR 找回轮的锚点)
4. observations[age] = 本次观测;history_observations 追加
5. time_since_update = 0;hits += 1;hit_streak += 1

若本帧是从漏检找回(此前收到过 None),KF.update 内部会先 unfreeze 重放再做本次修正------见 1.5。

1.5 ORU:漏检冻结与找回重放

问题:漏检期间每帧 predict,P 持续膨胀、速度被外推放大------找回时滤波器参数已被"盲推"污染,状态拉回容易,协方差与速度的信任关系已经乱了。ORU(Observation-centric Re-Update)的做法:漏检前冻结全部参数,找回后用两帧真实观测线性插值出"虚拟轨迹"重放 predict-update,把滤波器洗干净。

冻结(update 收到 None,即本帧这条轨迹没配上任何检测):

复制代码
KF.update(None):
    history_obs.append(None)                  # 漏检帧占位
    last_measurement = history_obs[-2]        # 冻结前最后一次真实观测(z 空间)
    attr_saved = deepcopy(整个 __dict__)       # 快照全部参数
    observed = False                           # 进入漏检态
KalmanBoxTracker 侧:frozen = True
    (predict 时 7 维照常外推;8 维额外清零 vw/vh,见 2.4)

找回重放(unfreeze:漏检后第一次 update 收到真实观测时触发):

复制代码
1. __dict__ = attr_saved                    整体回滚到冻结时刻(参数、协方差全回去)
   history_obs 去掉末尾 None;用完整历史定位最后两个真实观测的下标
2. box1 = last_measurement(冻结前最后观测,z 空间 [cx,cy,s,r])
   box2 = 本次找回的观测
   7 维先换算:w = √(s·r),h = s/r------插值要在直尺寸空间做
   (源码 r = w/float(h) 已适配为 w/h 数组除法,数值等价------本文件唯一适配改动处)
3. 帧距 time_gap = 两观测的下标差
4. 匀速假设线性插值虚拟轨迹:
   dx = (x2−x1)/gap,dy、dw、dh 同式;第 i 步 = box1 + (i+1)×步长,逐点转回 [cx,cy,s,r]
5. 逐虚拟框 update(new_box),步间 predict
   ------ 等效于"漏检期间目标匀速走且每帧都有观测"
6. 重放完的 P、K、速度信任关系是干净的,再对本次真实观测做正常 update

重放实算------漏检 2 帧后找回,完整观测历史 = z1, None, None, z2:

复制代码
z1 = [300, 400, 5120, 0.8](冻结前最后观测)    z2 = [324, 406, 5120, 0.8](找回观测)
换算:w1 = √(5120×0.8) = 64, h1 = √(5120/0.8) = 80;z2 同宽高
下标:两个真实观测在下标 0 和 3 → time_gap = 3
步长:dx = (324−300)/3 = 8, dy = (406−400)/3 = 2, dw = dh = 0

虚拟框(每帧 update,步间 predict):
  i=0: [308, 402, 5120, 0.8]
  i=1: [316, 404, 5120, 0.8]
  i=2: [324, 406, 5120, 0.8]    ← 最后一个只 update,不再 predict

一句话:漏检期间不积累错误,找回时当无事发生、匀速补课。

1.6 CMC:相机运动的仿射搬移(7 维分支)

相机自身在动(抖动/平移/缩放)时,目标"看起来"整体位移,运动模型全部失准------CMC 每帧先估计相机运动的仿射变换(线性部分 m 2×2 + 平移 t 2×1),把轨迹搬到新相机坐标系再谈预测。

KalmanBoxTracker.apply_affine_correction 先搬观测记录(两种规格一致):last_observation 与 observations 字典里 Δt 窗口内的每个观测框,4 个角点坐标 m·p + t。

KF 状态部分走 KalmanFilterNew.apply_affine_correction 的 7 维分支:

python 复制代码
self.x[:2]  = m @ self.x[:2] + t            # 中心:旋转缩放 + 平移
self.x[4:6] = m @ self.x[4:6]               # 中心速度:跟着旋转缩放(方向语义保持)
self.P[:2, :2]   = m @ self.P[:2, :2] @ m.T     # 中心块共轭变换(保持正定)
self.P[4:6, 4:6] = m @ self.P[4:6, 4:6] @ m.T   # 速度块共轭变换
# s、r(x[2]、x[6])原地不动------非线性量无法直接仿射(源码的缩放补偿尝试被注释掉)

冻结快照同步:若轨迹正处于漏检态(observed=False 且有快照),attr_saved 里的 x:2、x4:6、P 两个子块与 last_measurement:2 同样搬移------找回重放的起点也在新坐标系里。

代价:s、r 吃不到相机缩放补偿------相机变焦时 7 维的尺寸估计会失真,这是 8 维(见 2.6)与 CMC 配合更优的一处。


二、8 维 KF(默认,new_kf=True)

2.1 状态与观测

复制代码
状态 x = [x, y, w, h, vx, vy, vw, vh]ᵀ      (8x1 列向量)
         └中心┘ └宽高┘ └─ 速度 ─┘

观测 z = [x, y, w, h]ᵀ                       (检测框转换而来,4x1)
  • 中心 + 宽高直接建模:w、h 是线性量,各自有速度项 vw、vh------侧转、驶近镜头等尺寸变化有直接的动态模型;
  • 坐标转换 convert_bbox_to_z_new / convert_x_to_bbox_new:中心换算与 7 维同式,尺寸直接取 w、h(无 s/r 换算)。
python 复制代码
# 8x8:位置 += 速度 × 1 帧
F = [[1,0,0,0, 1,0,0,0],
     [0,1,0,0, 0,1,0,0],
     [0,0,1,0, 0,0,1,0],
     [0,0,0,1, 0,0,0,1],
     [0,0,0,0, 1,0,0,0],
     [0,0,0,0, 0,1,0,0],
     [0,0,0,0, 0,0,1,0],
     [0,0,0,0, 0,0,0,1]]

# 4x8:只观测到位置与宽高,速度隐含
H = [[1,0,0,0, 0,0,0,0],
     [0,1,0,0, 0,0,0,0],
     [0,0,1,0, 0,0,0,0],
     [0,0,0,1, 0,0,0,0]]

匀速模型:下一帧位置 = 当前位置 + 速度 × 1 帧,速度保持不变。

2.2 初始化

复制代码
x = [检测框中心x, y, w, h, 0, 0, 0, 0]ᵀ      初速度为 0
Q = new_kf_process_noise(w, h)                尺寸自适应(见 2.3)
P = Q 的结构放大:位置块(前 4 维)×4、速度块(后 4 维)×100

P 的放大含义:协方差 = "我对这个估计有多不确定"。速度块 ×100(方差放大万倍)表示"初速度瞎猜的",前几帧 update 会以极快的速度把速度修正到观测差分出来的真实值;位置块 ×4 表示"检测框大体准但留余地"。(7 维是全局常数 ×10/×1000,见 1.2------8 维的 P 直接复用噪声结构,尺度随目标大小。)

2.3 噪声设计:随尺寸自适应(8 维核心改进)

过程噪声 Q ------ 每帧 predict 前重算

python 复制代码
def new_kf_process_noise(w, h, p=1/20, v=1/160):
    Q = diag( (p·w)², (p·h)², (p·w)², (p·h)²,     # x, y, w, h
              (v·w)², (v·h)², (v·w)², (v·h)² )    # vx, vy, vw, vh
  • 位置噪声 ∝ 目标尺寸的 1/20:w=10px 的远处小目标位置噪声 ~0.5px,w=200px 的近处大目标 ~10px------同一相对不确定度,不同绝对尺度;
  • 速度噪声 ∝ 1/160:速度比位置稳得多(同上两目标分别 0.06px、1.25px);
  • 用当前估计的 w、h 每帧重算 (predict 里 Q = new_kf_process_noise(self.kf.x[2,0], self.kf.x[3,0]))------目标变大变小,噪声尺度跟着变。

观测噪声 R ------ 每次 update 前重算

python 复制代码
def new_kf_measurement_noise(w, h, m=1/20):
    R = diag( (m·w)², (m·h)², (m·w)², (m·h)² )

观测(检测框)的可信度也随目标大小缩放:小目标 1px 的检测抖动和大目标 20px 的抖动不是一回事。

为什么这样设计

卡尔曼增益 K = P·Hᵀ·S⁻¹(S = H·P·Hᵀ + R)由 P 与 R 的相对大小决定:R 大 → 信预测不信观测,R 小 → 信观测。全局常数 R 意味着"小目标的 2px 抖动和大目标的 2px 抖动被同等对待"------对小目标,2px 已经是 20% 的宽,滤波器却以为观测很准,轨迹跟着抖;尺寸自适应后两类目标获得一致的平滑度。

2.4 predict:盲推一步

python 复制代码
x = F · x                        # 位置外推:x+=vx, y+=vy, w+=vw, h+=vh
P = F · P · Fᵀ + Q               # 协方差外推:不确定性随时间膨胀

KalmanBoxTracker.predict 在调 KF.predict 之前做两件保护:

  1. 负尺寸保护 :if w + vw ≤ 0: vw = 0(h 同理)------外推出的框宽高不能是负的,速度项越界就钳回 0;
  2. 冻结保护 :if frozen: vw = vh = 0------漏检中的轨迹不外推尺寸变化(目标被挡住时尺寸不变是更合理的先验),位置速度照常外推等找回。

随后按当前宽高算 Q 传入(Q = new_kf_process_noise(w, h))。状态维护与 7 维一致:age += 1;上帧漏检则 hit_streak = 0;time_since_update += 1。

2.5 update:观测修正

五步公式与 1.4 完全一致(含 Joseph 形式与 1.4 的全部状态维护),差异只在 R 的来源:每次 update 前 R = new_kf_measurement_noise(当前 w, h) 传入 KF.update------观测噪声跟目标尺寸走(7 维是建好的固定 R)。直觉同 1.4:P 大(刚建档、长时间漏检后)K 趋大,观测把状态大步拉回;P 小(长期稳定跟踪)K 趋小,检测抖动被滤掉。

2.6 ORU 与 CMC:8 维的不同表现

ORU :冻结/重放机制与 1.5 完全一致,唯一差异在第 2 步------z 空间本来就是 x,y,w,h,免去 s/r 换算,直接线性插值(unfreeze 的 new_kf=True 分支)。

CMC:8 维走 kron 全量搬移:

python 复制代码
big_m = np.kron(np.eye(4), m)        # m 是 2x2 仿射线性部分 → 8x8 块对角
x     = big_m · x;x[:2] += t        # 每个 (量, 其速度) 对按 m 变换,平移只加中心
P     = big_m · P · big_mᵀ           # 协方差共轭变换(保持正定)
  • x,vx、y,vy、w,vw、h,vh 四对各自被 m 旋转缩放------宽高的速度向量也跟着相机变换,方向语义保持;平移 t 只加在中心 x、y 上(相机平移不改变尺寸与速度方向);
  • 冻结快照全量同步:attr_saved 的 x、P 整体变换,last_measurement 前 2 位做 m·p + t、后 2 位做 m·p------找回重放的起点也在新坐标系里;
  • w、h、vw、vh 都能吃到相机的缩放/旋转补偿------没有 7 维 s/r 原地不动的失真问题。

三、7 维 / 8 维速查对照

环节 7 维(OC-SORT 规格,--new_kf_off) 8 维(默认)
状态 cx,cy,s,r,vcx,vcy,vs,r 无速度 x,y,w,h,vx,vy,vw,vh
尺寸语义 s=面积、r=宽高比(非线性,需换算) w、h 直接线性
建档 P₀ diag(10,10,10,10,10⁴,10⁴,10⁴)(全局常数) Q 结构:位置 ×4、速度 ×100(随尺寸)
Q 固定 diag(1,1,1,1,0.01,0.01,0.0001),构造一次 ∝ 当前宽高(p=1/20, v=1/160),每帧重算
R 固定 diag(1,1,10,10) ∝ 当前宽高(m=1/20),每次 update 重算
predict 保护 s+vs≤0 钳 vs=0 w/h 越界钳速度 + 冻结时清 vw/vh
update 五步公式(1.4 实算),固定 R 同左,仅 R 按尺寸重算
ORU 重放 s/r 先换算 w/h 插值(1.5 实算) 直接 w/h 插值
CMC 搬移 只搬中心与速度两块,s/r 不动 kron(I4,m) 全状态搬移
转换函数 convert_bbox_to_z / convert_x_to_bbox convert_bbox_to_z_new / convert_x_to_bbox_new

四、一帧里的完整时序

复制代码
轨迹视角一帧(8 维默认):
  [CMC]  apply_affine_correction(仿射)      ← 相机动了先搬坐标(观测记录 + KF 状态 + 冻结快照)
  [预测] 负尺寸保护 → 冻结时清 vw/vh → 按当前宽高算 Q → x=Fx, P=FPFᵀ+Q → age+=1
  [关联] 预测框进第一轮(IoU+VDC+外观)→ 配上或进 OCR 找回轮
  [配上] update(bbox):状态维护 + 按宽高算 R + KF 修正(若从漏检找回,先 unfreeze 重放)
         update_emb(emb, alpha):特征 EMA
  [漏检] update(None):freeze 快照 → 下帧 predict 继续外推等找回
  [输出] 本帧配上且 hit_streak≥min_hits → 输出 [x1,y1,x2,y2,id]
  [判死] time_since_update > max_age → 删除

7 维差异:CMC 只搬中心与速度两块;predict 无尺寸保护与冻结清零、Q 用固定矩阵;
         update 用固定 R;ORU 重放前先 s/r→w/h 换算。

描述 链接
项目 基于 Ultralytics +Deep-OC-SORT 多目标跟踪与改进
往期回顾 链接
数据集 数据集汇总
软件开发 深度学习软件开发汇总
相关推荐
老师我太想进步了20262 小时前
【数据结构-3】算法美学之降复杂度
数据结构·算法
机器人零零壹7 小时前
技术白皮书:iRobotCAM如何解决机器人激光切割应用中的核心难点,从CAD离线编程到双目视觉定位与轨迹自适应提升复杂空间曲线切割的精度、效率与柔性化水平
人工智能·算法
ZhengEnCi10 小时前
大模型的分类到底怎么分:从基础模型到推理模型、从通用到专用的完整图谱
算法
取名真是11 小时前
Python 算法知识点从基础到进阶
开发语言·python·算法
罗西的思考11 小时前
【轻量化复刻大模型 / KIMI K3】KDA 模块交互式演示
人工智能·算法·机器学习
小豆庄12 小时前
监控视频脱敏的追踪漂移与ID撞号陷阱
目标跟踪·ffmpeg·音视频·视频·敏捷流程
nm_zfyjt13 小时前
信息流广告投放优化实践:线索成本评估模型与转化链路重构(家装建材案例)
算法·重构·内蒙古朋友圈广告服务商·内蒙古朋友圈获客
w_zero_one15 小时前
算法--构造
java·数据结构·算法
其实防守也摸鱼15 小时前
信创安全竞赛知识总结与题库
大数据·网络·人工智能·算法·安全·php