跳转至

光谱图/热力图 - QwtPlotSpectrogram

QwtPlotSpectrogram 用于绘制光谱图(Spectrogram)或热力图,将三维数据显示在二维平面上,其中第三维(强度值)通过颜色来表示。常用于科学数据分析、图像处理与声学分析。

主要功能特性

数值可以映射为颜色显示二维数据场(图像模式),也可以在指定数值处绘制等高线(等高线模式)。颜色映射支持线性、渐变、离散等方式,渲染可分摊到多个 CPU 核心,并提供多种插值算法使显示更平滑。

基本概念

光谱图原理

光谱图将三维数据 (x, y, value) 映射到二维平面:

1
2
3
4
5
6
7
8
9
        Y轴
        │
        │   ┌─────────────────┐
        │   │ 颜色编码强度值  │
        │   │ █░▒▓█░▒▓█░▒▓█ │  ← 每个像素颜色对应一个值
        │   │ ▒▓█░▒▓█░▒▓█░▒ │
        │   │ ▓█░▒▓█░▒▓█░▒▓ │
        │   └─────────────────┘
        └──────────────────────→ X轴

显示模式

模式 枚举值 说明
图像模式 ImageMode 将数值映射为颜色显示
等高线模式 ContourMode 绘制等高线

类继承结构

下面的类图给出几个关键角色及其关系:QwtPlotSpectrogram 继承自栅格基类 QwtPlotRasterItem,从 QwtRasterData 子类获取数值,再经 QwtColorMap 把数值转换为颜色:

classDiagram
    class QwtPlotItem {
        +attach()
        +setRenderHint()
        +setRenderThreadCount()
    }

    class QwtPlotRasterItem {
        +setAlpha()
        +setCachePolicy()
        +setPaintAttribute()
    }

    class QwtPlotSpectrogram {
        +setDisplayMode()
        +setColorMap()
        +setContourLevels()
        +setData()
    }

    class QwtRasterData {
        +value(x, y)
        +interval()
        +contourLines()
    }

    class QwtColorMap {
        +rgb()
        +color()
        +colorTable()
    }

    QwtPlotItem <|-- QwtPlotRasterItem
    QwtPlotRasterItem <|-- QwtPlotSpectrogram
    QwtPlotSpectrogram --> QwtRasterData : 使用
    QwtPlotSpectrogram --> QwtColorMap : 使用

使用方法

光谱图的例子位于:examples/2D/spectrogram,例子截图如下:

Spectrogram

Spectrogram Contour

1. 基本光谱图

基本流程是:创建 QwtPlotSpectrogram,用 setDisplayMode() 打开 ImageMode,指定颜色映射(这里用带若干中间色标的 QwtLinearColorMap),再通过 setData() 传入栅格数据,最后把 item attach 到 plot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <QwtPlot>
#include <QwtPlotSpectrogram>
#include <QwtRasterData>
#include <QwtColorMap>

QwtPlot* plot = new QwtPlot();
plot->setTitle("光谱图示例");
plot->setCanvasBackground(Qt::black);

// 创建光谱图
QwtPlotSpectrogram* spectrogram = new QwtPlotSpectrogram();

// 启用图像显示模式
spectrogram->setDisplayMode(QwtPlotSpectrogram::ImageMode, true);

// 设置颜色映射
QwtLinearColorMap* colorMap = new QwtLinearColorMap(Qt::darkBlue, Qt::yellow);
colorMap->addColorStop(0.2, Qt::blue);
colorMap->addColorStop(0.4, Qt::cyan);
colorMap->addColorStop(0.6, Qt::green);
colorMap->addColorStop(0.8, Qt::orange);
spectrogram->setColorMap(colorMap);

// 创建数据
// 这里使用自定义的QwtRasterData派生类
spectrogram->setData(new MyRasterData());

spectrogram->attach(plot);
plot->replot();

2. 自定义栅格数据

创建 QwtRasterData 派生类提供数据:实现纯虚函数 interval() 与 value() 即可;等高线计算是可选的,基类已内置 CONREC 算法实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <QwtRasterData>
#include <QwtInterval>

#include <cmath>

// 自定义栅格数据类
class MyRasterData : public QwtRasterData
{
public:
    MyRasterData()
    {
        // 可选的性能提示:包围盒内没有NaN空洞
        setAttribute(QwtRasterData::WithoutGaps, true);

        // 设置数据范围
        m_intervals[Qt::XAxis] = QwtInterval(0, 100);
        m_intervals[Qt::YAxis] = QwtInterval(0, 100);
        m_intervals[Qt::ZAxis] = QwtInterval(0, 1);  // 值范围
    }

