【问题标题】:QwtPlotSpectrogram with log scales具有对数刻度的 QwtPlotSpectrogram
【发布时间】:2019-12-12 00:04:12
【问题描述】:

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

#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 来达到一半,但它仍然缺少逆变换,因此显示的数据是原始数据的“对数”版本。

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 传入的图像数据转换为使用设置的颜色图的图像并拉伸该图像以适合绘图。

【问题讨论】:

  • 我不明白你想要什么,你可以更详细地解释一下,也许你想要的图像会有所帮助
  • @eyllanesc 我通过组合两张图像来伪造结果。我希望这能更好地解释它。本质上,我想在预先计算的图像旁边放置一个任意比例。
  • 你是否期望相同的“可见行为”在线性和对数尺度上是相同的?
  • 是的,我只是不想改变音阶。我不希望重新调整数据。图像应保持不变(线性)。
  • 你没有错。制作对数刻度是为了平滑斜率,我们使用数字以便您理解:您的数字范围从 1 到 101。该范围内的对数是多少?然后从 0 到 2,因此在该比例下,任何斜率的线都将转换为斜率约为 0 的线,这意味着不区分这些值。如果您的数据呈指数趋势,那么对数刻度会将其更改为线性

标签: c++ qt qwt


【解决方案1】:

我发现完成这项工作的最佳方法是从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 ) );
  }
};

将 main 更改为从 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 );

然后我得到想要的输出

【讨论】:

    【解决方案2】:

    QwtPlotMatrixRasterData 不适用于非线性比例尺!

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

    【讨论】:

    • QwtRasterData 是一个接口。我已经尝试过自己的实现(参见上面的编辑),但并没有达到预期的结果。
    • 请继续使用官方 Qwt 支持渠道之一,可能会发布一个简短且可编译的演示。那我去看看。
    猜你喜欢
    • 2020-07-18
    • 2023-04-02
    • 2011-11-01
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多