Skip to content

Commit

Permalink
[FEATURE] brightness and contrast filter for rasters
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Mar 14, 2013
1 parent 1294190 commit 1145bd2
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 8 deletions.
66 changes: 66 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -169,6 +169,7 @@
#include "qgsrasterlayer.h"
#include "qgsrasterlayerproperties.h"
#include "qgsrasternuller.h"
#include "qgsbrightnesscontrastfilter.h"
#include "qgsrasterrenderer.h"
#include "qgsrasterlayersaveasdialog.h"
#include "qgsrectangle.h"
Expand Down Expand Up @@ -1050,6 +1051,10 @@ void QgisApp::createActions()
connect( mActionFullHistogramStretch, SIGNAL( triggered() ), this, SLOT( fullHistogramStretch() ) );
connect( mActionLocalCumulativeCutStretch, SIGNAL( triggered() ), this, SLOT( localCumulativeCutStretch() ) );
connect( mActionFullCumulativeCutStretch, SIGNAL( triggered() ), this, SLOT( fullCumulativeCutStretch() ) );
connect( mActionIncreaseBrightness, SIGNAL( triggered() ), this, SLOT( increaseBrightness() ) );
connect( mActionDecreaseBrightness, SIGNAL( triggered() ), this, SLOT( decreaseBrightness() ) );
connect( mActionIncreaseContrast, SIGNAL( triggered() ), this, SLOT( increaseContrast() ) );
connect( mActionDecreaseContrast, SIGNAL( triggered() ), this, SLOT( decreaseContrast() ) );

// Vector Menu Items
connect( mActionOSMDownload, SIGNAL( triggered() ), this, SLOT( osmDownloadDialog() ) );
Expand Down Expand Up @@ -1675,6 +1680,10 @@ void QgisApp::setTheme( QString theThemeName )
mActionHelpContents->setIcon( QgsApplication::getThemeIcon( "/mActionHelpContents.png" ) );
mActionLocalHistogramStretch->setIcon( QgsApplication::getThemeIcon( "/mActionLocalHistogramStretch.png" ) );
mActionFullHistogramStretch->setIcon( QgsApplication::getThemeIcon( "/mActionFullHistogramStretch.png" ) );
//~ mActionIncreaseBrightness->setIcon( QgsApplication::getThemeIcon( "/mActionIncreaseBrightness.png" ) );
//~ mActionDecreaseBrightness->setIcon( QgsApplication::getThemeIcon( "/mActionDecreaseBrightness.png" ) );
//~ mActionIncreaseContrast->setIcon( QgsApplication::getThemeIcon( "/mActionIncreaseContrast.png" ) );
//~ mActionDecreaseContrast->setIcon( QgsApplication::getThemeIcon( "/mActionDecreaseContrast.png" ) );
mActionZoomActualSize->setIcon( QgsApplication::getThemeIcon( "/mActionZoomNative.png" ) );
mActionQgisHomePage->setIcon( QgsApplication::getThemeIcon( "/mActionQgisHomePage.png" ) );
mActionAbout->setIcon( QgsApplication::getThemeIcon( "/mActionHelpAbout.png" ) );
Expand Down Expand Up @@ -6454,6 +6463,63 @@ void QgisApp::histogramStretch( bool visibleAreaOnly, QgsRasterLayer::ContrastEn
mMapCanvas->refresh();
}

void QgisApp::increaseBrightness()
{
adjustBrightnessContrast( 1 );
}

void QgisApp::decreaseBrightness()
{
adjustBrightnessContrast( -1 );
}

void QgisApp::increaseContrast()
{
adjustBrightnessContrast( 1, false );
}

void QgisApp::decreaseContrast()
{
adjustBrightnessContrast( -1, false );
}


void QgisApp::adjustBrightnessContrast( int delta, bool updateBrightness )
{
QgsMapLayer * myLayer = mMapLegend->currentLayer();

if ( !myLayer )
{
QMessageBox::information( this,

This comment has been minimized.

Copy link
@slarosa

slarosa Mar 14, 2013

Member

shouldn't it be better to get QMessageBar instead of QMessageBox for this sort of warnings ?

Btw, thanks for this new feature!

This comment has been minimized.

Copy link
@alexbruy

alexbruy Mar 15, 2013

Author Contributor

Hmm... maybe you are right and QMessageBar is better solution. But in such case I think we should update all similar warnings to use QMessageBar to make UI consistent

This comment has been minimized.

Copy link
@slarosa

slarosa Mar 15, 2013

Member

Hi,
I started that work in #468 .

tr( "No Layer Selected" ),
tr( "To change brightness or contrast, you need to have a raster layer selected." ) );
return;
}

QgsRasterLayer* myRasterLayer = qobject_cast<QgsRasterLayer *>( myLayer );
if ( !myRasterLayer )
{
QMessageBox::information( this,
tr( "No Raster Layer Selected" ),
tr( "To change brightness or contrast, you need to have a raster layer selected." ) );
return;
}

QgsBrightnessContrastFilter* brightnessFilter = myRasterLayer->brightnessFilter();

if ( updateBrightness )
{
brightnessFilter->setBrightness( brightnessFilter->brightness() + delta );
}
else
{
brightnessFilter->setContrast( brightnessFilter->contrast() + delta );
}

myRasterLayer->setCacheImage( NULL );
mMapCanvas->refresh();
}

void QgisApp::helpContents()
{
openURL( "index.html" );
Expand Down
20 changes: 20 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -670,6 +670,22 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
void localCumulativeCutStretch();
/** Perform a full extent cumulative cut stretch */
void fullCumulativeCutStretch();
/**Increase raster brightness
* Valid for non wms raster layers only.
* @note Added in QGIS 2.0 */
void increaseBrightness();
/**Decrease raster brightness
* Valid for non wms raster layers only.
* @note Added in QGIS 2.0 */
void decreaseBrightness();
/**Increase raster contrast
* Valid for non wms raster layers only.
* @note Added in QGIS 2.0 */
void increaseContrast();
/**Decrease raster contrast
* Valid for non wms raster layers only.
* @note Added in QGIS 2.0 */
void decreaseContrast();
//! plugin manager
void showPluginManager();
//! load python support if possible
Expand Down Expand Up @@ -1143,6 +1159,10 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
/**Do histogram stretch for singleband gray / multiband color rasters*/
void histogramStretch( bool visibleAreaOnly = false, QgsRasterLayer::ContrastEnhancementLimits theLimits = QgsRasterLayer::ContrastEnhancementMinMax );

/**Apply raster brightness
* @note Added in QGIS 2.0 */
void adjustBrightnessContrast( int delta, bool updateBrightness = true );

QgisAppStyleSheet* mStyleSheetBuilder;

// actions for menus and toolbars -----------------
Expand Down
7 changes: 4 additions & 3 deletions src/core/CMakeLists.txt
Expand Up @@ -40,12 +40,12 @@ SET(QGIS_CORE_SRCS
symbology-ng/qgspointdisplacementrenderer.cpp
symbology-ng/qgsvectorfieldsymbollayer.cpp
symbology-ng/qgscolorbrewerpalette.cpp

diagram/qgsdiagram.cpp
diagram/qgspiediagram.cpp
diagram/qgstextdiagram.cpp
diagram/qgshistogramdiagram.cpp

qgis.cpp
qgsapplication.cpp
qgsattributeaction.cpp
Expand Down Expand Up @@ -204,6 +204,7 @@ SET(QGIS_CORE_SRCS
raster/qgssinglebandcolordatarenderer.cpp
raster/qgssinglebandgrayrenderer.cpp
raster/qgssinglebandpseudocolorrenderer.cpp
raster/qgsbrightnesscontrastfilter.cpp

renderer/qgscontinuouscolorrenderer.cpp
renderer/qgsgraduatedsymbolrenderer.cpp
Expand Down Expand Up @@ -446,7 +447,7 @@ SET(QGIS_CORE_HDRS
diagram/qgstextdiagram.h
diagram/qgshistogramdiagram.h


composer/qgslegendmodel.h
composer/qgscomposerlegenditem.h

Expand Down
195 changes: 195 additions & 0 deletions src/core/raster/qgsbrightnesscontrastfilter.cpp
@@ -0,0 +1,195 @@
/***************************************************************************
qgsbrightnesscontrastfilter.cpp
---------------------
begin : February 2013
copyright : (C) 2013 by Alexander Bruy
email : alexander dot bruy at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsrasterdataprovider.h"
#include "qgsbrightnesscontrastfilter.h"

#include <QDomDocument>
#include <QDomElement>


QgsBrightnessContrastFilter::QgsBrightnessContrastFilter( QgsRasterInterface* input )
: QgsRasterInterface( input ),
mBrightness( 0 ),
mContrast( 0 )
{
}

QgsBrightnessContrastFilter::~QgsBrightnessContrastFilter()
{
}

QgsRasterInterface * QgsBrightnessContrastFilter::clone() const
{
QgsDebugMsg( "Entered" );
QgsBrightnessContrastFilter * filter = new QgsBrightnessContrastFilter( 0 );
filter->setBrightness( mBrightness );
return filter;
}

int QgsBrightnessContrastFilter::bandCount() const
{
if ( mOn )
{
return 1;
}

if ( mInput )
{
return mInput->bandCount();
}

return 0;
}

QGis::DataType QgsBrightnessContrastFilter::dataType( int bandNo ) const
{
if ( mOn )
{
return QGis::ARGB32_Premultiplied;
}

if ( mInput )
{
return mInput->dataType( bandNo );
}

return QGis::UnknownDataType;
}

bool QgsBrightnessContrastFilter::setInput( QgsRasterInterface* input )
{
QgsDebugMsg( "Entered" );

// Brightness filter can only work with single band ARGB32_Premultiplied
if ( !input )
{
QgsDebugMsg( "No input" );
return false;
}

if ( !mOn )
{
// In off mode we can connect to anything
QgsDebugMsg( "OK" );
mInput = input;
return true;
}

if ( input->bandCount() < 1 )
{
QgsDebugMsg( "No input band" );
return false;
}

if ( input->dataType( 1 ) != QGis::ARGB32_Premultiplied &&
input->dataType( 1 ) != QGis::ARGB32 )
{
QgsDebugMsg( "Unknown input data type" );
return false;
}

mInput = input;
QgsDebugMsg( "OK" );
return true;
}

QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
Q_UNUSED( bandNo );
QgsDebugMsg( "Entered" );

QgsRasterBlock *outputBlock = new QgsRasterBlock();
if ( !mInput )
{
return outputBlock;
}

// At this moment we know that we read rendered image
int bandNumber = 1;
QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
delete inputBlock;
return outputBlock;
}

if ( mBrightness == 0 && mContrast == 0 )
{
QgsDebugMsg( "No brightness changes." );
delete outputBlock;
return inputBlock;
}

if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) )
{
delete inputBlock;
return outputBlock;
}

// adjust image
QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
QRgb myColor;
int r, g, b;

double f = ( 259 * ( mContrast + 255 ) ) / ( 255 * ( 259 - mContrast ) );

for ( size_t i = 0; i < ( size_t )width*height; i++ )
{
if ( inputBlock->color( i ) == myNoDataColor )
{
outputBlock->setColor( i, myNoDataColor );
continue;
}

myColor = inputBlock->color( i );
r = qBound( 0, qRound( f * ( qBound( 0, qRed( myColor ) + mBrightness, 255 ) - 128 ) + 128 ), 255 );
g = qBound( 0, qRound( f * ( qBound( 0, qGreen( myColor ) + mBrightness, 255 ) - 128 ) + 128 ), 255 );
b = qBound( 0, qRound( f * ( qBound( 0, qBlue( myColor ) + mBrightness, 255 ) - 128 ) + 128 ), 255 );

outputBlock->setColor( i, qRgb( r, g, b ) );
}

delete inputBlock;
return outputBlock;
}

void QgsBrightnessContrastFilter::writeXML( QDomDocument& doc, QDomElement& parentElem )
{
if ( parentElem.isNull() )
{
return;
}

QDomElement filterElem = doc.createElement( "brightnesscontrast" );

filterElem.setAttribute( "brightness", QString::number( mBrightness ) );
filterElem.setAttribute( "contrast", QString::number( mContrast ) );
parentElem.appendChild( filterElem );
}

void QgsBrightnessContrastFilter::readXML( const QDomElement& filterElem )
{
if ( filterElem.isNull() )
{
return;
}

mBrightness = filterElem.attribute( "brightness", "0" ).toInt();
mContrast = filterElem.attribute( "contrast", "0" ).toInt();
}

3 comments on commit 1145bd2

@timlinux
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Alex

I was just playing with this a little - dont you think it would be more useful to have a combo and/or slider widget so that these values can be set precisely? At the moment it requires a lot of clicking - often without any perceptible change in my test image.

Regards

Tim

@alexbruy
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Tim

I already thinking about adding sliders to raster properties dialog to allow more precise way to set this parameters.

@timlinux
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi

I already thinking about adding sliders to raster properties dialog to allow more precise way to set this parameters.

Great. Note I think they should be accompanied by spin boxes to allow precise value entry too.

Please sign in to comment.