Ultralytics YOLO26 Heatmap 方案:视频追踪热力图生成指南(含源码实现剖析)
本文基于 Ultralytics 仓库中的官方文档 heatmaps.md 与对应源码 heatmap.py,系统讲解 YOLO26 的 Heatmap(热力图)解决方案:如何用一条 CLI 命令或几十行 Python 代码,把任意视频转换为累计活动强度图,用于车流分析、人流动线、驻留区域等可视化任务。读完本文,你将掌握完整的 CLI/Python 调用方式、全部参数(含默认值)与色板(colormap)配置、region 区域计数联动方法,以及热力图强度累积、归一化、颜色映射与帧混合的底层实现细节。
为什么用热力图做视频分析
Heatmap 方案的核心思路是:让强度值在"被跟踪对象停留过的地方"持续累积,繁忙区域呈现暖色,安静区域保持冷色。相比逐帧人工复盘,它的优势在于:
- 一眼看穿活动模式:对象停留越久、出现越频繁的位置,强度累积越高。高流量车道、热门货架、人群拥堵点无需逐帧审查即可凸显;
- 内置跟踪能力:方案内部自动运行 YOLO26 的目标检测与跟踪,不需要额外搭建独立的跟踪管线;
- 同一趟完成计数:传入
region参数后,在热力图累积的同时统计进出指定区域的对象数量,把"热力图 + 区域计数"两项分析任务合并到一次运行中。
典型应用场景(引自官方文档的实景示例):
| 交通场景 | 零售场景 |
|---|---|
| 热力图叠加展示车辆交通密度(warm 色区 = 车流密集/缓慢路段) | 热力图叠加展示顾客店内动线(warm 色区 = 热门货架/滞留点) |
如何生成热力图:CLI 与 Python 完整示例
Heatmap 方案可以从命令行或 Python 两种方式驱动视频源。
CLI 方式
# 运行热力图示例(未指定 source 时自动下载演示视频)
yolo solutions heatmap show=True
# 指定视频源
yolo solutions heatmap source="path/to/video.mp4"
# 指定自定义色板
yolo solutions heatmap colormap=cv2.COLORMAP_INFERNO
# 热力图 + 区域计数组合使用
yolo solutions heatmap region="[(20, 400), (1080, 400), (1080, 360), (20, 360)]"
从源码结构看,CLI 模式在未提供 source 时会从官方资源地址下载默认演示视频 solutions_ci_demo.mp4(pose 模型则用 solution_ci_pose_demo.mp4),该逻辑位于 BaseSolution 初始化。
Python 方式
Python 示例逐帧读取视频、调用 heatmap(im0) 处理,并把带热力图叠加的结果帧写入输出文件:
import cv2
from ultralytics import solutions
cap = cv2.VideoCapture("path/to/video.mp4")
assert cap.isOpened(), "Error reading video file"
# Video writer
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
video_writer = cv2.VideoWriter("heatmap_output.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
# For object counting with heatmap, you can pass region points.
# region_points = [(20, 400), (1080, 400)] # line points
# region_points = [(20, 400), (1080, 400), (1080, 360), (20, 360)] # rectangle region
# region_points = [(20, 400), (1080, 400), (1080, 360), (20, 360), (20, 400)] # polygon points
# Initialize heatmap object
heatmap = solutions.Heatmap(
show=True, # display the output
model="yolo26n.pt", # path to the YOLO26 model file
colormap=cv2.COLORMAP_PARULA, # colormap of heatmap
# region=region_points, # object counting with heatmaps, you can pass region_points
# classes=[0, 2], # generate heatmap for specific classes, e.g., person and car.
)
# Process video
while cap.isOpened():
success, im0 = cap.read()
if not success:
print("Video frame is empty or processing is complete.")
break
results = heatmap(im0)
# print(results) # access the output
video_writer.write(results.plot_im) # write the processed frame.
cap.release()
video_writer.release()
cv2.destroyAllWindows() # destroy all opened windows
要点说明:
- 输出保存依赖
results.plot_im——该属性是已经叠加了热力图的处理帧,直接交给cv2.VideoWriter写入即可; region支持三种几何形式:2 个点为直线(线段计数)、4 个点为矩形、5 个点及以上为多边形(首尾重复闭合),与区域计数文档 object-counting.md 中的约定一致;classes用于按类别过滤,例如classes=[0, 2]表示只对 COCO 索引 0(person)与 2(car)生成热力图。
Heatmap() 参数参考
Heatmap 的参数统一由 SolutionConfig(一个 dataclass)集中管理,未识别的键会直接抛出 ValueError,因此传入参数名必须严格匹配。以下表格继承自官方文档的三组参数表,默认值与 SolutionConfig 源码 一致。
核心参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model |
str |
None |
Ultralytics YOLO 模型文件路径。为 None 时回退到 yolo26n.pt |
colormap |
int |
cv2.COLORMAP_DEEPGREEN |
热力图使用的 OpenCV 色板常量 |
show_in |
bool |
True |
是否显示"进入区域"的计数 |
show_out |
bool |
True |
是否显示"离开区域"的计数 |
region |
list |
None |
兴趣区域点集((x, y) 元组列表)。None 时不启用区域计数;传入后同时开启热力图与进出计数 |
line_width |
int |
2 |
绘制区域边界、计数文本框等线条的粗细 |
verbose |
bool |
True |
启用逐帧日志(输入尺寸、类别数量、处理耗时)。跟踪调用本身始终保持静默 |
跟踪参数
Heatmap 同时接受 track 相关参数,它们会被打包进 track_add_args 后透传给底层的 model.track():
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tracker |
str |
'botsort.yaml' |
跟踪算法配置文件,内置选项:botsort.yaml、bytetrack.yaml、ocsort.yaml、deepocsort.yaml、fasttrack.yaml、tracktrack.yaml,对应 ultralytics/cfg/trackers/ 目录下的 YAML |
conf |
float |
0.25 |
检测置信度阈值;调低可跟踪更多对象但可能引入误检 |
iou |
float |
0.7 |
过滤重叠检测框的 IoU 阈值 |
classes |
list |
None |
按类别索引过滤,如 classes=[0, 2, 3] 只跟踪指定类别 |
device |
str |
None |
推理设备(cpu、0 或 cuda:0 等) |
可视化参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
show |
bool |
False |
为 True 且环境支持显示时,弹出窗口实时预览叠加结果(窗口按 q 关闭,见 display_output) |
show_conf |
bool |
True |
在标注中显示每个检测的置信度 |
show_labels |
bool |
True |
在标注中显示类别标签 |
支持的色板(colormap)
colormap 参数接受任意 OpenCV 色板常量(直接传 cv2 模块中的常量)。官方文档列出以下 22 种常用选择:
| 色板名称 | 说明 |
|---|---|
cv2.COLORMAP_AUTUMN |
秋季色板 |
cv2.COLORMAP_BONE |
骨质色板 |
cv2.COLORMAP_JET |
Jet 色板 |
cv2.COLORMAP_WINTER |
冬季色板 |
cv2.COLORMAP_RAINBOW |
彩虹色板 |
cv2.COLORMAP_OCEAN |
海洋色板 |
cv2.COLORMAP_SUMMER |
夏季色板 |
cv2.COLORMAP_SPRING |
春季色板 |
cv2.COLORMAP_COOL |
冷色板 |
cv2.COLORMAP_HSV |
HSV(色相、饱和度、明度)色板 |
cv2.COLORMAP_PINK |
粉色色板 |
cv2.COLORMAP_HOT |
热色板 |
cv2.COLORMAP_PARULA |
Parula 色板(示例代码所用) |
cv2.COLORMAP_MAGMA |
Magma 色板 |
cv2.COLORMAP_INFERNO |
Inferno 色板(CLI 示例所用) |
cv2.COLORMAP_PLASMA |
Plasma 色板 |
cv2.COLORMAP_VIRIDIS |
Viridis 色板 |
cv2.COLORMAP_CIVIDIS |
Cividis 色板(色盲友好) |
cv2.COLORMAP_TWILIGHT |
Twilight 色板 |
cv2.COLORMAP_TWILIGHT_SHIFTED |
偏移 Twilight 色板 |
cv2.COLORMAP_TURBO |
Turbo 色板 |
cv2.COLORMAP_DEEPGREEN |
深绿色板(默认值) |
工作原理:源码级深度剖析
官方文档对机制的概括是:Heatmap 类继承自 ObjectCounter,首帧创建与帧同尺寸的空强度层,之后每帧执行两步——① YOLO26 检测并跟踪所有对象;② 对每个被跟踪对象,在其边界框中心附近的圆形区域内提升热力图强度;最后每帧对累计强度层做归一化、按所选色板着色,并与原帧混合叠加。下面结合源码逐环节展开。
调用链与跟踪提取
Heatmap 的继承链为 Heatmap → ObjectCounter → BaseSolution。实例化后像函数一样调用(heatmap(im0))会进入 BaseSolution.__call__,其中用 ops.Profile 分别计时"跟踪"与"方案处理"两段,并在 verbose=True 时打印逐帧日志(帧号、输入尺寸、各类对象数量、track 与 solution 毫秒耗时),结果对象的 speed 字典里也可读取这两段耗时。
真正的检测+跟踪发生在 extract_tracks:
self.tracks = self.model.track(
source=im0, persist=True, classes=self.classes, verbose=False, **self.track_add_args
)[0]
persist=True是关键:让跟踪器跨帧保持状态,同一物理对象获得稳定track_id,这是"累计活动强度"能够跨帧成立的前提;classes过滤与conf、iou、tracker、device、max_det、quantize、imgsz等参数经track_add_args一并下发;- 随后把
boxes(xyxy或 OBB 的xyxyxyxy角点)、clss、track_ids、confs抽取为 CPU 列表供后续处理; - 对于被跟踪器移除(retrieved 失败/超期)的 ID,会调用
forget_tracks清理track_history等簿记,避免 7×24 长时流下内存无限增长。
强度累积:heatmap_effect 做了什么
首次调用 process 时,按当前帧尺寸创建一个 float32 全零强度层:
if not self.initialized:
self.heatmap = np.zeros(im0.shape[:2], dtype=np.float32)
self.initialized = True
之后每个被跟踪框都会触发 heatmap_effect,其实现要点:
- 外接轴对齐框:
get_enclosing_box把 OBB 角点归一化为[x0, y0, x1, y1],并裁剪到图像边界内; - 圆形影响域:半径取
min(x1-x0, y1-y0) // 2,即在框的短边一半范围内做圆形衰减区(中心为框中心); - 向量化更新:用
np.meshgrid生成 ROI 网格,计算每点平方距离,仅对半径内的像素执行+= 2。
radius_squared = (min(x1 - x0, y1 - y0) // 2) ** 2
xv, yv = np.meshgrid(np.arange(x0, x1), np.arange(y0, y1))
dist_squared = (xv - ((x0 + x1) // 2)) ** 2 + (yv - ((y0 + y1) // 2)) ** 2
within_radius = dist_squared <= radius_squared
self.heatmap[y0:y1, x0:x1][within_radius] += 2
因此同一位置被同一对象连续跟踪 N 帧,强度增加 2N;对象停留越久、经过越频繁的位置,累计强度越高——这正是"繁忙区域发光、冷清区域保持冷色"的量化来源。
归一化、着色与混合
每帧处理末尾,只有当强度层非全零(self.heatmap.any(),即至少跟踪到过一个对象)时才执行叠加:
normalized_heatmap = cv2.normalize(self.heatmap, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
colored_heatmap = cv2.applyColorMap(normalized_heatmap, self.colormap)
plot_im = cv2.addWeighted(plot_im, 0.5, colored_heatmap, 0.5, 0)
三个固定步骤:cv2.normalize(NORM_MINMAX) 把累计值线性拉伸到 0–255 → cv2.applyColorMap 按 colormap 参数着色 → cv2.addWeighted 以 0.5/0.5 权重与原帧等权混合。由于归一化是全局 min–max,视频越播放到后面,早期区域颜色越被"压暗",最新热点趋向最暖色,形成动态累积效果。若当前帧没有跟踪到任何对象,则该帧原样输出、不叠加热力图。
注意一个与父类的差异:Heatmap.process 不绘制目标框(没有调用 annotator.box_label),只做强度累积;区域边界线则仅在传入 region 时以紫色 (104, 0, 123) 绘出,计数文本(IN/OUT)通过 display_counts 叠加在画面右上角,并受 show_in / show_out 控制。
同趟完成区域计数
传入 region 后,Heatmap.__init__ 会立即调用 initialize_region:点数 ≥3 构建 shapely.Polygon,2 点构建 LineString,并把几何对象准备(prep)后存入 self.r_s 以加速空间查询。
逐帧处理中,每个目标都会执行 store_tracking_history(记录框中心轨迹,最多保留 30 个点)与 count_objects:
- 直线区域:判断"上一位置→当前位置"连线段是否与直线相交,再根据线段方向(竖直比较 x、水平比较 y)确定 IN/OUT,每个
track_id只计一次(counted_ids去重); - 多边形区域:当前中心点落入多边形内,或从外部穿越边界时计数(快速对象可能一帧内跨越区域而不产生内部点,因此额外做了线段穿越判定);方向由最近约 5 帧的主运动轴判断(右移/下移为 IN,左移/上移为 OUT),以 5 帧基线抵抗单帧抖动。
计数结果按类别汇总进 classwise_count(形如 {"person": {"IN": 3, "OUT": 1}}),并随每帧结果返回。
返回值 SolutionResults
每帧调用返回一个 SolutionResults 对象,Heatmap 场景下有效字段:
| 字段 | 类型 | 含义 |
|---|---|---|
plot_im |
np.ndarray |
已叠加热力图(及可选区域/计数)的当前帧 |
in_count |
int |
累计进入 region 的对象数(未设 region 时为 0) |
out_count |
int |
累计离开 region 的对象数 |
classwise_count |
dict |
按类别的 IN/OUT 计数 |
total_tracks |
int |
当前帧被跟踪的对象总数 |
speed |
dict |
track 与 solution 两段耗时(ms),由 __call__ 统一注入 |
直接 print(results) 会以非空字段列表的形式输出(SolutionResults.__str__ 会排除 plot_im 与零值字段),便于快速调试。
仓库中还提供了一个交互示例 examples/heatmaps.ipynb,可对照参考其逐帧处理与保存方式。
常见问题(FAQ)
Ultralytics YOLO26 如何从视频中生成热力图?
通过跨帧跟踪对象,在每个被跟踪对象的位置累积强度值,再把累计层归一化、着色并与原帧混合。对象频繁出现或长时间停留的区域强度更高,呈现更暖的颜色。配置项见上文参数参考。
如何把热力图输出保存为视频文件?
用 cv2.VideoWriter 逐帧写入 results.plot_im,如上文 Python 示例所示;plot_im 是已叠加热力图的帧,无需再做任何处理。
能否把热力图与对象计数结合?
能。给 Heatmap() 传入 region(直线/矩形/多边形点集),方案会在构建热力图的同时统计进出该区域的对象;返回结果包含 in_count、out_count 与按类别的 classwise_count。区域配置细节可参考 object-counting.md。
如何只对特定类别做热力图?
传入 classes 参数指定类别索引,例如 classes=[0, 2] 仅使用 COCO 索引 0(person)和 2(car)构建热力图:
import cv2
from ultralytics import solutions
cap = cv2.VideoCapture("path/to/video.mp4")
heatmap = solutions.Heatmap(show=True, model="yolo26n.pt", classes=[0, 2])
while cap.isOpened():
success, im0 = cap.read()
if not success:
break
results = heatmap(im0)
cap.release()
cv2.destroyAllWindows()
与 OpenCV / Matplotlib 等通用可视化工具相比有什么不同?
Heatmap 方案把目标检测、跟踪、强度累积与叠加渲染整合到一次调用中;使用 OpenCV 或 Matplotlib 则需要自行搭建整条管线。它以实时方式处理视频流,开箱即用地支持持久化跟踪与可自定义色板。模型层面的细节可进一步参阅 YOLO26 模型文档 与 跟踪模式文档。
小结
Ultralytics YOLO26 Heatmap 方案用少量代码把跟踪结果转化为直观的活动叠加层:一条 yolo solutions heatmap 命令或 solutions.Heatmap(...) 加一个读帧循环即可投产。它的工程价值在于三点——跟踪管线内置且跨帧持久、强度累积算法向量化高效、可与 region 计数同趟完成;而源码中固定的 0.5/0.5 混合权重、全局 min–max 归一化、每框 +2 的累积步长等细节,也解释了实际画面中"热点越来越亮、冷区逐渐变淡"的视觉效果。需要进一步探索时,可结合 对象计数指南、其他 Ultralytics 方案 以及 跟踪模式 文档扩展你的视频分析管线。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00