QGis二次開發基礎 -- 柵格影像增強顯示

張明奇-卡哥發表於2020-10-31

作為一個GIS軟體,對柵格影像的基本支援也是必須的。QGis能夠開啟顯示多種格式的柵格影像,包括常用的JPEG、PNG,以及遙感影像格式ENVI Image、GeoTIF等。今天想來聊一聊簡單的幾個調整柵格影像對比度、亮度以及拉伸增強顯示的基本功能。
這裡寫圖片描述

關於如何新增資料顯示,請參考 柵格向量資料顯示

兩個類

QgsRasterLayer

柵格圖層 QgsRasterLayer 與 QgsVectorLayer 一樣,都是 QgsMapLayer 的子類,是作為地圖空間顯示的圖層物件。這個物件包含了許多柵格圖層的屬性以及顯示方式等方法,今天我們關心的重點在於拉伸顯示、對比度、亮度等調整,因此從API文件中,重點關注幾個地方:

  • setContrastEnhancement 方法
  • 對比度和亮度的控制

setContrastEnhancement 是柵格圖層控制拉伸顯示的方法,它通過特定的拉伸方式、拉伸值域範圍、顯示影像的範圍等引數來增強顯示的樣式。定義如下:
這裡寫圖片描述
其中我們需要特別關注前三個引數。

QgsContrastEnhancement::ContrastEnhancementAlgorithm 為選用的拉伸演算法,具體包括:

  • NoEnhancement –> 無增強
  • StretchToMinimumMaximum –> 最小最大值拉伸
  • StretchAndClipToMinimumMaximum –> 最小最大值拉伸並裁剪值域範圍
  • ClipToMinimumMaximum –> 裁剪值域範圍到最小最大值
  • UserDefinedEnhancement –> 使用者自定義

而 QgsRaster::ContrastEnhancementLimits 為控制增強的值域範圍,包括:

  • ContrastEnhancementNone –> 無增強
  • ContrastEnhancementMinMax –> 最小最大值(預設)
  • ContrastEnhancementStdDev –> 標準差方差範圍
  • ContrastEnhancementCumulativeCut –> 累計裁剪範圍

QgsRectangle 就是選擇要統計值域的範圍了,這個引數可以控制拉伸是用全圖的值域拉伸還是僅僅根據當前顯示範圍的值域進行拉伸。

從QgsRasterLayer 的API文件中,我們看到分別包含一個控制對比度顯示的類 QgsBrightnessContrastFilter 的引用,於是下面我們關注一下這個類。

QgsBrightnessContrastFilter

QGis中用於控制柵格影像對比度和亮度的物件為 QgsBrightnessContrastFilter ,來看看他的基本方法:
這裡寫圖片描述
可以看到,調整柵格圖層顯示亮度的方法為 setBrightness(), 調整顯示對比度的方法為 setContrast()。很明瞭,利用這兩個方法就能控制亮度和對比度了,非常簡單。

兩個方法

QGis當中控制柵格圖層增強的工具欄上大概有8個按鈕,如圖所示:

這裡寫圖片描述
前面4個控制拉伸顯示,後面4個分別是亮度和對比度的增加、減少。

我們剛才講了,拉伸顯示具有好幾種方式,為了能夠重用程式碼,QGis將工具欄中這幾個按鈕的事件進行了整合,定義了兩個基本函式:

  • histogramStretch
  • adjustBrightnessContrast

呼叫這兩個函式,但傳遞不同引數來控制不同的顯示方式。下面給出這兩個函式的實現程式碼並做相應註釋說明:

