【Qt】绘制热度图、频谱图、地形图、colormap
什么热度图啊、频谱图啊,诸如此类的,其本质都是数值与颜色在一幅图上的映射,我们称其为 colormap。
这里为简化描述,颜色统一采用RGBA模式,RGB就是红绿蓝,A代表透明度。
于是乎画出一张colormap,即遍历整幅图,为每个像素点设置一个RGBA值,函数可以表示为:
int colorMatrix[width][height]; void drawColorMap(){ for(int i = 0; i < width; ++i){ for(int j = 0; j < height; ++j){ colormap[i][j] = colorMatrix[i][j]; } } }
最终得到如下这种形式的效果:
首先,我们对QCustomplot所呈现出的图像有个基础的认识,要实现colormap需要用到的地方我在图中标注了下,主要包括。
坐标轴的隐藏(看个人需求) 创建一个QCPColorMap类,用于实现上边所说的 drawColorMap 功能 创建一个图标尺,用来显示颜色的区间上边的三步对应程序:
// configure axis rect: m_customPlot = new QCustomPlot(this); m_customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom); // this will also allow rescaling the color scale by dragging/zooming m_customPlot->axisRect()->setupFullAxesBox(true); //隐藏坐标,上下左右 m_customPlot->xAxis->setVisible(true); m_customPlot->yAxis->setVisible(true); m_customPlot->xAxis2->setVisible(true); m_customPlot->yAxis2->setVisible(true); // set up the QCPColorMap: m_colorMap = new QCPColorMap(m_customPlot->xAxis, m_customPlot->yAxis); // add a color scale: m_colorScale = new QCPColorScale(m_customPlot); m_customPlot->plotLayout()->addElement(0, 1, m_colorScale); // add it to the right of the main axis rect m_colorScale->setType(QCPAxis::atRight); // scale shall be vertical bar with tick/axis labels right (actually atRight is already the default) m_colorMap->setColorScale(m_colorScale); // associate the color map with the color scale // set the color gradient of the color map to one of the presets: m_colorMap->setGradient(QCPColorGradient::gpJet);
这里引入了一个 QCPColorGradient 的概念,我们不难想到这样一个问题:加入值为0时颜色是蓝色,值为10时颜色是红色,那值为5时颜色是什么?这就是 QCPColorGradient 要做的事了。
难道就这么简单?假如我们要绘制的图像可以分为 400 * 300 个网格分布,按常理来说只需要遍历这些网格并设置颜色就行了,但是实际情况往往是我们获取到的数据很少,
比如只有64个位置,如何扩充到 400 * 300 这么大的格子里呢,该如何插值呢?
不难想象,离这些点越近的格子受到这个点影响越大,反之越小。
距离,关键词为距离,令距离为r,则r代表着插值方式是线性的,改为r2则是曲线的,效果更佳平滑,r3, r^4 以此类推
为了简化计算,采用距离的平方作为权重,只需累加已知的点在未知点的权重即可:
for(int i = 0; i < datax; ++i){ for(int j = 0; j < datay; ++j){ m_colorMap->data()->cellToCoord(i, j, &x, &y); //计算权重值 double sum = 0.0; for(int k = 0; k < m_channelAxis.size(); ++k){ //(x, y) 为 (i, j) 在 colormap坐标系下的映射 auto& point = m_channelAxis[k]; double rr = (point.x() - x) * (point.x() - x) + (point.y() - y) * (point.y() - y); sum += 1 / rr; m_channelWeight[i][j][k] = 1 / rr; } for(int k = 0; k < m_channelAxis.size(); ++k){ m_channelWeight[i][j][k] /= sum; } } }
这样我们就把所有网格对应的颜色值计算出来了,时间复杂度 O(width * height * points)。
等等,这个复杂度貌似很大?400 * 300 * 64 = 7,680,000。如果你看过根据数据范围推测算法复杂度这篇文章,
并且在leetcode刷了刷题,就知道700万这个量级是很大的啦,放leetcode跑肯定超时。
因此我们需要需要优化时间复杂度。
时间复杂度优化如果搞过图像处理估计你已经知道要怎么优化了,上面的问题等价为 : 已知一幅小尺寸图,如何放大成大尺寸图。
没错,小尺寸。我们上面的网格400 * 300太大了,如果是算 40 * 30 呢?瞬间计算次数就小了有没有!算完之后我们再给他放大嘛。
这里采用双线性插值的方法:
for (int i = 0; i < datax - 1; i++) { for (int j = 0; j < datay - 1; j++) { double V1 = m_matrix1[i][j]; double V2 = m_matrix1[i + 1][j]; double V3 = m_matrix1[i + 1][j + 1]; double V4 = m_matrix1[i][j + 1]; for (int m = 0; m < ratex; m++) { for (int n = 0; n < ratey; n++) { int x = i * ratex + m, y = j * ratey + n; if(m_inCircle[x][y] == false) continue; m_matrix[x][y] = doubleLinear(m, n, ratex, ratey, V1, V2, V3, V4); m_colorMap->data()->setCell(x, y, m_matrix[x][y]); } } } } double HotPlot::doubleLinear(int m, int n, int X, int Y, double V1, double V2, double V3, double V4) { return (m * n * (V3 - V4 - V2 + V1) + X * n * (V4 - V1) + m * Y * (V2 - V1)) / (X * Y) + V1; }
再来看看现在的计算量 :40 * 30 * 64 + 400 * 300 = 196800,由700万降到了20万!当然,算的少了多多少少会对图像质量有影响,我们适当在复杂度和图像效果上做一些均衡吧。
DemoQColorMapDemo
更新,上述的插值方法为距离反比算法,其实还有很多插值算法,会有更好的效果,我写了个插值算法放在下面了SimpleMultivariateInterpolation
__EOF__
本文作者: miyan 本文链接: https://www.cnblogs.com/miyanyan/p/14701477.html 关于博主: 评论和私信会在第一时间回复。或者直接私信我。 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。
网址:【Qt】绘制热度图、频谱图、地形图、colormap http://c.mxgxt.com/news/view/362724
相关内容
45万粉丝绘制明星地图团队架构图如何绘制的
团队架构图如何绘制
“天河一号”参与绘制“基因图谱” 世界第一个
我国科学家即将绘制完成第一张黄种人基因组图谱
音乐图谱
社交图谱
团队架构图如何绘制模板
知识图谱软件
知识图谱