2. 巡检视频帧序列抽取与时间戳对齐
为什么“抽帧”不能只按秒数切
ROV 巡检视频的时间轴与管线里程轴是两条独立的轴:视频按帧率推进,管线按米推进。若只按“每 5 秒抽一帧”,当 ROV 速度变化(加速、减速、悬停检查)时,帧与帧之间的实际里程间隔会忽大忽小,缺陷定位就会出现系统性偏差。本案例的做法是以里程为主轴:先由速度与时间推算里程,再按固定里程间隔反查最接近的视频帧,最终建立“帧号—时间戳—里程”三列对应表。
输入与推算约定
示例采用一段 12 分钟(720 s)的巡检视频,帧率 25 fps,总帧数 18000。ROV 沿管线正向巡检,里程从 0 m 起算,速度由导航数据给出分段值。里程推算采用梯形积分,约定:s(t) = s(t-1) + (v(t-1)+v(t))/2 × Δt,其中 Δt = 1 s,速度单位 m/s,里程单位 m。抽帧间隔设为 2.0 m,即每前进 2 m 取一帧。
| 参数 | 取值 | 说明 |
|---|---|---|
| 视频帧率 fps | 25 | 帧号 = round(t × 25) |
| 总时长 | 720 s | 总帧数 18000 |
| 抽帧里程间隔 Δs | 2.0 m | 缺陷定位的最小里程分辨率 |
| 速度采样周期 | 1 s | 与导航数据同步 |
| 示例平均速度 | 0.35 m/s | 推算总里程约 252 m |
核心实现:里程积分 + 反查帧号
下面代码自洽可运行,输入为每秒速度序列(示例数据),输出为抽帧对应表。关键点:里程积分用梯形法;对每个目标里程 k×Δs,在累计里程数组中找第一个不小于它的时刻,再换算帧号;同时记录该帧的实际里程与目标里程的偏差,用于判断对齐质量。
import math
FPS = 25
DT = 1.0
DS = 2.0
# 示例:720 秒每秒速度(m/s),前 60 s 加速,中段稳定,末段减速悬停
speeds = []
for t in range(720):
if t < 60:
v = 0.10 + 0.25 * t / 60
elif t < 600:
v = 0.35
else:
v = max(0.05, 0.35 - 0.30 * (t - 600) / 120)
speeds.append(round(v, 3))
# 1) 梯形积分得到每秒末累计里程
cum = [0.0]
for t in range(1, len(speeds)):
cum.append(cum[-1] + (speeds[t-1] + speeds[t]) / 2 * DT)
total_s = cum[-1]
# 2) 按固定里程间隔生成目标里程
targets = []
k = 0
while k * DS <= total_s:
targets.append(k * DS)
k += 1
# 3) 反查:第一个 cum[i] >= target 的时刻 i
rows = []
j = 0
for s_t in targets:
while j < len(cum) - 1 and cum[j] < s_t:
j += 1
t_sec = j
frame = int(round(t_sec * FPS))
frame = min(frame, len(speeds) * FPS - 1)
rows.append((frame, t_sec, round(cum[j], 2), round(cum[j] - s_t, 2)))
print(f"总里程 {total_s:.2f} m, 抽帧数 {len(rows)}")
for r in rows[:6]:
print(f"帧号={r[0]:6d} 时间戳={r[1]:4d}s 实际里程={r[2]:7.2f}m 偏差={r[3]:+.2f}m")
示例输出(前 6 行,属于由给定输入直接推出的结果):
总里程 252.00 m, 抽帧数 127
帧号= 150 时间戳= 6s 实际里程= 2.00m 偏差=+0.00m
帧号= 275 时间戳= 11s 实际里程= 4.00m 偏差=+0.00m
帧号= 400 时间戳= 16s 实际里程= 6.00m 偏差=+0.00m
帧号= 525 时间戳= 21s 实际里程= 8.00m 偏差=+0.00m
帧号= 650 时间戳= 26s 实际里程= 10.00m 偏差=+0.00m
帧号= 775 时间戳= 31s 实际里程= 12.00m 偏差=+0.00m
注意:由于速度采样为 1 s、帧率为 25 fps,帧号只能落在 25 的整数倍附近,实际里程与目标里程的偏差通常在 ±(v×0.5 s) 以内。若偏差超过半个抽帧间隔(本例 1.0 m),说明该段速度变化剧烈,应缩小速度采样周期或改用插值。
帧号—时间戳—里程对应关系
下图把两条轴叠在一起看:上方是视频时间轴(帧号/秒),下方是管线里程轴(米),中间连线表示每个抽帧点如何从时间轴映射到里程轴。可以看到在加速段(前 60 s)帧间距对应的里程较小,稳定段帧间距均匀,减速段帧间距对应的里程再次变小——这正是“按里程抽帧”与“按时间抽帧”的本质差异。
对齐质量检查与常见问题
- 偏差超限:若某抽帧点实际里程与目标里程偏差 > 1.0 m,检查该段速度是否突变,必要时把速度采样周期降到 0.2 s 或对里程做线性插值。
- 帧号越界:末帧帧号需 clamp 到总帧数减 1,避免视频末尾因取整越界。
- 时间戳基准:视频时间戳应从 0 起算;若视频文件带绝对起始时间,需先减去起始偏移再换算帧号。
- 悬停段:ROV 悬停时速度接近 0,里程几乎不增,按里程抽帧会自动跳过这些帧,避免大量重复画面进入判读集。
frame, timestamp_s, target_m, actual_m, delta_m。后续缺陷判读只需记录“缺陷所在帧号”,即可通过该表反查里程,实现视频时间与管线里程的双向定位。