首页
/ Ultralytics YOLO26 Heatmap 方案:视频追踪热力图生成指南(含源码实现剖析)

Ultralytics YOLO26 Heatmap 方案:视频追踪热力图生成指南(含源码实现剖析)

2026-09-06 17:51:56作者:廉皓灿Ida

本文基于 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.yamlbytetrack.yamlocsort.yamldeepocsort.yamlfasttrack.yamltracktrack.yaml,对应 ultralytics/cfg/trackers/ 目录下的 YAML
conf float 0.25 检测置信度阈值;调低可跟踪更多对象但可能引入误检
iou float 0.7 过滤重叠检测框的 IoU 阈值
classes list None 按类别索引过滤,如 classes=[0, 2, 3] 只跟踪指定类别
device str None 推理设备(cpu0cuda: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 时打印逐帧日志(帧号、输入尺寸、各类对象数量、tracksolution 毫秒耗时),结果对象的 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 过滤与 confioutrackerdevicemax_detquantizeimgsz 等参数经 track_add_args 一并下发;
  • 随后把 boxesxyxy 或 OBB 的 xyxyxyxy 角点)、clsstrack_idsconfs 抽取为 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,其实现要点:

  1. 外接轴对齐框get_enclosing_box 把 OBB 角点归一化为 [x0, y0, x1, y1],并裁剪到图像边界内;
  2. 圆形影响域:半径取 min(x1-x0, y1-y0) // 2,即在框的短边一半范围内做圆形衰减区(中心为框中心);
  3. 向量化更新:用 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.applyColorMapcolormap 参数着色 → cv2.addWeighted0.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 tracksolution 两段耗时(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_countout_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 方案 以及 跟踪模式 文档扩展你的视频分析管线。

登录后查看全文
热门项目推荐
相关项目推荐