QGIS API Documentation  2.99.0-Master (e077efd)
qgscontrastenhancement.cpp
Go to the documentation of this file.
1 /* **************************************************************************
2  qgscontrastenhancement.cpp - description
3  -------------------
4 begin : Mon Oct 22 2007
5 copyright : (C) 2007 by Peter J. Ersts
6 email : [email protected]
7 
8 This class contains code that was originally part of the larger QgsRasterLayer
9 class originally created circa 2004 by T.Sutton, Gary E.Sherman, Steve Halasz
10 ****************************************************************************/
11 
12 /* **************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 #include "qgslogger.h"
22 
23 #include "qgscontrastenhancement.h"
28 #include "qgsrasterblock.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31 
33  : mContrastEnhancementAlgorithm( NoEnhancement )
34  , mContrastEnhancementFunction( nullptr )
35  , mEnhancementDirty( false )
36  , mLookupTable( nullptr )
37  , mRasterDataType( theDataType )
38 {
39  mMinimumValue = minimumValuePossible( mRasterDataType );
40  mMaximumValue = maximumValuePossible( mRasterDataType );
41  mRasterDataTypeRange = mMaximumValue - mMinimumValue;
42 
43  mLookupTableOffset = mMinimumValue * -1;
44 
45  mContrastEnhancementFunction = new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue );
46 
47  //If the data type is larger than 16-bit do not generate a lookup table
48  if ( mRasterDataTypeRange <= 65535.0 )
49  {
50  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange+1 )];
51  }
52 
53 }
54 
56  : mContrastEnhancementFunction( nullptr )
57  , mEnhancementDirty( true )
58  , mLookupTable( nullptr )
59  , mMinimumValue( ce.mMinimumValue )
60  , mMaximumValue( ce.mMaximumValue )
61  , mRasterDataType( ce.mRasterDataType )
62  , mRasterDataTypeRange( ce.mRasterDataTypeRange )
63 {
64  mLookupTableOffset = minimumValuePossible( mRasterDataType ) * -1;
65 
66  // setContrastEnhancementAlgorithm sets also QgsContrastEnhancementFunction
67  setContrastEnhancementAlgorithm( ce.mContrastEnhancementAlgorithm, false );
68 
69  //If the data type is larger than 16-bit do not generate a lookup table
70  if ( mRasterDataTypeRange <= 65535.0 )
71  {
72  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange+1 )];
73  }
74 }
75 
77 {
78  delete [] mLookupTable;
79  delete mContrastEnhancementFunction;
80 }
81 /*
82  *
83  * Static methods
84  *
85  */
86 
91 {
92  switch ( theDataType )
93  {
94  case Qgis::Byte:
96  case Qgis::UInt16:
98  case Qgis::Int16:
100  case Qgis::UInt32:
102  case Qgis::Int32:
104  case Qgis::Float32:
106  case Qgis::Float64:
108  case Qgis::CInt16:
110  case Qgis::CInt32:
112  case Qgis::CFloat32:
114  case Qgis::CFloat64:
116  case Qgis::ARGB32:
119  // XXX - mloskot: not handled?
120  break;
121  }
122 
124 }
125 
130 {
131  switch ( theDataType )
132  {
133  case Qgis::Byte:
135  case Qgis::UInt16:
137  case Qgis::Int16:
139  case Qgis::UInt32:
141  case Qgis::Int32:
143  case Qgis::Float32:
144  return std::numeric_limits<float>::max() * -1.0;
145  case Qgis::Float64:
146  return std::numeric_limits<double>::max() * -1.0;
147  case Qgis::CInt16:
149  case Qgis::CInt32:
151  case Qgis::CFloat32:
152  return std::numeric_limits<float>::max() * -1.0;
153  case Qgis::CFloat64:
154  return std::numeric_limits<double>::max() * -1.0;
155  case Qgis::ARGB32:
158  // XXX - mloskot: not handled?
159  break;
160  }
161 
162  return std::numeric_limits<double>::max() * -1.0;
163 }
164 
165 /*
166  *
167  * Non-Static methods
168  *
169  */
170 
177 {
178  if ( mEnhancementDirty )
179  {
180  generateLookupTable();
181  }
182 
183  if ( mLookupTable && NoEnhancement != mContrastEnhancementAlgorithm )
184  {
185  return mLookupTable[static_cast <int>( theValue + mLookupTableOffset )];
186  }
187  else
188  {
189  // Even if the contrast enhancement algorithms is set to NoEnhancement
190  // The input values will still have to be scaled for all data types
191  // greater than 1 byte.
192  return mContrastEnhancementFunction->enhance( theValue );
193  }
194 }
195 
199 bool QgsContrastEnhancement::generateLookupTable()
200 {
201  mEnhancementDirty = false;
202 
203  if ( !mContrastEnhancementFunction )
204  return false;
205  if ( NoEnhancement == mContrastEnhancementAlgorithm )
206  return false;
207  if ( Qgis::Byte != mRasterDataType && Qgis::UInt16 != mRasterDataType && Qgis::Int16 != mRasterDataType )
208  return false;
209  if ( !mLookupTable )
210  return false;
211 
212  QgsDebugMsg( "building lookup table" );
213  QgsDebugMsg( "***MinimumValue : " + QString::number( mMinimumValue ) );
214  QgsDebugMsg( "***MaximumValue : " + QString::number( mMaximumValue ) );
215  QgsDebugMsg( "***mLookupTableOffset : " + QString::number( mLookupTableOffset ) );
216  QgsDebugMsg( "***mRasterDataTypeRange : " + QString::number( mRasterDataTypeRange ) );
217 
218  for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ )
219  {
220  mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset );
221  }
222 
223  return true;
224 }
225 
232 {
233  if ( mContrastEnhancementFunction )
234  {
235  return mContrastEnhancementFunction->isValueInDisplayableRange( theValue );
236  }
237 
238  return false;
239 }
240 
248 {
249  switch ( theAlgorithm )
250  {
252  delete mContrastEnhancementFunction;
253  mContrastEnhancementFunction = new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue );
254  break;
256  delete mContrastEnhancementFunction;
257  mContrastEnhancementFunction = new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue );
258  break;
259  case ClipToMinimumMaximum :
260  delete mContrastEnhancementFunction;
261  mContrastEnhancementFunction = new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue );
262  break;
264  //Do nothing
265  break;
266  default:
267  delete mContrastEnhancementFunction;
268  mContrastEnhancementFunction = new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue );
269  break;
270  }
271 
272  mEnhancementDirty = true;
273  mContrastEnhancementAlgorithm = theAlgorithm;
274 
275  if ( generateTable )
276  {
277  generateLookupTable();
278  }
279 }
280 
287 {
288  QgsDebugMsgLevel( "called", 4 );
289 
290  if ( theFunction )
291  {
292  delete mContrastEnhancementFunction;
293  mContrastEnhancementFunction = theFunction;
294  mContrastEnhancementAlgorithm = UserDefinedEnhancement;
295  generateLookupTable();
296  }
297 }
298 
305 void QgsContrastEnhancement::setMaximumValue( double theValue, bool generateTable )
306 {
307  QgsDebugMsgLevel( "called value: " + QString::number( theValue ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
308 
309  if ( theValue > maximumValuePossible( mRasterDataType ) )
310  {
311  mMaximumValue = maximumValuePossible( mRasterDataType );
312  }
313  else
314  {
315  mMaximumValue = theValue;
316  }
317 
318  if ( mContrastEnhancementFunction )
319  {
320  mContrastEnhancementFunction->setMaximumValue( theValue );
321  }
322 
323  mEnhancementDirty = true;
324 
325  if ( generateTable )
326  {
327  generateLookupTable();
328  }
329 }
330 
337 void QgsContrastEnhancement::setMinimumValue( double theValue, bool generateTable )
338 {
339  QgsDebugMsgLevel( "called value: " + QString::number( theValue ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
340 
341  if ( theValue < minimumValuePossible( mRasterDataType ) )
342  {
343  mMinimumValue = minimumValuePossible( mRasterDataType );
344  }
345  else
346  {
347  mMinimumValue = theValue;
348  }
349 
350  if ( mContrastEnhancementFunction )
351  {
352  mContrastEnhancementFunction->setMinimumValue( theValue );
353  }
354 
355  mEnhancementDirty = true;
356 
357  if ( generateTable )
358  {
359  generateLookupTable();
360  }
361 }
362 
363 void QgsContrastEnhancement::writeXml( QDomDocument& doc, QDomElement& parentElem ) const
364 {
365  //minimum value
366  QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
367  QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
368  minElem.appendChild( minText );
369  parentElem.appendChild( minElem );
370 
371  //maximum value
372  QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) );
373  QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
374  maxElem.appendChild( maxText );
375  parentElem.appendChild( maxElem );
376 
377  //algorithm
378  QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
379  QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
380  algorithmElem.appendChild( algorithmText );
381  parentElem.appendChild( algorithmElem );
382 }
383 
384 void QgsContrastEnhancement::readXml( const QDomElement& elem )
385 {
386  QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
387  if ( !minValueElem.isNull() )
388  {
389  mMinimumValue = minValueElem.text().toDouble();
390  }
391  QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
392  if ( !maxValueElem.isNull() )
393  {
394  mMaximumValue = maxValueElem.text().toDouble();
395  }
396  QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
397  if ( !algorithmElem.isNull() )
398  {
399  QString algorithmString = algorithmElem.text();
401  // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility
402  if ( algorithmString == QLatin1String( "0" ) )
403  {
404  algorithm = NoEnhancement;
405  }
406  else if ( algorithmString == QLatin1String( "1" ) )
407  {
408  algorithm = StretchToMinimumMaximum;
409  }
410  else if ( algorithmString == QLatin1String( "2" ) )
411  {
412  algorithm = StretchAndClipToMinimumMaximum;
413  }
414  else if ( algorithmString == QLatin1String( "3" ) )
415  {
416  algorithm = ClipToMinimumMaximum;
417  }
418  else if ( algorithmString == QLatin1String( "4" ) )
419  {
420  algorithm = UserDefinedEnhancement;
421  }
422  else
423  {
424  algorithm = contrastEnhancementAlgorithmFromString( algorithmString );
425  }
426 
427  setContrastEnhancementAlgorithm( algorithm );
428  }
429 }
430 
432 {
433  switch ( algorithm )
434  {
435  case NoEnhancement:
436  return QStringLiteral( "NoEnhancement" );
438  return QStringLiteral( "StretchToMinimumMaximum" );
440  return QStringLiteral( "StretchAndClipToMinimumMaximum" );
442  return QStringLiteral( "ClipToMinimumMaximum" );
444  return QStringLiteral( "UserDefinedEnhancement" );
445  }
446  return QStringLiteral( "NoEnhancement" );
447 }
448 
450 {
451  if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
452  {
454  }
455  else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
456  {
458  }
459  else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) )
460  {
461  return ClipToMinimumMaximum;
462  }
463  else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) )
464  {
465  return UserDefinedEnhancement;
466  }
467  else
468  {
469  return NoEnhancement;
470  }
471 }
void setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm, bool generateTable=true)
Set the contrast enhancement algorithm.
Thirty two bit signed integer (qint32)
Definition: qgis.h:67
static QString printValue(double value)
Print double value with all necessary significant digits.
static double minimumValuePossible(Qgis::DataType)
Helper function that returns the minimum possible value for a GDAL data type.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
bool isValueInDisplayableRange(double)
Return true if pixel is in stretable range, false if pixel is outside of range (i.e., clipped)
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:66
DataType
Raster data types.
Definition: qgis.h:60
Thirty two bit floating point (float)
Definition: qgis.h:68
Sixteen bit signed integer (qint16)
Definition: qgis.h:65
Complex Int16.
Definition: qgis.h:70
Sixty four bit floating point (double)
Definition: qgis.h:69
void setContrastEnhancementFunction(QgsContrastEnhancementFunction *)
A public method that allows the user to set their own custom contrast enhancment function.
void setMinimumValue(double)
Mutator for the minimum value.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:75
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
A linear enhanceContrast enhancement that first clips to min max and then enhanceContrastes linearly ...
Complex Float32.
Definition: qgis.h:72
void setMinimumValue(double, bool generateTable=true)
Return the minimum value for the contrast enhancement range.
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
void readXml(const QDomElement &elem)
Unknown or unspecified type.
Definition: qgis.h:62
Complex Int32.
Definition: qgis.h:71
A contrast enhancement funcion is the base class for all raster contrast enhancements.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:64
A color enhancement function that performs a linear enhanceContrast between min and max...
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:452
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:437
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used...
A raster contrast enhancement that will clip a value to the specified min/max range.
int enhanceContrast(double)
Apply the contrast enhancement to a value. Return values are 0 - 254, -1 means the pixel was clipped ...
static double maximumValuePossible(Qgis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
virtual int enhance(double)
A customizable method that takes in a double and returns a int between 0 and 255. ...
virtual bool isValueInDisplayableRange(double)
A customicable method to indicate if the pixels is displayable.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
QgsContrastEnhancement(Qgis::DataType theDatatype=Qgis::Byte)
Complex Float64.
Definition: qgis.h:73
void setMaximumValue(double, bool generateTable=true)
Set the maximum value for the contrast enhancement range.
Eight bit unsigned integer (quint8)
Definition: qgis.h:63
void setMaximumValue(double)
Mustator for the maximum value.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:74