i.landsat.rgb.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #!/usr/bin/env python
  2. ############################################################################
  3. #
  4. # MODULE: i.landsat.rgb
  5. #
  6. # AUTHOR(S): Markus Neteler. <neteler itc it>
  7. # Hamish Bowman, scripting enhancements
  8. # Converted to Python by Glynn Clements
  9. #
  10. # PURPOSE: create pretty LANDSAT RGBs: the trick is to remove outliers
  11. # using percentiles (area under the histogram curve)
  12. #
  13. # COPYRIGHT: (C) 2006, 2008, 2012 by the GRASS Development Team
  14. #
  15. # This program is free software under the GNU General Public
  16. # License (>=v2). Read the file COPYING that comes with GRASS
  17. # for details.
  18. #
  19. # TODO: implement better brightness control
  20. #############################################################################
  21. #%module
  22. #% description: Performs auto-balancing of colors for LANDSAT images.
  23. #% keywords: imagery
  24. #% keywords: landsat
  25. #% keywords: colors
  26. #%end
  27. #%option G_OPT_R_INPUT
  28. #% key: red
  29. #% description: Name of LANDSAT red channel
  30. #%end
  31. #%option G_OPT_R_INPUT
  32. #% key: green
  33. #% description: Name of LANDSAT green channel
  34. #%end
  35. #%option G_OPT_R_INPUT
  36. #% key: blue
  37. #% description: Name of LANDSAT blue channel
  38. #%end
  39. #%option
  40. #% key: strength
  41. #% type: double
  42. #% description: Cropping intensity (upper brightness level)
  43. #% options: 0-100
  44. #% answer : 98
  45. #% required: no
  46. #%end
  47. #%flag
  48. #% key: f
  49. #% description: Extend colors to full range of data on each channel
  50. #% guisection: Colors
  51. #%end
  52. #%flag
  53. #% key: p
  54. #% description: Preserve relative colors, adjust brightness only
  55. #% guisection: Colors
  56. #%end
  57. #%flag
  58. #% key: r
  59. #% description: Reset to standard color range
  60. #% guisection: Colors
  61. #%end
  62. #%flag
  63. #% key: s
  64. #% description: Process bands serially (default: run in parallel)
  65. #%end
  66. import sys
  67. import os
  68. import string
  69. import grass.script as grass
  70. try:
  71. # new for python 2.6, in 2.5 it may be easy_install'd.
  72. import multiprocessing as mp
  73. do_mp = True
  74. except:
  75. do_mp = False
  76. def get_percentile(map, percentiles):
  77. # todo: generalize for any list length
  78. val1 = percentiles[0]
  79. val2 = percentiles[1]
  80. values = '%s,%s' % (val1, val2)
  81. s = grass.read_command('r.univar', flags = 'ge',
  82. map = map, percentile = values)
  83. kv = grass.parse_key_val(s)
  84. # cleanse to match what the key name will become
  85. val_str1 = ('percentile_%.15g' % float(val1)).replace('.','_')
  86. val_str2 = ('percentile_%.15g' % float(val2)).replace('.','_')
  87. return (float(kv[val_str1]), float(kv[val_str2]))
  88. # wrapper to handle multiprocesses communications back to the parent
  89. def get_percentile_mp(map, percentiles, conn):
  90. # Process() doesn't like storing connection parts in
  91. # separate dictionaries, only wants to pass through tuples,
  92. # so instead of just sending the sending the pipe we have to
  93. # send both parts then keep the one we want. ??
  94. output_pipe, input_pipe = conn
  95. input_pipe.close()
  96. result = get_percentile(map, percentiles)
  97. grass.debug('child (%s) (%.1f, %.1f)' % (map, result[0], result[1]))
  98. output_pipe.send(result)
  99. output_pipe.close()
  100. def set_colors(map, v0, v1):
  101. rules = [
  102. "0% black\n",
  103. "%f black\n" % v0,
  104. "%f white\n" % v1,
  105. "100% white\n"
  106. ]
  107. rules = ''.join(rules)
  108. grass.write_command('r.colors', map = map, rules = '-', stdin = rules, quiet = True)
  109. def main():
  110. red = options['red']
  111. green = options['green']
  112. blue = options['blue']
  113. brightness = options['strength']
  114. full = flags['f']
  115. preserve = flags['p']
  116. reset = flags['r']
  117. global do_mp
  118. if flags['s']:
  119. do_mp = False
  120. # 90 or 98? MAX value controls brightness
  121. # think of percent (0-100), must be positive or 0
  122. # must be more than "2" ?
  123. if full:
  124. for i in [red, green, blue]:
  125. grass.run_command('r.colors', map = i, color = 'grey', quiet = True)
  126. sys.exit(0)
  127. if reset:
  128. for i in [red, green, blue]:
  129. grass.run_command('r.colors', map = i, color = 'grey255', quiet = True)
  130. sys.exit(0)
  131. if not preserve:
  132. if do_mp:
  133. grass.message(_("Processing..."))
  134. # set up jobs and launch them
  135. proc = {}
  136. conn = {}
  137. for i in [red, green, blue]:
  138. conn[i] = mp.Pipe()
  139. proc[i] = mp.Process(target = get_percentile_mp,
  140. args = (i, ['2', brightness],
  141. conn[i],))
  142. proc[i].start()
  143. grass.percent(1, 2, 1)
  144. # collect results and wait for jobs to finish
  145. for i in [red, green, blue]:
  146. output_pipe, input_pipe = conn[i]
  147. (v0, v1) = input_pipe.recv()
  148. grass.debug('parent (%s) (%.1f, %.1f)' % (i, v0, v1))
  149. input_pipe.close()
  150. proc[i].join()
  151. set_colors(i, v0, v1)
  152. grass.percent(1, 1, 1)
  153. else:
  154. for i in [red, green, blue]:
  155. grass.message(_("Processing..."))
  156. (v0, v1) = get_percentile(i, ['2', brightness])
  157. grass.debug("<%s>: min=%f max=%f" % (i, v0, v1))
  158. set_colors(i, v0, v1)
  159. else:
  160. all_max = 0
  161. all_min = 999999
  162. if do_mp:
  163. grass.message(_("Processing..."))
  164. # set up jobs and launch jobs
  165. proc = {}
  166. conn = {}
  167. for i in [red, green, blue]:
  168. conn[i] = mp.Pipe()
  169. proc[i] = mp.Process(target = get_percentile_mp,
  170. args = (i, ['2', brightness],
  171. conn[i],))
  172. proc[i].start()
  173. grass.percent(1, 2, 1)
  174. # collect results and wait for jobs to finish
  175. for i in [red, green, blue]:
  176. output_pipe, input_pipe = conn[i]
  177. (v0, v1) = input_pipe.recv()
  178. grass.debug('parent (%s) (%.1f, %.1f)' % (i, v0, v1))
  179. input_pipe.close()
  180. proc[i].join()
  181. all_min = min(all_min, v0)
  182. all_max = max(all_max, v1)
  183. grass.percent(1, 1, 1)
  184. else:
  185. for i in [red, green, blue]:
  186. grass.message(_("Processing..."))
  187. (v0, v1) = get_percentile(i, ['2', brightness])
  188. grass.debug("<%s>: min=%f max=%f" % (i, v0, v1))
  189. all_min = min(all_min, v0)
  190. all_max = max(all_max, v1)
  191. grass.debug("all_min=%f all_max=%f" % (all_min, all_max))
  192. for i in [red, green, blue]:
  193. set_colors(i, all_min, all_max)
  194. # write cmd history:
  195. mapset = grass.gisenv()['MAPSET']
  196. for i in [red, green, blue]:
  197. if grass.find_file(i)['mapset'] == mapset:
  198. grass.raster_history(i)
  199. if __name__ == "__main__":
  200. options, flags = grass.parser()
  201. main()