作者在优化人体姿态检测时遇到了FP16转换、裁剪加速失败、缓存写入时序、姿态匹配阈值等坑。最终总结:先测量再动手,理解瓶颈所在,怀疑输入格式。
看到“FP16更快”,很多人会直接上手。但作者尝试将ONNX模型转成FP16,保留float32的输入输出,结果模型里塞满了Cast节点,绕过了Tensor Core加速路径,反而更慢了。
from onnxconverter_common import float16
model = float16.convert_float_to_float16(onnx.load_from_string(b), keep_io_types=True)
最后又改回FP32,速度已经够用。这件事的教训是:不要轻信“某方案一定更快”,它取决于你的运行时版本和batch大小。
作者想通过裁剪每个人体区域再变换来加速warpAffine。看起来输入小了,应该更快?错了。warpAffine的计算量取决于输出尺寸(192×256),和输入大小无关。裁剪反而带来了切片开销,速度降到0.88倍。更糟的是,裁剪跑出图像边界时,clamping让同一人的关键点偏移了最多107像素。结果不准确,也变慢了,只能放弃。
结论:下手优化前,先搞清楚真正的性能瓶颈在哪里(这里是输出像素数)。确认结果一致后再保留优化。
有一次构建失败后,每次运行都报错:Cannot deserialize with an empty memory buffer。原因是写入顺序的问题:open(..., "wb")在构建完成前就会截断文件。如果构建中途失败,留下一个0字节的文件,下次加载就直接崩了。
修复方法:先构建成功再写入文件。顺便检查文件大小,0字节就重新构建。
if not os.path.exists(cache) or os.path.getsize(cache) == 0:
data = _build(onnx_path, profiles)
with open(cache, "wb") as f:
f.write(data)
这是最有意思的一个坑。目标姿态的选择对结果质量影响超出预期。
作者试着调阈值,但效果微乎其微。最后发现问题的本质:越独特的姿态,保持时间越短;越常出现的姿态,越不独特。决定体验的不是阈值,而是姿态的选择。
解决方案是多姿态库——同时准备几个又独特又能保持住的姿态,匹配任何一个就算命中。用数量覆盖那些一闪而过的姿态,同时去掉那些烂大街的。在太极拳演示(四个保持姿势)中,匹配率从极低提升到99%(2176/2190帧)。
教训:阈值调节只是表面功夫,先想清楚你想找什么。
在WSL里打开一个人群视频,frames[0]直接IndexError。作者怀疑是WSL环境问题。实际上WSL的ffmpeg解码不了AV1,下载器偏偏选了AV1流。修复方法不是修复环境,而是在下载时就指定编码。
yt-dlp -f "bv*[vcodec!=av01][height<=720]+ba/b[vcodec!=av01][height<=720]" URL
经验:当你说“环境里跑不动”时,先怀疑输入格式。
新GPU(sm_120)在Windows上不支持native GPU计算,TensorRT只能在隔离的venv里跑,不带PyTorch和MediaPipe。通常这种时候得重写匹配逻辑。但匹配代码是纯NumPy的,作者直接把它放在sys.path里导入,原封不动使用。
import sys; sys.path.insert(0, "src")
from kasane.matching.crowd import match_people
单个人0.16ms的开销,塞进38fps的管线里毫无知觉。把重依赖和轻逻辑分开,换环境时就能无缝移植。
检测是瓶颈,作者集中优化这里,速度从18.5fps提升到38fps。三件事起了作用:
blobFromImage)。1/(detect+pose)变为1/max(detect, pose)。OpenCV、CUDA、TensorRT在繁重计算时会释放GIL,所以Python线程能真正并行。优化前两步让速度从18.5到27fps,第三步从27到38fps。
真正有决定意义的决策不多:
显而易见,但作者承认,他浪费的时间正比于他跳过这些显而易见步骤的次数。
接下来作者打算写如何从动漫插画中构建目标姿态——从2D图画中提取关节点,当真人重合时触发反应。
blobFromImage, warpAffine) — opencv.org免费获取企业 AI 成熟度诊断报告,发现转型机会
关注公众号

扫码关注,获取最新 AI 资讯
3 步完成企业诊断,获取专属转型建议
已有 200+ 企业完成诊断