Skip to content

Commit

Permalink
Merge pull request #37456 from alexbruy/gamma-correction
Browse files Browse the repository at this point in the history
Gamma correction filter for raster layers
  • Loading branch information
alexbruy committed Jul 1, 2020
2 parents 059f9fa + 6a6f7ff commit 6068c64
Show file tree
Hide file tree
Showing 25 changed files with 833 additions and 371 deletions.
2 changes: 2 additions & 0 deletions images/images.qrc
Expand Up @@ -274,6 +274,7 @@
<file>themes/default/mActionCustomProjection.svg</file>
<file>themes/default/mActionDecreaseBrightness.svg</file>
<file>themes/default/mActionDecreaseContrast.svg</file>
<file>themes/default/mActionDecreaseGamma.svg</file>
<file>themes/default/mActionDeleteAttribute.svg</file>
<file>themes/default/mActionDeletePart.svg</file>
<file>themes/default/mActionDeleteRing.svg</file>
Expand Down Expand Up @@ -318,6 +319,7 @@
<file>themes/default/mActionIdentify.svg</file>
<file>themes/default/mActionIncreaseBrightness.svg</file>
<file>themes/default/mActionIncreaseContrast.svg</file>
<file>themes/default/mActionIncreaseGamma.svg</file>
<file>themes/default/mActionInOverview.svg</file>
<file>themes/default/mActionInvertSelection.svg</file>
<file>themes/default/mActionKeyboardShortcuts.svg</file>
Expand Down
1 change: 1 addition & 0 deletions images/themes/default/mActionDecreaseGamma.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/themes/default/mActionIncreaseGamma.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -13,7 +13,7 @@
class QgsBrightnessContrastFilter : QgsRasterInterface
{
%Docstring
Brightness/contrast filter pipe for rasters.
Brightness/contrast and gamma correction filter pipe for rasters.
%End

%TypeHeaderCode
Expand All @@ -24,27 +24,94 @@ Brightness/contrast filter pipe for rasters.

virtual QgsBrightnessContrastFilter *clone() const /Factory/;

%Docstring
Clone itself, create deep copy
%End

virtual int bandCount() const;

%Docstring
Gets number of bands
%End

virtual Qgis::DataType dataType( int bandNo ) const;

%Docstring
Returns data type for the band specified by number
%End

virtual bool setInput( QgsRasterInterface *input );

%Docstring
Set input.
Returns ``True`` if set correctly, ``False`` if cannot use that input
%End

virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = 0 ) /Factory/;

%Docstring
Read block of data using given extent and size.
Returns pointer to data.
Caller is responsible to free the memory returned.

:param bandNo: band number
:param extent: extent of block
:param width: pixel width of block
:param height: pixel height of block
:param feedback: optional raster feedback object for cancellation/preview. Added in QGIS 3.0.
%End

void setBrightness( int brightness );
%Docstring
Set brightness level. Acceptable value range is -255…255

.. seealso:: :py:func:`brightness`
%End

int brightness() const;
%Docstring
Returns current brightness level.

.. seealso:: :py:func:`setBrightness`
%End

void setContrast( int contrast );
%Docstring
Set contrast level. Acceptable value range is -100…100

.. seealso:: :py:func:`contrast`
%End

int contrast() const;
%Docstring
Returns current contrast level.

.. seealso:: :py:func:`setContrast`
%End

void setGamma( double gamma );
%Docstring
Set gamma value. Acceptable value range is -0.1…10

.. seealso:: :py:func:`gamma`

.. versionadded:: 3.16
%End

double gamma() const;
%Docstring
Returns current gamma value.

.. seealso:: :py:func:`setGamma`

.. versionadded:: 3.16
%End

virtual void writeXml( QDomDocument &doc, QDomElement &parentElem ) const;

%Docstring
Write base class members to xml.
%End

virtual void readXml( const QDomElement &filterElem );

Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/raster/qgsrasterpipe.sip.in
Expand Up @@ -30,7 +30,7 @@ Base class for processing modules.
ResamplerRole,
ProjectorRole,
NullerRole,
HueSaturationRole
HueSaturationRole,
};

QgsRasterPipe();
Expand Down
52 changes: 52 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -2785,6 +2785,8 @@ void QgisApp::createActions()
connect( mActionDecreaseBrightness, &QAction::triggered, this, &QgisApp::decreaseBrightness );
connect( mActionIncreaseContrast, &QAction::triggered, this, &QgisApp::increaseContrast );
connect( mActionDecreaseContrast, &QAction::triggered, this, &QgisApp::decreaseContrast );
connect( mActionIncreaseGamma, &QAction::triggered, this, &QgisApp::increaseGamma );
connect( mActionDecreaseGamma, &QAction::triggered, this, &QgisApp::decreaseGamma );

#ifdef HAVE_GEOREFERENCER
connect( mActionShowGeoreferencer, &QAction::triggered, this, &QgisApp::showGeoreferencer );
Expand Down Expand Up @@ -3940,6 +3942,8 @@ void QgisApp::setTheme( const QString &themeName )
mActionDecreaseBrightness->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDecreaseBrightness.svg" ) ) );
mActionIncreaseContrast->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIncreaseContrast.svg" ) ) );
mActionDecreaseContrast->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDecreaseContrast.svg" ) ) );
mActionIncreaseGamma->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIncreaseGamma.svg" ) ) );
mActionDecreaseGamma->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDecreaseGamma.svg" ) ) );
mActionZoomActualSize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomNative.png" ) ) );
mActionQgisHomePage->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionQgisHomePage.png" ) ) );
mActionAbout->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionHelpAbout.svg" ) ) );
Expand Down Expand Up @@ -12210,6 +12214,54 @@ void QgisApp::adjustBrightnessContrast( int delta, bool updateBrightness )
}
}