    // 返回某个轴的包围区间(纯虚函数)
    virtual QwtInterval interval(Qt::Axis axis) const override
    {
        if (axis >= 0 && axis <= 2)
            return m_intervals[axis];

        return QwtInterval();
    }

    // 返回指定位置的值(核心方法,纯虚函数)
    virtual double value(double x, double y) const override
    {
        // 示例:计算位置对应的值
        const double dx = x - 50;
        const double dy = y - 50;
        const double dist = std::sqrt(dx * dx + dy * dy);
        return std::exp(-dist / 20.0);  // 高斯分布
    }

    // 可选:重写等高线计算(ContourLines 即 QMap<double, QPolygonF>),
    // 基类默认运行CONREC算法:
    // virtual ContourLines contourLines(const QRectF& rect, const QSize& raster,
    //     const QList<double>& levels, ConrecFlags flags) const override;

private:
    QwtInterval m_intervals[3];
};

3. 颜色映射配置

线性颜色映射

QwtLinearColorMap 在 addColorStop() 添加的色标之间插值,色标位置取 0.0~1.0 的归一化值;setFormat() 在 QwtColorMap::RGB(平滑过渡)与 QwtColorMap::Indexed(颜色表图像)之间选择:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 创建从蓝到红的线性颜色映射
QwtLinearColorMap* colorMap = new QwtLinearColorMap(Qt::blue, Qt::red);

// 设置格式(RGB或Indexed)
colorMap->setFormat(QwtColorMap::RGB);  // RGB模式,颜色平滑过渡

// 添加颜色停靠点
colorMap->addColorStop(0.0, Qt::black);   // 最小值:黑色
colorMap->addColorStop(0.3, Qt::blue);    // 30%:蓝色
colorMap->addColorStop(0.5, Qt::green);   // 50%:绿色
colorMap->addColorStop(0.7, Qt::yellow);  // 70%:黄色
colorMap->addColorStop(1.0, Qt::white);   // 最大值:白色

spectrogram->setColorMap(colorMap);

预定义颜色映射

除了自己定义色标,Qwt 还提供现成的颜色映射:QwtHueColorMap 遍历 HSV 模型的色相产生彩虹效果,QwtAlphaColorMap 改变固定颜色的透明度,QwtSaturationValueColorMap 则在固定色相下改变饱和度与明度;它们都通过同一个 setColorMap() 调用赋给 spectrogram:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 使用Qwt内置的Hue色图(彩虹色)
QwtHueColorMap* hueMap = new QwtHueColorMap();
hueMap->setHueInterval(0, 359);  // 全色相环(HSV色相:0-359)
hueMap->setSaturation(255);
hueMap->setValue(255);
spectrogram->setColorMap(hueMap);

// 使用Alpha色图(单一颜色的透明度变化)
QwtAlphaColorMap* alphaMap = new QwtAlphaColorMap(Qt::darkBlue);
alphaMap->setAlphaInterval(0, 255);  // 从全透明到不透明
spectrogram->setColorMap(alphaMap);

// 使用饱和度/明度色图(固定色相,变化饱和度与明度)
QwtSaturationValueColorMap* svMap = new QwtSaturationValueColorMap();
svMap->setHue(220);
svMap->setSaturationInterval(100, 255);
svMap->setValueInterval(70, 255);
spectrogram->setColorMap(svMap);

4. 等高线显示

在 ContourMode 下,spectrogram 绘制等值线而非彩色图像;两个显示模式同时打开时则叠加显示:setContourLevels() 指定哪些数值画线,setDefaultContourPen() 为没有专属画笔的级别设置默认画笔:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 启用等高线模式
spectrogram->setDisplayMode(QwtPlotSpectrogram::ContourMode, true);

// 设置等高线级别
QList<double> contourLevels;
contourLevels << 0.1 << 0.2 << 0.3 << 0.4 << 0.5 << 0.6 << 0.7 << 0.8;
spectrogram->setContourLevels(contourLevels);

// 设置等高线样式
spectrogram->setDefaultContourPen(QPen(Qt::white, 1));

// 同时显示图像和等高线
spectrogram->setDisplayMode(QwtPlotSpectrogram::ImageMode, true);
spectrogram->setDisplayMode(QwtPlotSpectrogram::ContourMode, true);

5. 数据分辨率设置

