QGIS API Documentation  2.99.0-Master (19b062c)
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  : mRasterDataType( dataType )
34 {
35  mMinimumValue = minimumValuePossible( mRasterDataType );
36  mMaximumValue = maximumValuePossible( mRasterDataType );
37  mRasterDataTypeRange = mMaximumValue - mMinimumValue;
38 
39  mLookupTableOffset = mMinimumValue * -1;
40 
41  mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
42 
43  //If the data type is larger than 16-bit do not generate a lookup table
44  if ( mRasterDataTypeRange <= 65535.0 )
45  {
46  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )];
47  }
48 
49 }
50 
52  : mEnhancementDirty( true )
53  , mMinimumValue( ce.mMinimumValue )
54  , mMaximumValue( ce.mMaximumValue )
55  , mRasterDataType( ce.mRasterDataType )
56  , mRasterDataTypeRange( ce.mRasterDataTypeRange )
57 {
58  mLookupTableOffset = minimumValuePossible( mRasterDataType ) * -1;
59 
60  // setContrastEnhancementAlgorithm sets also QgsContrastEnhancementFunction
61  setContrastEnhancementAlgorithm( ce.mContrastEnhancementAlgorithm, false );
62 
63  //If the data type is larger than 16-bit do not generate a lookup table
64  if ( mRasterDataTypeRange <= 65535.0 )
65  {
66  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )];
67  }
68 }
69 
71 {
72  delete [] mLookupTable;
73 }
74 /*
75  *
76  * Static methods
77  *
78  */
79 
84 {
85  switch ( dataType )
86  {
87  case Qgis::Byte:
88  return std::numeric_limits<unsigned char>::max();
89  case Qgis::UInt16:
90  return std::numeric_limits<unsigned short>::max();
91  case Qgis::Int16:
92  return std::numeric_limits<short>::max();
93  case Qgis::UInt32:
94  return std::numeric_limits<unsigned int>::max();
95  case Qgis::Int32:
96  return std::numeric_limits<int>::max();
97  case Qgis::Float32:
98  return std::numeric_limits<float>::max();
99  case Qgis::Float64:
100  return std::numeric_limits<double>::max();
101  case Qgis::CInt16:
102  return std::numeric_limits<short>::max();
103  case Qgis::CInt32:
104  return std::numeric_limits<int>::max();
105  case Qgis::CFloat32:
106  return std::numeric_limits<float>::max();
107  case Qgis::CFloat64:
108  return std::numeric_limits<double>::max();
109  case Qgis::ARGB32:
112  // XXX - mloskot: not handled?
113  break;
114  }
115 
116  return std::numeric_limits<double>::max();
117 }
118 
123 {
124  switch ( dataType )
125  {
126  case Qgis::Byte:
127  return std::numeric_limits<unsigned char>::min();
128  case Qgis::UInt16:
129  return std::numeric_limits<unsigned short>::min();
130  case Qgis::Int16:
131  return std::numeric_limits<short>::min();
132  case Qgis::UInt32:
133  return std::numeric_limits<unsigned int>::min();
134  case Qgis::Int32:
135  return std::numeric_limits<int>::min();
136  case Qgis::Float32:
137  return std::numeric_limits<float>::max() * -1.0;
138  case Qgis::Float64:
139  return std::numeric_limits<double>::max() * -1.0;
140  case Qgis::CInt16:
141  return std::numeric_limits<short>::min();
142  case Qgis::CInt32:
143  return std::numeric_limits<int>::min();
144  case Qgis::CFloat32:
145  return std::numeric_limits<float>::max() * -1.0;
146  case Qgis::CFloat64:
147  return std::numeric_limits<double>::max() * -1.0;
148  case Qgis::ARGB32:
151  // XXX - mloskot: not handled?
152  break;
153  }
154 
155  return std::numeric_limits<double>::max() * -1.0;
156 }
157 
158 /*
159  *
160  * Non-Static methods
161  *
162  */
163 
170 {
171  if ( mEnhancementDirty )
172  {
173  generateLookupTable();
174  }
175 
176  if ( mLookupTable && NoEnhancement != mContrastEnhancementAlgorithm )
177  {
178  double shiftedValue = value + mLookupTableOffset;
179  if ( shiftedValue >= 0 && shiftedValue < mRasterDataTypeRange + 1 )
180  return mLookupTable[static_cast <int>( shiftedValue )];
181  return 0;
182  }
183  else
184  {
185  // Even if the contrast enhancement algorithms is set to NoEnhancement
186  // The input values will still have to be scaled for all data types
187  // greater than 1 byte.
188  return mContrastEnhancementFunction->enhance( value );
189  }
190 }
191 
195 bool QgsContrastEnhancement::generateLookupTable()
196 {
197  mEnhancementDirty = false;
198 
199  if ( !mContrastEnhancementFunction )
200  return false;
201  if ( NoEnhancement == mContrastEnhancementAlgorithm )
202  return false;
203  if ( Qgis::Byte != mRasterDataType && Qgis::UInt16 != mRasterDataType && Qgis::Int16 != mRasterDataType )
204  return false;
205  if ( !mLookupTable )
206  return false;
207 
208  QgsDebugMsg( "building lookup table" );
209  QgsDebugMsg( "***MinimumValue : " + QString::number( mMinimumValue ) );
210  QgsDebugMsg( "***MaximumValue : " + QString::number( mMaximumValue ) );
211  QgsDebugMsg( "***mLookupTableOffset : " + QString::number( mLookupTableOffset ) );
212  QgsDebugMsg( "***mRasterDataTypeRange : " + QString::number( mRasterDataTypeRange ) );
213 
214  for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ )
215  {
216  mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset );
217  }
218 
219  return true;
220 }
221 
228 {
229  if ( mContrastEnhancementFunction )
230  {
231  return mContrastEnhancementFunction->isValueInDisplayableRange( value );
232  }
233 
234  return false;
235 }
236 
244 {
245  switch ( algorithm )
246  {
248  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
249  break;
251  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) );
252  break;
253  case ClipToMinimumMaximum :
254  mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
255  break;
257  //Do nothing
258  break;
259  default:
260  mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
261  break;
262  }
263 
264  mEnhancementDirty = true;
265  mContrastEnhancementAlgorithm = algorithm;
266 
267  if ( generateTable )
268  {
269  generateLookupTable();
270  }
271 }
272 
279 {
280  QgsDebugMsgLevel( "called", 4 );
281 
282  if ( function )
283  {
284  mContrastEnhancementFunction.reset( function );
285  mContrastEnhancementAlgorithm = UserDefinedEnhancement;
286  generateLookupTable();
287  }
288 }
289 
296 void QgsContrastEnhancement::setMaximumValue( double value, bool generateTable )
297 {
298  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
299 
300  if ( value > maximumValuePossible( mRasterDataType ) )
301  {
302  mMaximumValue = maximumValuePossible( mRasterDataType );
303  }
304  else
305  {
306  mMaximumValue = value;
307  }
308 
309  if ( mContrastEnhancementFunction )
310  {
311  mContrastEnhancementFunction->setMaximumValue( value );
312  }
313 
314  mEnhancementDirty = true;
315 
316  if ( generateTable )
317  {
318  generateLookupTable();
319  }
320 }
321 
328 void QgsContrastEnhancement::setMinimumValue( double value, bool generateTable )
329 {
330  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
331 
332  if ( value < minimumValuePossible( mRasterDataType ) )
333  {
334  mMinimumValue = minimumValuePossible( mRasterDataType );
335  }
336  else
337  {
338  mMinimumValue = value;
339  }
340 
341  if ( mContrastEnhancementFunction )
342  {
343  mContrastEnhancementFunction->setMinimumValue( value );
344  }
345 
346  mEnhancementDirty = true;
347 
348  if ( generateTable )
349  {
350  generateLookupTable();
351  }
352 }
353 
354 void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
355 {
356  //minimum value
357  QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
358  QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
359  minElem.appendChild( minText );
360  parentElem.appendChild( minElem );
361 
362  //maximum value
363  QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) );
364  QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
365  maxElem.appendChild( maxText );
366  parentElem.appendChild( maxElem );
367 
368  //algorithm
369  QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
370  QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
371  algorithmElem.appendChild( algorithmText );
372  parentElem.appendChild( algorithmElem );
373 }
374 
375 void QgsContrastEnhancement::readXml( const QDomElement &elem )
376 {
377  QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
378  if ( !minValueElem.isNull() )
379  {
380  mMinimumValue = minValueElem.text().toDouble();
381  }
382  QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
383  if ( !maxValueElem.isNull() )
384  {
385  mMaximumValue = maxValueElem.text().toDouble();
386  }
387  QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
388  if ( !algorithmElem.isNull() )
389  {
390  QString algorithmString = algorithmElem.text();
392  // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility
393  if ( algorithmString == QLatin1String( "0" ) )
394  {
395  algorithm = NoEnhancement;
396  }
397  else if ( algorithmString == QLatin1String( "1" ) )
398  {
399  algorithm = StretchToMinimumMaximum;
400  }
401  else if ( algorithmString == QLatin1String( "2" ) )
402  {
403  algorithm = StretchAndClipToMinimumMaximum;
404  }
405  else if ( algorithmString == QLatin1String( "3" ) )
406  {
407  algorithm = ClipToMinimumMaximum;
408  }
409  else if ( algorithmString == QLatin1String( "4" ) )
410  {
411  algorithm = UserDefinedEnhancement;
412  }
413  else
414  {
415  algorithm = contrastEnhancementAlgorithmFromString( algorithmString );
416  }
417 
418  setContrastEnhancementAlgorithm( algorithm );
419  }
420 }
421 
423 {
424  switch ( algorithm )
425  {
426  case NoEnhancement:
427  return QStringLiteral( "NoEnhancement" );
429  return QStringLiteral( "StretchToMinimumMaximum" );
431  return QStringLiteral( "StretchAndClipToMinimumMaximum" );
433  return QStringLiteral( "ClipToMinimumMaximum" );
435  return QStringLiteral( "UserDefinedEnhancement" );
436  }
437  return QStringLiteral( "NoEnhancement" );
438 }
439 
441 {
442  if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
443  {
445  }
446  else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
447  {
449  }
450  else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) )
451  {
452  return ClipToMinimumMaximum;
453  }
454  else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) )
455  {
456  return UserDefinedEnhancement;
457  }
458  else
459  {
460  return NoEnhancement;
461  }
462 }
void setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm, bool generateTable=true)
Set the contrast enhancement algorithm.
Thirty two bit signed integer (qint32)
Definition: qgis.h:85
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:37
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:84
DataType
Raster data types.
Definition: qgis.h:78
Thirty two bit floating point (float)
Definition: qgis.h:86
Sixteen bit signed integer (qint16)
Definition: qgis.h:83
Complex Int16.
Definition: qgis.h:88
Sixty four bit floating point (double)
Definition: qgis.h:87
void setContrastEnhancementFunction(QgsContrastEnhancementFunction *)
A public method that allows the user to set their own custom contrast enhancement function...
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:93
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
A linear enhanceContrast enhancement that first clips to min max and then enhanceContrastes linearly ...
Complex Float32.
Definition: qgis.h:90
void setMinimumValue(double, bool generateTable=true)
Return the minimum value for the contrast enhancement range.
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Return a string to serialize ContrastEnhancementAlgorithm.
void readXml(const QDomElement &elem)
Unknown or unspecified type.
Definition: qgis.h:80
Complex Int32.
Definition: qgis.h:89
A contrast enhancement function is the base class for all raster contrast enhancements.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:82
A color enhancement function that performs a linear enhanceContrast between min and max...
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
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.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
Complex Float64.
Definition: qgis.h:91
QgsContrastEnhancement(Qgis::DataType datatype=Qgis::Byte)
void setMaximumValue(double, bool generateTable=true)
Set the maximum value for the contrast enhancement range.
Eight bit unsigned integer (quint8)
Definition: qgis.h:81
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:92