i.tasscap.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ############################################################################
  4. #
  5. # MODULE: i.tasscap
  6. # AUTHOR(S): Agustin Lobo, Markus Neteler
  7. # Converted to Python by Glynn Clements
  8. # Code improvements by Leonardo Perathoner
  9. # Sentinel-2 support by Veronica Andreo
  10. #
  11. # PURPOSE: At-satellite reflectance based tasseled cap transformation.
  12. # COPYRIGHT: (C) 1997-2014 by the GRASS Development Team
  13. #
  14. # This program is free software under the GNU General Public
  15. # License (>=v2). Read the file COPYING that comes with GRASS
  16. # for details.
  17. #
  18. #############################################################################
  19. # References:
  20. # LANDSAT-4/LANDSAT-5:
  21. # script based on i.tasscap.tm4 from Dr. Agustin Lobo - alobo@ija.csic.es
  22. # TC-factor changed to CRIST et al. 1986, p.1467 (Markus Neteler 1/99)
  23. # Proc. IGARSS 1986
  24. #
  25. # LANDSAT-7:
  26. # TASSCAP factors cited from:
  27. # DERIVATION OF A TASSELED CAP TRANSFORMATION BASED ON LANDSAT 7 AT-SATELLITE REFLECTANCE
  28. # Chengquan Huang, Bruce Wylie, Limin Yang, Collin Homer and Gregory Zylstra Raytheon ITSS,
  29. # USGS EROS Data Center Sioux Falls, SD 57198, USA
  30. # http://landcover.usgs.gov/pdf/tasseled.pdf
  31. #
  32. # This is published as well in INT. J. OF RS, 2002, VOL 23, NO. 8, 1741-1748.
  33. # Compare discussion:
  34. # http://adis.cesnet.cz/cgi-bin/lwgate/IMAGRS-L/archives/imagrs-l.log0211/date/article-14.html
  35. #
  36. # Landsat8: Baig, M.H.A., Zhang, L., Shuai, T., Tong, Q., 2014. Derivation of a tasselled cap transformation
  37. # based on Landsat 8 at-satellite reflectance. Remote Sensing Letters 5, 423-431.
  38. # doi:10.1080/2150704X.2014.915434
  39. #
  40. # MODIS Tasseled Cap coefficients
  41. # https://gis.stackexchange.com/questions/116107/tasseled-cap-transformation-on-modis-in-grass/116110
  42. # Ref: Lobser & Cohen (2007). MODIS tasselled cap: land cover characteristics
  43. # expressed through transformed MODIS data.
  44. # International Journal of Remote Sensing, Volume 28(22), Table 3
  45. #
  46. # Sentinel-2 Tasseled Cap coefficients
  47. # https://www.researchgate.net/publication/329184434_ORTHOGONAL_TRANSFORMATION_OF_SEGMENTED_IMAGES_FROM_THE_SATELLITE_SENTINEL-2
  48. # Nedkov, R. (2017). ORTHOGONAL TRANSFORMATION OF SEGMENTED IMAGES FROM THE SATELLITE SENTINEL-2.
  49. # Comptes rendus de l'Académie bulgare des sciences. 70. 687-692.
  50. #
  51. #############################################################################
  52. #%Module
  53. #% description: Performs Tasseled Cap (Kauth Thomas) transformation.
  54. #% keyword: imagery
  55. #% keyword: transformation
  56. #% keyword: Landsat
  57. #% keyword: MODIS
  58. #% keyword: Tasseled Cap transformation
  59. #%end
  60. #%option G_OPT_R_INPUTS
  61. #% description: For Landsat4-7: bands 1, 2, 3, 4, 5, 7; for Landsat8: bands 2, 3, 4, 5, 6, 7; for MODIS: bands 1, 2, 3, 4, 5, 6, 7; for Sentinel-2: bands 1 to 12, 8A
  62. #%end
  63. #%option G_OPT_R_BASENAME_OUTPUT
  64. #% label: basename for output raster map(s)
  65. #%end
  66. #%option
  67. #% key: sensor
  68. #% type: string
  69. #% description: Satellite sensor
  70. #% required: yes
  71. #% multiple: no
  72. #% options: landsat4_tm,landsat5_tm,landsat7_etm,landsat8_oli,modis,sentinel2
  73. #%end
  74. import grass.script as grass
  75. # weights for 6 Landsat bands: TM4, TM5, TM7, OLI
  76. # MODIS: Red, NIR1, Blue, Green, NIR2, SWIR1, SWIR2
  77. # Sentinel-2: B1 to B12, B8A
  78. parms = [[(0.3037, 0.2793, 0.4743, 0.5585, 0.5082, 0.1863), # Landsat TM4
  79. (-0.2848, -0.2435, -0.5435, 0.7243, 0.0840, -0.1800),
  80. (0.1509, 0.1973, 0.3279, 0.3406, -0.7112, -0.4572)],
  81. [(0.2909, 0.2493, 0.4806, 0.5568, 0.4438, 0.1706, 10.3695), # Landsat TM5
  82. (-0.2728, -0.2174, -0.5508, 0.7221, 0.0733, -0.1648, -0.7310),
  83. (0.1446, 0.1761, 0.3322, 0.3396, -0.6210, -0.4186, -3.3828),
  84. (0.8461, -0.0731, -0.4640, -0.0032, -0.0492, -0.0119, 0.7879)],
  85. [(0.3561, 0.3972, 0.3904, 0.6966, 0.2286, 0.1596), # Landsat TM7
  86. (-0.3344, -0.3544, -0.4556, 0.6966, -0.0242, -0.2630),
  87. (0.2626, 0.2141, 0.0926, 0.0656, -0.7629, -0.5388),
  88. (0.0805, -0.0498, 0.1950, -0.1327, 0.5752, -0.7775)],
  89. [(0.3029, 0.2786, 0.4733, 0.5599, 0.5080, 0.1872), # Landsat OLI
  90. (-0.2941, -0.2430, -0.5424, 0.7276, 0.0713, -0.1608),
  91. (0.1511, 0.1973, 0.3283, 0.3407, -0.7117, -0.4559),
  92. (-0.8239, 0.0849, 0.4396, -0.0580, 0.2013, -0.2773)],
  93. [(0.4395, 0.5945, 0.2460, 0.3918, 0.3506, 0.2136, 0.2678), # MODIS
  94. (-0.4064, 0.5129, -0.2744, -0.2893, 0.4882, -0.0036, -0.4169),
  95. (0.1147, 0.2489, 0.2408, 0.3132, -0.3122, -0.6416, -0.5087)],
  96. [(0.0356, 0.0822, 0.1360, 0.2611, 0.2964, 0.3338, 0.3877, 0.3895, 0.0949, 0.0009, 0.3882, 0.1366, 0.4750), # Sentinel-2
  97. (-0.0635, -0.1128, -0.1680, -0.3480, -0.3303, 0.0852, 0.3302, 0.3165, 0.0467, -0.0009, -0.4578, -0.4064, 0.3625),
  98. (0.0649, 0.1363, 0.2802, 0.3072, 0.5288, 0.1379, -0.0001, -0.0807, -0.0302, 0.0003, -0.4064, -0.5602, -0.1389)]]
  99. # satellite information
  100. satellites = ['landsat4_tm', 'landsat5_tm', 'landsat7_etm', 'landsat8_oli',
  101. 'modis', 'sentinel2']
  102. used_bands = [6, 6, 6, 6, 7, 13]
  103. # components information
  104. ordinals = ["first", "second", "third", "fourth"]
  105. names = ["Brightness", "Greenness", "Wetness", "Haze"]
  106. def calc1bands6(out, bands, k1, k2, k3, k4, k5, k6, k0=0):
  107. """
  108. Tasseled cap transformation equation for Landsat bands
  109. """
  110. equation = ('$out = $k1 * $in1band + $k2 * $in2band + $k3 * $in3band + '
  111. '$k4 * $in4band + $k5 * $in5band + $k6 * $in6band + $k0')
  112. grass.mapcalc(equation, out=out, k1=k1, k2=k2, k3=k3, k4=k4, k5=k5,
  113. k6=k6, k0=k0, **bands)
  114. def calc1bands7(out, bands, k1, k2, k3, k4, k5, k6, k7):
  115. """
  116. Tasseled cap transformation equation for MODIS bands
  117. """
  118. equation = ('$out = $k1 * $in1band + $k2 * $in2band + $k3 * $in3band + '
  119. '$k4 * $in4band + $k5 * $in5band + $k6 * $in6band + $k7 * '
  120. '$in7band')
  121. grass.mapcalc(equation, out=out, k1=k1, k2=k2, k3=k3, k4=k4, k5=k5, k6=k6,
  122. k7=k7, **bands)
  123. def calc1bands13(out, bands, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13):
  124. """
  125. Tasseled cap transformation equation for Sentinel-2 bands
  126. """
  127. equation = ('$out = $k1 * $in1band + $k2 * $in2band + $k3 * $in3band + '
  128. '$k4 * $in4band + $k5 * $in5band + $k6 * $in6band + $k7 * '
  129. '$in7band + $k8 * $in8band + $k9 * $in9band + $k10 * $in10band + '
  130. '$k11 * $in11band + $k12 * $in12band + $k13 * $in13band')
  131. grass.mapcalc(equation, out=out, k1=k1, k2=k2, k3=k3, k4=k4, k5=k5, k6=k6,
  132. k7=k7, k8=k8, k9=k9, k10=k10, k11=k11, k12=k12, k13=k13,
  133. **bands)
  134. def calcN(outpre, bands, satel):
  135. """
  136. Calculating Tasseled Cap components
  137. """
  138. i = satellites.index(satel)
  139. grass.message(_("Satellite %s...") % satel)
  140. for j, p in enumerate(parms[i]):
  141. out = "%s.%d" % (outpre, j + 1)
  142. ord = ordinals[j]
  143. name = " (%s)" % names[j]
  144. message = "Calculating {ordinal} TC component {outprefix}{outname} ..."
  145. message = message.format(ordinal=ord, outprefix=out, outname=name)
  146. grass.message(_(message))
  147. bands_num = used_bands[i]
  148. # use combination function suitable for used number of bands
  149. eval("calc1bands%d(out, bands, *p)" % bands_num)
  150. grass.run_command('r.colors', map=out, color='grey', quiet=True)
  151. def main():
  152. options, flags = grass.parser()
  153. satellite = options['sensor']
  154. output_basename = options['output']
  155. inputs = options['input'].split(',')
  156. num_of_bands = used_bands[satellites.index(satellite)]
  157. if len(inputs) != num_of_bands:
  158. grass.fatal(_("The number of input raster maps (bands) should be %s") % num_of_bands)
  159. bands = {}
  160. for i, band in enumerate(inputs):
  161. band_num = i + 1
  162. bands['in' + str(band_num) + 'band'] = band
  163. grass.debug(bands, 1)
  164. # core tasseled cap components computation
  165. calcN(output_basename, bands, satellite)
  166. # assign "Data Description" field in all four component maps
  167. num_comp=len(parms[satellites.index(satellite)])
  168. for i in range(0,num_comp):
  169. comp=names[i]
  170. grass.run_command('r.support', map="%s.%d" % (output_basename, i + 1),
  171. description="Tasseled Cap %d: %s" % (i + 1, comp))
  172. grass.message(_("Tasseled Cap components calculated"))
  173. if __name__ == "__main__":
  174. main()