【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];
        }
    }
}

最終得到如下這種形式的效果:

實現方法

  • 以Qt為UI框架
  • 采用第三方庫QCustomplot, 因為這個庫使用起來很方便,只需要導入 .h 和 .cpp 文件就行,無需編譯成動態鏈接庫。

首先,我們對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萬!當然,算的少了多多少少會對圖像質量有影響,我們適當在復雜度和圖像效果上做一些均衡吧。

Demo

QColorMapDemo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM