带对数刻度的 QwtPlotSpectrogram

QwtPlotSpectrogram with log scales

我想在频谱图旁边加上对数刻度。我希望显示的图像与线性数据相同。线性刻度版本的代码如下所示:

#include <QApplication>
#include <QMainWindow>

#include <qwt_plot.h>
#include <qwt_plot_spectrogram.h>
#include <qwt_matrix_raster_data.h>
#include <qwt_color_map.h>
#include <qwt_scale_engine.h>

int main( int argc, char* argv[] ) {
  QApplication app( argc, argv );
  QMainWindow wnd;

  QVector<double> heat_values( 100 * 100 );
  for( int n = 0; n < 100 * 100; ++n ) {
    heat_values[n] = ( n % 100 ) + n / 100;
  };

  QwtPlotSpectrogram heat;
  auto heat_data = std::make_unique<QwtMatrixRasterData>();
  heat_data->setValueMatrix( heat_values, 100 );
  heat_data->setResampleMode(
      QwtMatrixRasterData::ResampleMode::NearestNeighbour );
  heat_data->setInterval( Qt::XAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::YAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::ZAxis, QwtInterval( 0, 200.0 ) );

  heat.setDisplayMode( QwtPlotSpectrogram::DisplayMode::ImageMode, true );
  heat.setColorMap( new QwtLinearColorMap( Qt::white, Qt::black ) );
  heat.setData( heat_data.release() );

  QwtPlot p;
  p.setAutoDelete( false );
  heat.attach( &p );
  p.repaint();

  wnd.setCentralWidget( &p );
  wnd.resize( 400, 300 );
  wnd.show();

  return QApplication::exec();
}

并产生预期的结果。

但是,我想要相同的图像但比例不同,例如从 1 到 101 的对数比例。但是在我这样更改比例之后:

  p.setAxisScaleEngine( QwtPlot::yLeft, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::yLeft, 1.0, 101.0 );
  p.setAxisScaleEngine( QwtPlot::xBottom, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::xBottom, 1.0, 101.0 );

那么频谱图就乱七八糟了。

有谁知道如何改变显示比例?

msvc 2017,x64,qwt 6.1.4,qt 5.12.2

编辑:

我可以通过定义自己的 RasterData 并将坐标映射回 bin 来完成一半的工作,但它仍然缺少逆变换,因此显示的数据是原始数据的 'log' 版本。

class RasterData : public QwtRasterData
{
public:
  double value( double const x, double const y ) const override {  
    int const ix = std::min<int>( std::max<int>( 0, x ), m_cols-1 );
    int const iy = std::min<int>( std::max<int>( 0, y ), m_cols-1 );
    return m_values[iy * m_cols + ix];
  }

  void setValueMatrix( QVector<double> const& values, int const cols ) {
    m_values = values;
    m_cols = cols;
  }

private: 
  QVector<double> m_values;
  int m_cols;
};

然后结果看起来像这样:

但本质上我想避免所有这些转换。我希望它使用设置的颜色映射将通过 setValueMatrix 传入的图像数据转换为图像,并拉伸该图像以适合绘图。

我发现完成这项工作的最佳方法是从 QwtPlotSpectrogram 派生并将转换更改为线性以调用 draw.

class PlotSpectrogram : public QwtPlotSpectrogram {
public:
  void draw(
      QPainter* painter,
      QwtScaleMap const& xMap,
      QwtScaleMap const & yMap,
      QRectF const& canvasRect ) const override {

    QwtScaleMap xMapLin( xMap );
    QwtScaleMap yMapLin( yMap );

    auto const xi = data()->interval( Qt::XAxis );
    auto const yi = data()->interval( Qt::YAxis );

    auto const dx = xMapLin.transform( xMap.s1() );
    xMapLin.setScaleInterval( xi.minValue(), xi.maxValue() );
    auto const dy = yMapLin.transform( yMap.s2() );
    yMapLin.setScaleInterval( yi.minValue(), yi.maxValue() );

    xMapLin.setTransformation( new QwtNullTransform() );
    yMapLin.setTransformation( new QwtNullTransform() );

    QwtPlotSpectrogram::draw(
        painter, xMapLin, yMapLin, canvasRect.translated( dx, -dy ) );
  }
};

主要更改为 20..50 的比例对数比例并使用 PlotSpectrogram

  PlotSpectrogram heat;
  auto heat_data = std::make_unique<QwtMatrixRasterData>();
  heat_data->setValueMatrix( heat_values, 100 );
  heat_data->setInterval( Qt::XAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::YAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::ZAxis, QwtInterval( 0, 200.0 ) );

  heat.setDisplayMode( QwtPlotSpectrogram::DisplayMode::ImageMode, true );
  heat.setColorMap( new QwtLinearColorMap( Qt::white, Qt::black ) );
  heat.setData( heat_data.release() );

  QwtPlot p;

  p.setAxisScaleEngine( QwtPlot::yLeft, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::yLeft, 20.0, 50.0 );
  p.setAxisScaleEngine( QwtPlot::xBottom, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::xBottom, 20.0, 50.0 );

  p.setAutoDelete( false );
  heat.attach( &p );

然后我得到了想要的输出

QwtPlotMatrixRasterData 不适用于非线性比例!

当使用 QwtRasterData 时,任何类型的刻度都可以开箱即用。