create_iwave.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. #!/usr/bin/env python
  2. """
  3. Created on Sat Mar 27 11:35:32 2010
  4. Program to interpolate filter function to correct
  5. step. Should be 2.5 nm
  6. Then output filter function in a format similar to
  7. what is needed in the Iwave.cpp file
  8. Needs numpy and scipy
  9. @author: daniel victoria, 2010
  10. contact: daniel {dot} victoria {at} gmail {dot} com
  11. usage() explains how this is supposed to work
  12. Basically it needs a .csv file with spectral response for each
  13. band in a column. First column has to be wavelength (nm)
  14. First line (and only first) is a header with Wl, followed by band names
  15. file name is used for sensor name
  16. Updated by: Anne Ghisla, 2010
  17. Bug fix (9/12/2010) by Daniel:
  18. 1) function interpolate_band was not generating the spectral response for the last value in the filter function. Fixed
  19. 2) function pretty_print was not printing the 8th value of every line, cutting the filter function short.
  20. """
  21. import os
  22. import sys
  23. import numpy as np
  24. from scipy import interpolate
  25. def usage():
  26. """How to use this..."""
  27. print "create_iwave.py <csv file>"
  28. print "Generate filter function IWave.cpp template from csv file"
  29. print "csv file must have wl response for each band in each column"
  30. print "first line must be a header with wl followed by band names"
  31. print "following lines will be the data."
  32. print "If response is null, leave empty in csv file. Ex.:"
  33. print "WL(nm),band 1,band 2,band 3,band 4"
  34. print "455,0.93,,,"
  35. print "485,0.94,0.00,,"
  36. print "545,0.00,0.87,0.00,"
  37. print "Program will interpolate filter function to 2.5 nm steps"
  38. print "and output a cpp template file in the IWave format"
  39. def read_input(csvfile):
  40. """
  41. Function to read input file
  42. return number of bands and array of values for each band
  43. should be a .csv file with the values
  44. of the filter function for each band in the sensor
  45. one column for band
  46. first line must have a header with sensor band name
  47. first column is wavelength
  48. """
  49. infile = open(csvfile, 'r')
  50. # get number of bands and band names
  51. bands = infile.readline().split(',')
  52. bands.remove(bands[0])
  53. bands[-1] = bands[-1].strip()
  54. infile.close()
  55. # create converter dictionary for import
  56. # fix nodata or \n
  57. conv = {}
  58. for b in range(len(bands)):
  59. conv[b+1] = lambda s: float(s or -99)
  60. values = np.loadtxt(csvfile, delimiter=',', skiprows=1, converters = conv)
  61. return (bands, values)
  62. def interpolate_band(values):
  63. """
  64. Receive wavelength and response for one band
  65. interpolate at 2.5 nm steps
  66. return interpolated filter func
  67. and min, max wl values
  68. values must be numpy array with 2 columns
  69. """
  70. # These 2 lines select the subarray
  71. # remove nodata (-99) lines in values array
  72. # where response is nodata?
  73. w = values[:,1] >= 0
  74. response = values[w]
  75. # interpolating
  76. f = interpolate.interp1d(response[:,0],response[:,1])
  77. filter_f = f(np.arange(response[0,0], response[-1,0] + 2.5, 2.5))
  78. # convert limits from nanometers to micrometers
  79. lowerlimit = response[0,0]/1000
  80. upperlimit = response[-1,0]/1000
  81. return(filter_f, (lowerlimit, upperlimit))
  82. def plot_filter(values):
  83. """Plot wl response values and interpolated
  84. filter function. This is just for checking...
  85. value is a 2 column numpy array
  86. function has to be used inside Spyder python environment
  87. """
  88. filter_f, limits = interpolate_band(values)
  89. # removing nodata
  90. w = values[:,1] >= 0
  91. response = values[w]
  92. plot(response[:,0],response[:,1], 'ro')
  93. plot(arange(limits[0], limits[1], 2.5), filter_f)
  94. return
  95. def pretty_print(filter_f):
  96. """
  97. Create pretty string out of filter function
  98. 8 values per line, with spaces, commas and all the rest
  99. """
  100. pstring = ''
  101. for i in range(len(filter_f)+1):
  102. if i%8 is 0:
  103. if i is not 0:
  104. value_wo_leading_zero = ('%.4f' % (filter_f[i-1])).lstrip('0')
  105. pstring += value_wo_leading_zero+', '
  106. if i is not 1:
  107. # trim the trailing whitespace at the end of line
  108. pstring = pstring.rstrip()
  109. pstring += "\n\t\t"
  110. else:
  111. value_wo_leading_zero = ('%.4f' % (filter_f[i-1])).lstrip('0')
  112. pstring += value_wo_leading_zero+', '
  113. # trim starting \n and trailing ,
  114. pstring = pstring.lstrip("\n").rstrip(", ")
  115. return pstring
  116. def write_cpp(bands, values, sensor, folder):
  117. """
  118. from bands, values and sensor name
  119. create output file in cpp style
  120. needs other functions: interpolate_bands, pretty_print
  121. """
  122. # getting necessary data
  123. # single or multiple bands?
  124. if len(bands) == 1:
  125. filter_f, limits = interpolate_band(values)
  126. else:
  127. filter_f = []
  128. limits = []
  129. for b in range(len(bands)):
  130. fi, li = interpolate_band(values[:,[0,b+1]])
  131. filter_f.append(fi)
  132. limits.append(li)
  133. # writing...
  134. outfile = open(os.path.join(folder, sensor+"_cpp_template.txt"), 'w')
  135. outfile.write('/* Following filter function created using create_iwave.py */\n\n')
  136. if len(bands) == 1:
  137. outfile.write('void IWave::%s()\n{\n\n' % (sensor.lower()))
  138. else:
  139. outfile.write('void IWave::%s(int iwa)\n{\n\n' % (sensor.lower()))
  140. # single band case
  141. if len(bands) == 1:
  142. outfile.write(' /* %s of %s */\n' % (bands[0], sensor))
  143. outfile.write(' static const float sr[%i] = {' % (len(filter_f)))
  144. filter_text = pretty_print(filter_f)
  145. outfile.write(filter_text)
  146. # calculate wl slot for band start
  147. # slots range from 250 to 4000 at 2.5 increments (total 1500)
  148. s_start = int((limits[0]*1000 - 250)/2.5)
  149. outfile.write('\n')
  150. outfile.write(' ffu.wlinf = %.4ff;\n' % (limits[0]))
  151. outfile.write(' ffu.wlsup = %.4ff;\n' % (limits[1]))
  152. outfile.write(' int i = 0;\n')
  153. outfile.write(' for(i = 0; i < %i; i++)\tffu.s[i] = 0;\n' % (s_start))
  154. outfile.write(' for(i = 0; i < %i; i++)\tffu.s[%i+i] = sr[i];\n' % (len(filter_f), s_start))
  155. outfile.write(' for(i = %i; i < 1501; i++)\tffu.s[i] = 0;\n' % (s_start + len(filter_f)))
  156. outfile.write('}\n')
  157. else: # more than 1 band
  158. # writing bands
  159. for b in range(len(bands)):
  160. outfile.write(' /* %s of %s */\n' % (bands[b], sensor))
  161. outfile.write(' static const float sr%i[%i] = {\n' % (b+1,len(filter_f[b])))
  162. filter_text = pretty_print(filter_f[b])
  163. outfile.write(filter_text+'\n };\n\t\n')
  164. # writing band limits
  165. for b in range(len(bands)):
  166. inf = ", ".join(["%.3f" % i[0] for i in limits])
  167. sup = ", ".join(["%.3f" % i[1] for i in limits])
  168. outfile.write(' static const float wli[%i] = {%s};\n' % (len(bands), inf))
  169. outfile.write(' static const float wls[%i] = {%s};\n' % (len(bands), sup))
  170. outfile.write('\n')
  171. outfile.write(' ffu.wlinf = (float)wli[iwa-1];\n')
  172. outfile.write(' ffu.wlsup = (float)wls[iwa-1];\n\n')
  173. outfile.write(' int i;\n')
  174. outfile.write(' for(i = 0; i < 1501; i++) ffu.s[i] = 0;\n\n')
  175. outfile.write(' switch(iwa)\n {\n')
  176. # now start of case part...
  177. for b in range(len(bands)):
  178. s_start = int((limits[b][0]*1000 - 250)/2.5)
  179. outfile.write(' case %i: for(i = 0; i < %i; i++) ffu.s[%i+i] = sr%i[i];\n' % ((b+1), len(filter_f[b]), s_start, (b+1)))
  180. outfile.write(' break;\n')
  181. outfile.write(' }\n}\n')
  182. return
  183. def main():
  184. """ control function """
  185. inputfile = sys.argv[1]
  186. # getting sensor name from full csv file name
  187. sensor = os.path.splitext(os.path.basename(inputfile))[0]
  188. print "Getting sensor name from csv file: %s" % (sensor)
  189. # getting data from file
  190. bands, values = read_input(inputfile)
  191. # writing file in same folder of input file
  192. write_cpp(bands, values, sensor, os.path.dirname(inputfile))
  193. print "Filter function written to %s" % (sensor+"_cpp_template.txt")
  194. print "Please check file for possible errors before inserting into IWave.cpp"
  195. print "Don't forget to add necessary data to IWave.h"
  196. return
  197. if __name__ == '__main__':
  198. if len(sys.argv) == 1:
  199. usage()
  200. sys.exit()
  201. else:
  202. main()