void QgisApp::increaseGamma()
{
double step = 0.1;
if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
{
step = 1.0;
}
adjustGamma( step );
}

void QgisApp::decreaseGamma()
{
double step = -0.1;
if ( QgsApplication::keyboardModifiers() == Qt::ShiftModifier )
{
step = -1.0;
}
adjustGamma( step );
}

void QgisApp::adjustGamma( double delta )
{
const auto constSelectedLayers = mLayerTreeView->selectedLayers();
for ( QgsMapLayer *layer : constSelectedLayers )
{
if ( !layer )
{
visibleMessageBar()->pushMessage( tr( "No Layer Selected" ),
tr( "To change gamma, you need to have a raster layer selected." ),
Qgis::Info, messageTimeout() );
return;
}

QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer );
if ( !rasterLayer )
{
visibleMessageBar()->pushMessage( tr( "No Layer Selected" ),
tr( "To change gamma, you need to have a raster layer selected." ),
Qgis::Info, messageTimeout() );
return;
}

QgsBrightnessContrastFilter *brightnessFilter = rasterLayer->brightnessFilter();
brightnessFilter->setGamma( brightnessFilter->gamma() + delta );

rasterLayer->triggerRepaint();
}
}

void QgisApp::helpContents()
{
Expand Down
24 changes: 23 additions & 1 deletion src/app/qgisapp.h
Expand Up @@ -1297,6 +1297,22 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
* Decrease raster contrast
* Valid for non wms raster layers only. */
void decreaseContrast();

/**
* Increase raster gamma
* Valid for non wms raster layers only.
* \since QGIS 3.16
*/
void increaseGamma();

/**
* Decrease raster gamma
* Valid for non wms raster layers only.
* \since QGIS 3.16
*/
void decreaseGamma();


//! plugin manager
void showPluginManager();
//! load Python support if possible
Expand Down Expand Up @@ -2104,9 +2120,15 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Do histogram stretch for singleband gray / multiband color rasters
void histogramStretch( bool visibleAreaOnly = false, QgsRasterMinMaxOrigin::Limits limits = QgsRasterMinMaxOrigin::MinMax );

//! Apply raster brightness
//! Apply raster brightness/contrast
void adjustBrightnessContrast( int delta, bool updateBrightness = true );

/**
* Apply raster gamma
* \since QGIS 3.16
*/
void adjustGamma( double delta );

//! Copy a vector style from a layer to another one, if they have the same geometry type
void duplicateVectorStyle( QgsVectorLayer *srcLayer, QgsVectorLayer *destLayer );

Expand Down
20 changes: 12 additions & 8 deletions src/core/raster/qgsbrightnesscontrastfilter.cpp
Expand Up @@ -32,6 +32,7 @@ QgsBrightnessContrastFilter *QgsBrightnessContrastFilter::clone() const
QgsBrightnessContrastFilter *filter = new QgsBrightnessContrastFilter( nullptr );
filter->setBrightness( mBrightness );
filter->setContrast( mContrast );
filter->setGamma( mGamma );
return filter;
}

Expand Down Expand Up @@ -122,9 +123,9 @@ QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle co
return outputBlock.release();
}

if ( mBrightness == 0 && mContrast == 0 )
if ( mBrightness == 0 && mContrast == 0 && mGamma == 1.0 )
{
QgsDebugMsgLevel( QStringLiteral( "No brightness changes." ), 4 );
QgsDebugMsgLevel( QStringLiteral( "No brightness/contrast/gamma changes." ), 4 );
return inputBlock.release();
}

Expand All @@ -139,6 +140,7 @@ QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle co

int r, g, b, alpha;
double f = std::pow( ( mContrast + 100 ) / 100.0, 2 );
double gammaCorrection = 1.0 / mGamma;

for ( qgssize i = 0; i < ( qgssize )width * height; i++ )
{
Expand All @@ -151,22 +153,22 @@ QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle co
myColor = inputBlock->color( i );
alpha = qAlpha( myColor );

r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f );
g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f );
b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f );
r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f, gammaCorrection );
g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f, gammaCorrection );
b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f, gammaCorrection );

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

return outputBlock.release();
}

int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor ) const
int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor, double gammaCorrection ) const
{
if ( alpha == 255 )
{
// Opaque pixel, do simpler math
return qBound( 0, ( int )( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
return qBound( 0, ( int )( 255 * std::pow( ( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255.0, gammaCorrection ) ), 255 );
}
else if ( alpha == 0 )
{
Expand All @@ -181,7 +183,7 @@ int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int a
double adjustedColor = colorComponent / alphaFactor;

// Make sure to return a premultiplied color
return alphaFactor * qBound( 0., ( ( ( ( ( ( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255. );
return alphaFactor * qBound( 0., std::pow( ( ( ( ( ( ( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255, gammaCorrection ), 255. );
}
}

Expand All @@ -196,6 +198,7 @@ void QgsBrightnessContrastFilter::writeXml( QDomDocument &doc, QDomElement &pare

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

Expand All @@ -208,4 +211,5 @@ void QgsBrightnessContrastFilter::readXml( const QDomElement &filterElem )

mBrightness = filterElem.attribute( QStringLiteral( "brightness" ), QStringLiteral( "0" ) ).toInt();
mContrast = filterElem.attribute( QStringLiteral( "contrast" ), QStringLiteral( "0" ) ).toInt();
mGamma = filterElem.attribute( QStringLiteral( "gamma" ), QStringLiteral( "1" ) ).toDouble();
}

0 comments on commit 6068c64

Please sign in to comment.