如何基于关键帧高效填充视频传感器数据数组
问题
现有数组:
arr = [[180, 210, 240, 270, 300],[38.7, 38.4, 38.2, 37.9,37.7]]
其中存储了视频帧编号及对应帧的传感器采集值。视频分析程序需要获取每帧的传感器值,但传感器为避免负载仅按大间隔采样,需实现一个计算成本低的函数返回未采样帧的传感器值,视频起始分析帧未知。
填充规则如下:
- 以相邻采样帧的中间位置为分界,中间区域使用对应采样值(例如帧195-225使用帧210的数值)
- 单个视频的采样间隔固定,不同视频可不同
- 传感器采样始于视频中途(示例中为第3秒),采样前所有帧用首个采样值,采样结束后所有帧用最后一个采样值(例如
f(0)=f(180)、f(350)=f(300))
不想每次查询都进行二分查找,也考虑过预先生成全步长数组,但担心内存占用与查询性能问题。以下是预填充数组的尝试代码:
sparse_data = [[180, 210, 240, 270, 300],[38.7, 38.4, 38.2, 37.9,37.7]] delta = sparse_data[0][1] - sparse_data[0][0] fr_max = sparse_data[0][-1] + delta fr_min = sparse_data[0][0] cur_sparse_idx = 1 self.magn_data = np.zeros(fr_max, dtype=np.float32) for i in range(fr_max): if i <= (fr_min + delta//2): self.magn_data[i] = sparse_data[1][0] elif i > fr_max: self.magn_data[i] = sparse_data[1][-1] else: if (i+delta//2) % delta == 0: cur_sparse_idx += 1 self.magn_data[i] = sparse_data[1][cur_sparse_idx]
请问是否有更优实现方案?
更优实现方案
方案一:数学映射直接计算(无内存占用)
直接通过帧号的数学运算定位对应采样值,完全避免预生成数组的内存开销,单次查询计算成本极低:
def get_sensor_value(frame_num, sparse_data): frames = sparse_data[0] values = sparse_data[1] delta = frames[1] - frames[0] half_delta = delta // 2 # 采样前的帧,返回首个采样值 if frame_num <= frames[0] + half_delta: return values[0] # 采样结束后的帧,返回最后一个采样值 if frame_num >= frames[-1] - half_delta: return values[-1] # 偏移帧号后,用整除直接定位采样索引 adjusted_frame = frame_num - half_delta idx = (adjusted_frame - frames[0]) // delta return values[idx + 1]
核心逻辑是将分界点对齐到采样间隔的整数倍,用整除运算直接锁定对应采样值,无需循环或二分查找,性能最优。
方案二:优化预生成数组(numpy向量化操作)
如果必须预生成数组,放弃Python循环,改用numpy向量化操作,速度提升数倍:
import numpy as np sparse_data = [[180, 210, 240, 270, 300],[38.7, 38.4, 38.2, 37.9,37.7]] frames = np.array(sparse_data[0]) values = np.array(sparse_data[1]) delta = frames[1] - frames[0] half_delta = delta // 2 # 按需设置最大覆盖帧范围 max_frame = frames[-1] + delta all_frames = np.arange(max_frame) # 计算每个帧对应的采样索引,自动处理边界 adjusted = all_frames - half_delta idx = np.clip((adjusted - frames[0]) // delta + 1, 0, len(values)-1) magn_data = values[idx].astype(np.float32)
利用numpy的广播和向量化特性,避免Python层级的循环,生成数组的效率远高于原代码,逻辑也更简洁。
方案三:按需缓存(LRU缓存)
如果查询的帧号重复率较高,结合方案一的计算逻辑加LRU缓存,兼顾性能与内存:
from functools import lru_cache # 转成tuple保证可哈希,用于缓存 frames_tuple = tuple(sparse_data[0]) values_tuple = tuple(sparse_data[1]) delta = frames_tuple[1] - frames_tuple[0] half_delta = delta // 2 @lru_cache(maxsize=1024) def get_sensor_value(frame_num): if frame_num <= frames_tuple[0] + half_delta: return values_tuple[0] if frame_num >= frames_tuple[-1] - half_delta: return values_tuple[-1] adjusted_frame = frame_num - half_delta idx = (adjusted_frame - frames_tuple[0]) // delta return values_tuple[idx + 1]
缓存命中率高时,性能接近直接数组查询,内存占用远小于预生成全量数组。
内容的提问来源于stack exchange,提问作者Raphael
相关产品推荐
相关产品推荐