栅格渲染存在质量与速度的权衡。没有显式的"像素大小"设置项,图像通常按绘图设备的分辨率渲染。可控的手段有:QwtRasterData::pixelHint() 让数据给出与数据栅格对齐的像素网格提示(QwtMatrixRasterData 自动实现);PaintInDeviceResolution 绘制属性会把这种对齐数据的图像放大到设备分辨率后再交给 QPainter;RenderAntialiased(继承自 QwtPlotItem)开关抗锯齿;setCachePolicy() 可在多次重绘之间缓存已渲染的图像:

1
2
3
4
5
6
7
8
// 把按pixelHint()对齐数据的图像放大到绘图设备分辨率
spectrogram->setPaintAttribute(QwtPlotRasterItem::PaintInDeviceResolution, true);

// 抗锯齿渲染提示(继承自QwtPlotItem)
spectrogram->setRenderHint(QwtPlotItem::RenderAntialiased, true);

// 缓存已渲染的图像,避免每次重绘都重新计算
spectrogram->setCachePolicy(QwtPlotRasterItem::PaintCache);

6. 多线程渲染

大幅面栅格可以并行渲染:继承自 QwtPlotItem 的 setRenderThreadCount() 把图像生成分摊到指定数量的线程。默认值为 1(单线程),传 0 则由系统选择合适的线程数:

1
2
3
4
5
// 设置渲染线程数(提升大数据渲染性能)
spectrogram->setRenderThreadCount(4);  // 使用4个线程并行渲染

// 默认值为1(单线程);传0则由系统选择理想的线程数
spectrogram->setRenderThreadCount(0);

提示 — 多线程渲染建议

  • 数据量超过100万像素时,建议启用多线程
  • 线程数建议设置为CPU核心数,不要超过8
  • 多线程渲染在图像模式效果最明显

7. 使用矩阵数据

纯矩阵数据无需自己派生 QwtRasterData:QwtMatrixRasterData 用 setValueMatrix() 包装一维 QVector<double>,用 setInterval() 设定 x/y/z 范围,并按 setResampleMode() 的设置(如 BilinearInterpolation)在单元格之间插值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <QwtMatrixRasterData>

// 使用矩阵数据类
QwtMatrixRasterData* matrixData = new QwtMatrixRasterData();

// 设置数据矩阵
QVector<double> values;
for (int i = 0; i < 100 * 100; i++) {
    values << (rand() % 100);
}
matrixData->setValueMatrix(values, 100);  // 100x100矩阵

// 设置数据范围
matrixData->setInterval(Qt::XAxis, QwtInterval(0, 100));
matrixData->setInterval(Qt::YAxis, QwtInterval(0, 100));
matrixData->setInterval(Qt::ZAxis, QwtInterval(0, 100));

// 设置插值方式
matrixData->setResampleMode(QwtMatrixRasterData::BilinearInterpolation);

spectrogram->setData(matrixData);

8. 颜色条(Color Bar)

添加颜色条显示颜色与数值的对应关系(刻度控件会接管颜色映射的所有权,因此要传入新实例,不要复用 spectrogram 已持有的那份):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <QwtScaleWidget>

// 在右侧添加颜色条
QwtScaleWidget* rightAxis = plot->axisWidget(QwtAxis::YRight);
rightAxis->setColorBarEnabled(true);
rightAxis->setColorBarWidth(20);

// 设置颜色条范围和颜色映射
rightAxis->setColorMap(QwtInterval(0, 100), new QwtLinearColorMap(Qt::darkBlue, Qt::yellow));

// 显示右侧坐标轴
plot->setAxisVisible(QwtAxis::YRight, true);

核心方法总结

方法 说明
setData() 设置栅格数据
setColorMap() 设置颜色映射
setDisplayMode() 设置显示模式
setContourLevels() 设置等高线级别
setDefaultContourPen() 设置等高线画笔
setColorTableSize() 设置颜色表条目数(用于 Indexed 格式颜色映射)
setRenderThreadCount() 设置渲染线程数(继承自 QwtPlotItem)
data() 获取栅格数据
colorMap() 获取颜色映射

颜色映射类总结

类名 说明
QwtLinearColorMap 由色标构成的线性颜色映射,颜色平滑过渡
QwtHueColorMap 变化HSV模型的色相,产生彩虹色效果
QwtAlphaColorMap 变化固定颜色的Alpha值(透明度)
QwtSaturationValueColorMap 固定色相,变化HSV模型的饱和度与明度

提示 — 应用场景

  • 科学数据分析:温度场、压力场等
  • 图像处理:热力图、密度图
  • 声学分析:频谱图、声纳图
  • 地理数据:地形图、海深图

示例 — 相关示例

  • 光谱图演示:examples/2D/spectrogram