mmcv 可视化 API 完全指南:图像、目标检测框与光流可视化实战
mmcv 可视化 API 完全指南:图像、目标检测框与光流可视化实战
mmcv.visualization 是 OpenMMLab 计算机视觉基础库中负责结果可视化的核心模块,提供从颜色管理、图像与检测框绘制到光流可视化的完整工具链。本指南将围绕 docs/en/api/visualization.rst 中列出的全部 8 个公开接口,结合 mmcv/visualization 下的源码实现与测试用例,逐一讲解其功能、参数语义、调用方式与底层原理,帮助读者快速掌握在 MMDetection、MMSegmentation 等上游算法库中通用的可视化能力。
模块总览:Color、Image 与 Optical Flow 三大子模块
mmcv.visualization 包在 mmcv/visualization/init.py 中统一导出 8 个公开 API,按功能划分为三个子模块:
| 子模块 | 导出函数 | 核心用途 |
|---|---|---|
| color.py | Color、color_val |
颜色定义与统一格式转换 |
| image.py | imshow、imshow_bboxes、imshow_det_bboxes |
图像显示、通用框绘制、检测框+标签绘制 |
| optflow.py | flow2rgb、flowshow、make_color_wheel |
光流场转伪彩图、光流可视化、颜色轮构建 |
从源码结构看,三个子模块之间存在清晰的依赖链条:image.py 依赖 color.py 完成颜色归一化,optflow.py 依赖 image.py 的 imshow 完成最终显示,同时依赖 mmcv/image 的 rgb2bgr 和 mmcv/video 的 flowread 完成数据预处理。理解这条依赖链,有助于掌握每个 API 的输入输出约定。
Color:统一颜色表示与校验
Color 枚举:8 种预定义 BGR 颜色
color.py 定义了 Color 枚举类,包含 red、green、blue、cyan、yellow、magenta、white、black 共 8 种常见颜色。需要注意的是,枚举值采用的是 BGR 通道顺序而非 RGB:
class Color(Enum):
red = (0, 0, 255)
green = (0, 255, 0)
blue = (255, 0, 0)
...
这与 OpenCV 的图像存储约定一致——mmcv 内部图像数组统一为 BGR 格式,因此 Color.red = (0, 0, 255) 恰好表示"蓝色通道为 255",在 BGR 语义下即红色。使用枚举成员时可以直接传给后续绘图函数,避免硬编码魔法数字。
color_val:5 种输入类型的统一转换
color_val(color.py)是整个可视化模块的颜色入口,它把各种形态的颜色输入统一转换为 (B, G, R) 三整数元组,支持以下类型:
| 输入类型 | 转换规则 | 校验条件 |
|---|---|---|
str |
按名称索引 Color 枚举,如 'green' → (0, 255, 0) |
名称必须是枚举成员 |
Color 枚举 |
直接返回 color.value |
— |
tuple |
原样返回 | 长度必须为 3,且每个通道在 [0, 255] |
int |
展开为灰度三元组 (c, c, c) |
数值在 [0, 255] |
np.ndarray |
先断言形状 (3,) 且值域合法,再转 uint8 并转 tuple |
ndim == 1 且 size == 3 |
对于其他类型(如 list、float),函数直接抛出 TypeError。这一点在 tests/test_visualization.py 中有明确验证:
assert mmcv.color_val(mmcv.Color.blue) == (255, 0, 0)
assert mmcv.color_val('green') == (0, 255, 0)
assert mmcv.color_val((1, 2, 3)) == (1, 2, 3)
assert mmcv.color_val(100) == (100, 100, 100)
assert mmcv.color_val(np.zeros(3, dtype=int)) == (0, 0, 0)
with pytest.raises(TypeError):
mmcv.color_val([255, 255, 255]) # list 不支持
with pytest.raises(AssertionError):
mmcv.color_val((0, 0, 500)) # 通道越界
正因为 color_val 收敛了所有颜色类型,后续 imshow_bboxes、imshow_det_bboxes 的颜色参数才都能以 Union[Color, str, tuple, int, np.ndarray] 这一"宽进"方式传入。
Image:图像显示与检测框绘制
imshow:窗口显示的基础设施
imshow(image.py)是图像显示的最底层封装,签名如下:
def imshow(img, win_name='', wait_time=0):
它接受图像路径或 numpy 数组,内部先调用 mmcv.imread 读取(见 mmcv/image/io.py,支持本地路径、s3://、http:// 等各类文件后端),再交给 cv2.imshow。wait_time 的语义值得注意:
wait_time == 0:进入while True循环,每毫秒调用一次cv2.waitKey(1),直到用户关闭窗口(通过cv2.WND_PROP_VISIBLE检测)或按下任意键才退出,避免窗口关闭后程序挂死;wait_time > 0:直接执行cv2.waitKey(wait_time),即等待指定毫秒数后自动关闭。
这一设计让 imshow 既可用于交互式调试(wait_time=0),也可用于批量生成演示(设定固定时长自动播放)。
imshow_bboxes:通用边界框绘制
imshow_bboxes(image.py)在图像上绘制一组或多组边界框,适合在尚未分类或仅有框坐标的场合使用。其参数说明如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
img |
str / ndarray | 必填 | 图像路径或数组 |
bboxes |
list / ndarray | 必填 | 形状 (k, 4) 的框数组,或由多个该形状数组组成的 list |
colors |
ColorType / list | 'green' |
单个颜色,或与 bboxes 等长的颜色列表 |
top_k |
int | -1 |
为正数时只绘制每组前 k 个框 |
thickness |
int | 1 |
线条粗细 |
show |
bool | True |
是否弹出窗口显示 |
win_name |
str | '' |
窗口名 |
wait_time |
int | 0 |
waitKey 参数,语义同 imshow |
out_file |
str | None |
非空时把结果写入该文件 |
实现要点:当 bboxes 是单个 ndarray 时会被包装成 [bboxes];若 colors 不是 list 则复制为与框组数等长;随后对每个 (k, 4) 框组逐框调用 cv2.rectangle 绘制左上角 (x1, y1) 到右下角 (x2, y2) 的矩形。top_k 通过 min(top_k, _bboxes.shape[0]) 截断,保证不会越界。函数最后会返回绘制完成的图像数组,方便继续处理或保存。
imshow_det_bboxes:检测结果一键可视化
imshow_det_bboxes(image.py)是目标检测场景下最常用的可视化入口,直接在图像上同时绘制边界框、类别标签和置信度分数,对应 MMDetection 训练/推理脚本中的可视化需求:
def imshow_det_bboxes(img, bboxes, labels, class_names=None,
score_thr=0, bbox_color='green', text_color='green',
thickness=1, font_scale=0.5, show=True,
win_name='', wait_time=0, out_file=None):
各参数语义如下:
bboxes:ndarray,形状必须为(n, 4)(无分数)或(n, 5)(最后一列为分数);labels:ndarray,与 bboxes 等长的一维标签数组;class_names:list[str],标签索引到类别名的映射;为None时显示cls {label}占位文本;score_thr:float,大于 0 时要求bboxes为(n, 5),并过滤掉分数不达标的框与对应标签;bbox_color/text_color:分别控制框线颜色与文字颜色,均经color_val归一化;font_scale:文字缩放系数,默认 0.5,配合cv2.FONT_HERSHEY_COMPLEX字体使用。
函数开头有 4 条断言保证输入合法:bboxes.ndim == 2、labels.ndim == 1、两者长度一致、bboxes.shape<a href="https://link.gitcode.com/i/106bb95e11263edc212afb45758f71f5" target="_blank">1] 为 4 或 5。绘制时每个框先转为 int32,在左上角上方 2 像素处用 putText 写出文本;若框带分数(len(bbox) > 4),文本会追加 |{score:.02f} 保留两位小数。整体流程与 imshow_bboxes 一致:show=True 时弹窗,out_file 非空时经 mmcv.imwrite 落盘(见 [mmcv/image/io.py,会根据扩展名自动选择编码格式并支持各类文件后端)。
Optical Flow:光流场的伪彩色可视化
make_color_wheel:Middlebury 风格颜色轮
make_color_wheel(optflow.py)按 Middlebury 光流基准的经典方案构建颜色轮:把色相环划分为红→黄、黄→绿、绿→青、青→蓝、蓝→品红、品红→红六个区间,默认每个区间的 bin 数为 [15, 6, 4, 11, 13, 6],总 bin 数为 55。
def make_color_wheel(bins=None): # bins=None 时使用 [15, 6, 4, 11, 13, 6]
六个区间各自按等差渐变填充 RGB 三分量,最终返回形状为 (total_bins, 3) 的 float32 数组。传入自定义 bins 时必须为长度 6 的 list/tuple,否则触发断言 len(bins) == 6。测试用例 tests/test_video/test_optflow.py 验证了默认颜色轮与自定义 [2, 2, 2, 2, 2, 2] 两种场景均能正常构建。
flow2rgb:光流场到 RGB 伪彩图的映射
flow2rgb(optflow.py)是光流可视化的核心转换函数:
def flow2rgb(flow, color_wheel=None, unknown_thr=1e6):
其输入 flow 必须为 (h, w, 2) 的数组,两个通道分别代表水平位移 dx 与垂直位移 dy。转换算法步骤如下:
- 未知点处理:
dx/dy为 NaN 或绝对值超过unknown_thr(默认1e6)的位置记为ignore_inds,先清零,最后这些像素在输出图中置黑; - 全局归一化:计算模长
rad = sqrt(dx² + dy²),若最大模长非零则整体除以max_rad,把位移归一化到单位圆内; - 角度→颜色轮索引:由
angle = arctan2(-dy, -dx) / π得到归一化角度,映射到颜色轮区间[0, num_bins-1],在相邻两个 bin 之间做线性插值(权重w)得到基础颜色; - 模长调制:模长 ≤ 1 的像素向白色方向混合(
1 - rad * (1 - color)),模长 > 1 的像素整体乘 0.75 压暗,从而用亮度区分位移大小; - 未知像素置黑:
flow_img[ignore_inds, :] = 0。
测试 tests/test_video/test_optflow.py 对含 [0, 0]、[0.5, 0.5]、[1, 1]、[2, 1] 及 inf(未知点)的流场做了数值级验证,确认输出与预期 RGB 值逐像素一致,未知点输出全黑。默认情况下 color_wheel=None 会自动调用 make_color_wheel(),也可传入自定义颜色轮,但要求其 ndim == 2 且第二维为 3。
flowshow:一键显示光流结果
flowshow(optflow.py)把"读流→转伪彩→显示"串成一条调用链:
def flowshow(flow, win_name='', wait_time=0):
flow = flowread(flow) # 读取 .flo 文件或校验 ndarray
flow_img = flow2rgb(flow) # 转为 RGB 伪彩图
imshow(rgb2bgr(flow_img), ...) # BGR 转换后弹窗显示
其中 flowread(见 mmcv/video/optflow.py)对路径输入会校验文件头是否为 PIEH,再按 (w, h, 2) 读取 float32 数据;对 ndarray 输入则校验 ndim == 3 且最后一维为 2。由于 flow2rgb 输出的是 RGB 顺序,显示前必须经 mmcv.image.rgb2bgr 反转为 BGR,flowshow 内部已自动完成这一步,使用者无需关心。
实战:检测结果与光流可视化的完整调用链
下面给出两个可直接运行的实战示例,覆盖 imshow_det_bboxes 与 flowshow 两条完整链路。示例假定环境已安装 mmcv 及 opencv-python、numpy。
示例 1:目标检测结果可视化
import mmcv
import numpy as np
# 读取图像(支持本地路径、s3、http 等)
img = mmcv.imread('demo.jpg') # 得到 BGR ndarray
# 模拟一组检测结果:每个框 (x1, y1, x2, y2, score)
bboxes = np.array([[10, 20, 120, 260, 0.96],
[200, 50, 320, 280, 0.87]], dtype=np.float32)
labels = np.array([0, 1])
class_names = ['cat', 'dog']
# 只显示分数 > 0.9 的框,结果同时弹窗与保存
result = mmcv.imshow_det_bboxes(
img,
bboxes,
labels,
class_names=class_names,
score_thr=0.9,
bbox_color='green',
text_color='green',
thickness=2,
font_scale=0.6,
win_name='det_result',
wait_time=0, # 按任意键或关闭窗口退出
out_file='result.jpg' # 自动按扩展名编码保存
)
说明:由于 score_thr=0.9,只有分数 0.96 的"cat"框会保留,并在框上标注 cat|0.96;若不传 class_names,则标注为 cls 0|0.96。若只想画框不要标签,改用 imshow_bboxes(img, [bboxes[:, :4]], colors='green') 即可。
示例 2:光流文件可视化
import mmcv
import numpy as np
# 方式一:直接可视化 .flo 光流文件(flowshow 内部完成读流与伪彩转换)
mmcv.flowshow('flow.flo', win_name='optical_flow', wait_time=0)
# 方式二:先转伪彩图再自行保存
flow = np.random.uniform(-1, 1, size=(240, 320, 2)).astype(np.float32)
flow_img = mmcv.flow2rgb(flow) # 输出 RGB 伪彩图,形状 (240, 320, 3)
bgr_img = mmcv.rgb2bgr(flow_img) # 转 BGR 后符合 imwrite 约定
mmcv.imwrite(bgr_img, 'flow_viz.jpg')
方式二展示了 flow2rgb 与 imwrite 的组合用法:由于 mmcv 图像接口统一约定 BGR,伪彩图在落盘前需显式调用 rgb2bgr。若要复现 Middlebury 的默认配色,直接用 mmcv.flow2rgb(flow) 即可(内部自动构建默认颜色轮)。
源码路径索引与延伸阅读
本模块涉及的关键实现与验证文件汇总如下,方便读者深入源码:
- 模块导出与依赖关系:mmcv/visualization/init.py
- 颜色枚举与转换:mmcv/visualization/color.py
- 图像显示与框绘制:mmcv/visualization/image.py
- 光流可视化:mmcv/visualization/optflow.py
- 底层 I/O 依赖:图像读写 mmcv/image/io.py、光流读写 mmcv/video/optflow.py
- 测试用例:颜色转换 tests/test_visualization.py、光流转换 tests/test_video/test_optflow.py
更完整的中英文 API 索引可参见 docs/zh_cn/api/visualization.rst 与 docs/en/api/visualization.rst。在基于 mmcv 构建的上游算法库中,这套可视化 API 承担了训练过程结果抽查、论文图表生成、demo 推理演示等常见任务,掌握其参数语义后可在各类项目中直接复用。