create_iwave.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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 names
  47. first column is wavelength
  48. values are those of the discrete band filter functions
  49. """
  50. infile = open(csvfile, 'r')
  51. # get number of bands and band names
  52. bands = infile.readline().split(',')
  53. bands.remove(bands[0])
  54. bands[-1] = bands[-1].strip()
  55. print "Number of bands found: %d" % len(bands)
  56. infile.close()
  57. # create converter dictionary for import
  58. # fix nodata or \n
  59. conv = {}
  60. for b in range(len(bands)):
  61. conv[b+1] = lambda s: float(s or -99)
  62. values = np.loadtxt(csvfile, delimiter=',', skiprows=1, converters = conv)
  63. return (bands, values)
  64. def interpolate_band(values):
  65. """
  66. Receive wavelength and response for one band
  67. interpolate at 2.5 nm steps
  68. return interpolated filter func
  69. and min, max wl values
  70. values must be numpy array with 2 columns
  71. """
  72. # These 2 lines select the subarray
  73. # remove nodata (-99) lines in values array
  74. # where response is nodata?
  75. w = values[:,1] >= 0
  76. response = values[w]
  77. # interpolating
  78. f = interpolate.interp1d(response[:,0],response[:,1])
  79. filter_f = f(np.arange(response[0,0], response[-1,0] + 2.5, 2.5))
  80. # convert limits from nanometers to micrometers
  81. lowerlimit = response[0,0]/1000
  82. upperlimit = response[-1,0]/1000
  83. return(filter_f, (lowerlimit, upperlimit))
  84. def plot_filter(values):
  85. """Plot wl response values and interpolated
  86. filter function. This is just for checking...
  87. value is a 2 column numpy array
  88. function has to be used inside Spyder python environment
  89. """
  90. filter_f, limits = interpolate_band(values)
  91. # removing nodata
  92. w = values[:,1] >= 0
  93. response = values[w]
  94. plot(response[:,0],response[:,1], 'ro')
  95. plot(arange(limits[0], limits[1], 2.5), filter_f)
  96. return
  97. def pretty_print(filter_f):
  98. """
  99. Create pretty string out of filter function
  100. 8 values per line, with spaces, commas and all the rest
  101. """
  102. pstring = ''
  103. for i in range(len(filter_f)+1):
  104. if i%8 is 0:
  105. if i is not 0:
  106. value_wo_leading_zero = ('%.4f' % (filter_f[i-1])).lstrip('0')
  107. pstring += value_wo_leading_zero+', '
  108. if i is not 1:
  109. # trim the trailing whitespace at the end of line
  110. pstring = pstring.rstrip()
  111. pstring += "\n\t\t"
  112. else:
  113. value_wo_leading_zero = ('%.4f' % (filter_f[i-1])).lstrip('0')
  114. pstring += value_wo_leading_zero+', '
  115. # trim starting \n and trailing ,
  116. pstring = pstring.lstrip("\n").rstrip(", ")
  117. return pstring
  118. def write_cpp(bands, values, sensor, folder):
  119. """
  120. from bands, values and sensor name
  121. create output file in cpp style
  122. needs other functions: interpolate_bands, pretty_print
  123. """
  124. # getting necessary data
  125. # single or multiple bands?
  126. if len(bands) == 1:
  127. filter_f, limits = interpolate_band(values)
  128. else:
  129. filter_f = []
  130. limits = []
  131. for b in range(len(bands)):
  132. fi, li = interpolate_band(values[:,[0,b+1]])
  133. filter_f.append(fi)
  134. limits.append(li)
  135. # writing...
  136. outfile = open(os.path.join(folder, sensor+"_cpp_template.txt"), 'w')
  137. outfile.write('/* Following filter function created using create_iwave.py */\n\n')
  138. if len(bands) == 1:
  139. outfile.write('void IWave::%s()\n{\n\n' % (sensor.lower()))
  140. else:
  141. outfile.write('void IWave::%s(int iwa)\n{\n\n' % (sensor.lower()))
  142. # single band case
  143. if len(bands) == 1:
  144. outfile.write(' /* %s of %s */\n' % (bands[0], sensor))
  145. outfile.write(' static const float sr[%i] = {' % (len(filter_f)))
  146. filter_text = pretty_print(filter_f)
  147. outfile.write(filter_text)
  148. # calculate wl slot for band start
  149. # slots range from 250 to 4000 at 2.5 increments (total 1500)
  150. s_start = int((limits[0]*1000 - 250)/2.5)
  151. outfile.write('\n')
  152. outfile.write(' ffu.wlinf = %.4ff;\n' % (limits[0]))
  153. outfile.write(' ffu.wlsup = %.4ff;\n' % (limits[1]))
  154. outfile.write(' int i = 0;\n')
  155. outfile.write(' for(i = 0; i < %i; i++)\tffu.s[i] = 0;\n' % (s_start))
  156. outfile.write(' for(i = 0; i < %i; i++)\tffu.s[%i+i] = sr[i];\n' % (len(filter_f), s_start))
  157. outfile.write(' for(i = %i; i < 1501; i++)\tffu.s[i] = 0;\n' % (s_start + len(filter_f)))
  158. outfile.write('}\n')
  159. else: # more than 1 band
  160. # writing bands
  161. for b in range(len(bands)):
  162. outfile.write(' /* %s of %s */\n' % (bands[b], sensor))
  163. outfile.write(' static const float sr%i[%i] = {\n' % (b+1,len(filter_f[b])))
  164. filter_text = pretty_print(filter_f[b])
  165. outfile.write(filter_text+'\n };\n\t\n')
  166. # writing band limits
  167. for b in range(len(bands)):
  168. inf = ", ".join(["%.3f" % i[0] for i in limits])
  169. sup = ", ".join(["%.3f" % i[1] for i in limits])
  170. outfile.write(' static const float wli[%i] = {%s};\n' % (len(bands), inf))
  171. outfile.write(' static const float wls[%i] = {%s};\n' % (len(bands), sup))
  172. outfile.write('\n')
  173. outfile.write(' ffu.wlinf = (float)wli[iwa-1];\n')
  174. outfile.write(' ffu.wlsup = (float)wls[iwa-1];\n\n')
  175. outfile.write(' int i;\n')
  176. outfile.write(' for(i = 0; i < 1501; i++) ffu.s[i] = 0;\n\n')
  177. outfile.write(' switch(iwa)\n {\n')
  178. # now start of case part...
  179. for b in range(len(bands)):
  180. s_start = int((limits[b][0]*1000 - 250)/2.5)
  181. 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)))
  182. outfile.write(' break;\n')
  183. outfile.write(' }\n}\n')
  184. return
  185. def main():
  186. """ control function """
  187. inputfile = sys.argv[1]
  188. # getting sensor name from full csv file name
  189. sensor = os.path.splitext(os.path.basename(inputfile))[0]
  190. print "Getting sensor name from csv file: %s" % (sensor)
  191. # getting data from file
  192. bands, values = read_input(inputfile)
  193. # writing file in same folder of input file
  194. write_cpp(bands, values, sensor, os.path.dirname(inputfile))
  195. print "Filter function written to %s" % (sensor+"_cpp_template.txt")
  196. print "Please check file for possible errors before inserting into IWave.cpp"
  197. print "Don't forget to add necessary data to IWave.h"
  198. return
  199. if __name__ == '__main__':
  200. if len(sys.argv) == 1:
  201. usage()
  202. sys.exit()
  203. else:
  204. main()