void qgis_dev::histogramStretch( bool visibleAreaOnly /*= false*/, QgsRaster::ContrastEnhancementLimits theLimits /*= QgsRaster::ContrastEnhancementMinMax */ )
{
    QgsMapLayer* myLayer = m_layerTreeView->currentLayer();
    if ( !myLayer ) // 判斷是否為地圖圖層
    {
        return;
    }

    QgsRasterLayer* myRasterLayer = qobject_cast<QgsRasterLayer*>( myLayer );
    if ( !myRasterLayer ) // 判斷是否為柵格圖層
    {
        return;
    }

    QgsRectangle myRectangle;
    if ( visibleAreaOnly ) // 這裡控制是否僅用當前可見範圍的值域進行拉伸
    {
        myRectangle = m_mapCanvas->mapSettings().outputExtentToLayerExtent( myRasterLayer, m_mapCanvas->extent() );
    }
    // 這一句是關鍵
    myRasterLayer->setContrastEnhancement( QgsContrastEnhancement::StretchToMinimumMaximum, theLimits, myRectangle );
    m_mapCanvas->refresh();
}
void qgis_dev::adjustBrightnessContrast( int delta, bool updateBrightness /*= true */ )
{
    foreach( QgsMapLayer* layer, m_layerTreeView->selectedLayers() ) // 遍歷所有選擇的圖層
    {
        if ( !layer )// 判斷是否為地圖圖層
        {
            return;
        }

        QgsRasterLayer* rasterLayer = qobject_cast<QgsRasterLayer *>( layer );
        if ( !rasterLayer )// 判斷是否為柵格圖層
        {
            return;
        }

        // 這裡是關鍵,用QgsBrightnessFilter類控制亮度與對比度
        QgsBrightnessContrastFilter* brightnessFilter = rasterLayer->brightnessFilter();
        if ( updateBrightness )
        {
            brightnessFilter->setBrightness( brightnessFilter->brightness() + delta );
        }
        else
        {
            brightnessFilter->setContrast( brightnessFilter->contrast() + delta );
        }

        rasterLayer->triggerRepaint(); // 重畫柵格圖層
    }
}

封裝呼叫

我想呼叫起來就已經很簡單了,不過是不同引數的組合而已。這裡就以QGis控制柵格圖層增強顯示的這幾個工具為例,給出它們的實現程式碼。

/// 顯示柵格影像的一些功能定義
    //! 區域性拉伸顯示
    void localHistogramStretch();
    //! 全域性拉伸顯示
    void fullHistogramStretch();
    //! 區域性累計裁剪拉伸顯示
    void localCumulativeCutStretch();
    //! 全域性累計裁剪拉伸顯示
    void fullCumulativeCutStretch();
    //! 增加顯示亮度
    void increaseBrightness();
    //! 減少顯示亮度
    void decreaseBrightness();
    //! 增加顯示對比度
    void increaseContrast();
    //! 減少顯示對比度
    void decreaseContrast();
// 以下是實現
void qgis_dev::localHistogramStretch()
{
    histogramStretch( true, QgsRaster::ContrastEnhancementMinMax );
}

void qgis_dev::fullHistogramStretch()
{
    histogramStretch( false, QgsRaster::ContrastEnhancementMinMax );
}

void qgis_dev::localCumulativeCutStretch()
{
    histogramStretch( true, QgsRaster::ContrastEnhancementCumulativeCut );
}

void qgis_dev::fullCumulativeCutStretch()
{
    histogramStretch( false, QgsRaster::ContrastEnhancementCumulativeCut );
}

void qgis_dev::increaseBrightness()
{
    int step = 1;
    if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
    {
        step = 10;
    }
    adjustBrightnessContrast( step );
}

void qgis_dev::decreaseBrightness()
{
    int step = -1;
    if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
    {
        step = -10;
    }
    adjustBrightnessContrast( step );
}

void qgis_dev::increaseContrast()
{
    int step = 1;
    if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
    {
        step = 10;
    }
    adjustBrightnessContrast( step, false );
}

void qgis_dev::decreaseContrast()
{
    int step = -1;
    if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
    {
        step = -10;
    }
    adjustBrightnessContrast( step, false );
}

最後,有關QGis二次開發這個系列部落格的內容,我整合到了一個工程中並放在了GitHub上,地址是 https://github.com/Jacory/qgis_dev, 需要參考的同學可以直接去clone或者fork程式碼。

謝謝閱讀。

相關文章