QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
81 {
82  switch ( dataType )
83  {
84  case Qgis::Byte:
85  return std::numeric_limits<unsigned char>::max();
86  case Qgis::UInt16:
87  return std::numeric_limits<unsigned short>::max();
88  case Qgis::Int16:
89  return std::numeric_limits<short>::max();
90  case Qgis::UInt32:
91  return std::numeric_limits<unsigned int>::max();
92  case Qgis::Int32:
93  return std::numeric_limits<int>::max();
94  case Qgis::Float32:
95  return std::numeric_limits<float>::max();
96  case Qgis::Float64:
97  return std::numeric_limits<double>::max();
98  case Qgis::CInt16:
99  return std::numeric_limits<short>::max();
100  case Qgis::CInt32:
101  return std::numeric_limits<int>::max();
102  case Qgis::CFloat32:
103  return std::numeric_limits<float>::max();
104  case Qgis::CFloat64:
105  return std::numeric_limits<double>::max();
106  case Qgis::ARGB32:
109  // XXX - mloskot: not handled?
110  break;
111  }
112 
113  return std::numeric_limits<double>::max();
114 }
115 
117 {
118  switch ( dataType )
119  {
120  case Qgis::Byte:
121  return std::numeric_limits<unsigned char>::min();
122  case Qgis::UInt16:
123  return std::numeric_limits<unsigned short>::min();
124  case Qgis::Int16:
125  return std::numeric_limits<short>::min();
126  case Qgis::UInt32:
127  return std::numeric_limits<unsigned int>::min();
128  case Qgis::Int32:
129  return std::numeric_limits<int>::min();
130  case Qgis::Float32:
131  return std::numeric_limits<float>::max() * -1.0;
132  case Qgis::Float64:
133  return std::numeric_limits<double>::max() * -1.0;
134  case Qgis::CInt16:
135  return std::numeric_limits<short>::min();
136  case Qgis::CInt32:
137  return std::numeric_limits<int>::min();
138  case Qgis::CFloat32:
139  return std::numeric_limits<float>::max() * -1.0;
140  case Qgis::CFloat64:
141  return std::numeric_limits<double>::max() * -1.0;
142  case Qgis::ARGB32:
145  // XXX - mloskot: not handled?
146  break;
147  }
148 
149  return std::numeric_limits<double>::max() * -1.0;
150 }
151 
152 /*
153  *
154  * Non-Static methods
155  *
156  */
157 
159 {
160  if ( mEnhancementDirty )
161  {
162  generateLookupTable();
163  }
164 
165  if ( mLookupTable && NoEnhancement != mContrastEnhancementAlgorithm )
166  {
167  double shiftedValue = value + mLookupTableOffset;
168  if ( shiftedValue >= 0 && shiftedValue < mRasterDataTypeRange + 1 )
169  return mLookupTable[static_cast <int>( shiftedValue )];
170  return 0;
171  }
172  else
173  {
174  // Even if the contrast enhancement algorithms is set to NoEnhancement
175  // The input values will still have to be scaled for all data types
176  // greater than 1 byte.
177  return mContrastEnhancementFunction->enhance( value );
178  }
179 }
180 
181 bool QgsContrastEnhancement::generateLookupTable()
182 {
183  mEnhancementDirty = false;
184 
185  if ( !mContrastEnhancementFunction )
186  return false;
187  if ( NoEnhancement == mContrastEnhancementAlgorithm )
188  return false;
189  if ( Qgis::Byte != mRasterDataType && Qgis::UInt16 != mRasterDataType && Qgis::Int16 != mRasterDataType )
190  return false;
191  if ( !mLookupTable )
192  return false;
193 
194  QgsDebugMsg( QStringLiteral( "building lookup table" ) );
195  QgsDebugMsg( "***MinimumValue : " + QString::number( mMinimumValue ) );
196  QgsDebugMsg( "***MaximumValue : " + QString::number( mMaximumValue ) );
197  QgsDebugMsg( "***mLookupTableOffset : " + QString::number( mLookupTableOffset ) );
198  QgsDebugMsg( "***mRasterDataTypeRange : " + QString::number( mRasterDataTypeRange ) );
199 
200  for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ )
201  {
202  mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset );
203  }
204 
205  return true;
206 }
207 
209 {
210  if ( mContrastEnhancementFunction )
211  {
212  return mContrastEnhancementFunction->isValueInDisplayableRange( value );
213  }
214 
215  return false;
216 }
217 
219 {
220  switch ( algorithm )
221  {
223  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
224  break;
226  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) );
227  break;
228  case ClipToMinimumMaximum :
229  mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
230  break;
232  //Do nothing
233  break;
234  default:
235  mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
236  break;
237  }
238 
239  mEnhancementDirty = true;
240  mContrastEnhancementAlgorithm = algorithm;
241 
242  if ( generateTable )
243  {
244  generateLookupTable();
245  }
246 }
247 
249 {
250  QgsDebugMsgLevel( QStringLiteral( "called" ), 4 );
251 
252  if ( function )
253  {
254  mContrastEnhancementFunction.reset( function );
255  mContrastEnhancementAlgorithm = UserDefinedEnhancement;
256  generateLookupTable();
257  }
258 }
259 
260 void QgsContrastEnhancement::setMaximumValue( double value, bool generateTable )
261 {
262  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
263 
264  if ( value > maximumValuePossible( mRasterDataType ) )
265  {
266  mMaximumValue = maximumValuePossible( mRasterDataType );
267  }
268  else
269  {
270  mMaximumValue = value;
271  }
272 
273  if ( mContrastEnhancementFunction )
274  {
275  mContrastEnhancementFunction->setMaximumValue( value );
276  }
277 
278  mEnhancementDirty = true;
279 
280  if ( generateTable )
281  {
282  generateLookupTable();
283  }
284 }
285 
286 void QgsContrastEnhancement::setMinimumValue( double value, bool generateTable )
287 {
288  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
289 
290  if ( value < minimumValuePossible( mRasterDataType ) )
291  {
292  mMinimumValue = minimumValuePossible( mRasterDataType );
293  }
294  else
295  {
296  mMinimumValue = value;
297  }
298 
299  if ( mContrastEnhancementFunction )
300  {
301  mContrastEnhancementFunction->setMinimumValue( value );
302  }
303 
304  mEnhancementDirty = true;
305 
306  if ( generateTable )
307  {
308  generateLookupTable();
309  }
310 }
311 
312 void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
313 {
314  //minimum value
315  QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
316  QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
317  minElem.appendChild( minText );
318  parentElem.appendChild( minElem );
319 
320  //maximum value
321  QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) );
322  QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
323  maxElem.appendChild( maxText );
324  parentElem.appendChild( maxElem );
325 
326  //algorithm
327  QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
328  QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
329  algorithmElem.appendChild( algorithmText );
330  parentElem.appendChild( algorithmElem );
331 }
332 
333 void QgsContrastEnhancement::readXml( const QDomElement &elem )
334 {
335  QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
336  if ( !minValueElem.isNull() )
337  {
338  mMinimumValue = minValueElem.text().toDouble();
339  }
340  QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
341  if ( !maxValueElem.isNull() )
342  {
343  mMaximumValue = maxValueElem.text().toDouble();
344  }
345  QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
346  if ( !algorithmElem.isNull() )
347  {
348  QString algorithmString = algorithmElem.text();
350  // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility
351  if ( algorithmString == QLatin1String( "0" ) )
352  {
353  algorithm = NoEnhancement;
354  }
355  else if ( algorithmString == QLatin1String( "1" ) )
356  {
357  algorithm = StretchToMinimumMaximum;
358  }
359  else if ( algorithmString == QLatin1String( "2" ) )
360  {
361  algorithm = StretchAndClipToMinimumMaximum;
362  }
363  else if ( algorithmString == QLatin1String( "3" ) )
364  {
365  algorithm = ClipToMinimumMaximum;
366  }
367  else if ( algorithmString == QLatin1String( "4" ) )
368  {
369  algorithm = UserDefinedEnhancement;
370  }
371  else
372  {
373  algorithm = contrastEnhancementAlgorithmFromString( algorithmString );
374  }
375 
376  setContrastEnhancementAlgorithm( algorithm );
377  }
378 }
379 
380 void QgsContrastEnhancement::toSld( QDomDocument &doc, QDomElement &element ) const
381 {
382  if ( doc.isNull() || element.isNull() )
383  return;
384 
385  QString algName;
386  switch ( contrastEnhancementAlgorithm() )
387  {
389  algName = QStringLiteral( "StretchToMinimumMaximum" );
390  break;
391  /* TODO: check if ClipToZero => StretchAndClipToMinimumMaximum
392  * because value outside min/max ar considered as NoData instead of 0 */
394  algName = QStringLiteral( "ClipToMinimumMaximum" );
395  break;
397  algName = QStringLiteral( "ClipToMinimumMaximum" );
398  break;
399  case NoEnhancement:
400  return;
403  QgsDebugMsg( QObject::tr( "No SLD1.0 conversion yet for stretch algorithm %1" ).arg( algName ) );
404  return;
405  }
406 
407  // Only <Normalize> is supported
408  // minValue and maxValue are that values as set depending on "Min /Max value settings"
409  // parameters
410  QDomElement normalizeElem = doc.createElement( QStringLiteral( "sld:Normalize" ) );
411  element.appendChild( normalizeElem );
412 
413  QDomElement vendorOptionAlgorithmElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
414  vendorOptionAlgorithmElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "algorithm" ) );
415  vendorOptionAlgorithmElem.appendChild( doc.createTextNode( algName ) );
416  normalizeElem.appendChild( vendorOptionAlgorithmElem );
417 
418  QDomElement vendorOptionMinValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
419  vendorOptionMinValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "minValue" ) );
420  vendorOptionMinValueElem.appendChild( doc.createTextNode( QString::number( minimumValue() ) ) );
421  normalizeElem.appendChild( vendorOptionMinValueElem );
422 
423  QDomElement vendorOptionMaxValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
424  vendorOptionMaxValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maxValue" ) );
425  vendorOptionMaxValueElem.appendChild( doc.createTextNode( QString::number( maximumValue() ) ) );
426  normalizeElem.appendChild( vendorOptionMaxValueElem );
427 }
428 
430 {
431  switch ( algorithm )
432  {
433  case NoEnhancement:
434  return QStringLiteral( "NoEnhancement" );
436  return QStringLiteral( "StretchToMinimumMaximum" );
438  return QStringLiteral( "StretchAndClipToMinimumMaximum" );
440  return QStringLiteral( "ClipToMinimumMaximum" );
442  return QStringLiteral( "UserDefinedEnhancement" );
443  }
444  return QStringLiteral( "NoEnhancement" );
445 }
446 
448 {
449  if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
450  {
452  }
453  else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
454  {
456  }
457  else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) )
458  {
459  return ClipToMinimumMaximum;
460  }
461  else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) )
462  {
463  return UserDefinedEnhancement;
464  }
465  else
466  {
467  return NoEnhancement;
468  }
469 }
Thirty two bit signed integer (qint32)
Definition: qgis.h:99
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
static QString printValue(double value)
Print double value with all necessary significant digits.
void setContrastEnhancementFunction(QgsContrastEnhancementFunction *function)
Allows the user to set their own custom contrast enhancement function.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
static double minimumValuePossible(Qgis::DataType)
Helper function that returns the minimum possible value for a GDAL data type.
void setMinimumValue(double value, bool generateTable=true)
Sets the minimum value for the contrast enhancement range.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:98
DataType
Raster data types.
Definition: qgis.h:92
Thirty two bit floating point (float)
Definition: qgis.h:100
Sixteen bit signed integer (qint16)
Definition: qgis.h:97
Complex Int16.
Definition: qgis.h:102
Sixty four bit floating point (double)
Definition: qgis.h:101
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:107
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setMaximumValue(double value, bool generateTable=true)
Sets the maximum value for the contrast enhancement range.
void toSld(QDomDocument &doc, QDomElement &element) const
Write ContrastEnhancement tags following SLD v1.0 specs SLD1.0 is limited to the parameters listed in...
A linear enhanceContrast enhancement that first clips to min max and then enhanceContrastes linearly ...
Complex Float32.
Definition: qgis.h:104
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i...
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
void readXml(const QDomElement &elem)
Unknown or unspecified type.
Definition: qgis.h:94
Complex Int32.
Definition: qgis.h:103
A contrast enhancement function is the base class for all raster contrast enhancements.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:96
double minimumValue() const
Returns the minimum value for the contrast enhancement range.
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.
static double maximumValuePossible(Qgis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm's parent class) implements the new pure virtual createInstance(self) call
double maximumValue() const
Returns the maximum value for the contrast enhancement range.
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
Complex Float64.
Definition: qgis.h:105
QgsContrastEnhancement(Qgis::DataType datatype=Qgis::Byte)
void setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm algorithm, bool generateTable=true)
Sets the contrast enhancement algorithm.
Eight bit unsigned integer (quint8)
Definition: qgis.h:95
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:106
int enhanceContrast(double value)
Applies the contrast enhancement to a value.