class_KdJamet.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # -*- coding: utf-8 -*-
  2. # class for Jamet Neural Network Kd inversion.
  3. # Python version :
  4. # 2016-05-11 D.Dessailly david.dessailly@univ-littoral.fr
  5. # LOG (Laboratoire d'Oceanoligie et Geoscience)
  6. import numpy as np
  7. import sys
  8. import os
  9. Sensor = {'idSEAWIFS' : 0, 'idMODIS' : 1, 'idMERIS' : 2}
  10. NBWL = 6
  11. #
  12. # Class to handle Jamet's Kd processing for MERIS/OLCI, SEAWIFS and MODIS Sensor
  13. #
  14. class KdJamet:
  15. # -- default __init__ Method +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  16. # Input :
  17. # idSensor : valid values -> ['idSEAWIFS', 'idMODIS', 'idMERIS']
  18. # --
  19. def __init__(self, idSensor):
  20. self.sensor = idSensor
  21. try:
  22. self.lutpath = os.getenv('KD_LUTS_PATH')
  23. except:
  24. print "KD_LUTS_PATH not set, add it to your environment variables\n for bash user add the following line to your .bashrc file :\n export KD_LUTS_PATH=/where/are/my/KD/LUTS\n then:\n source ~/.bashrc"
  25. sys.exit()
  26. if idSensor in Sensor:
  27. #
  28. self.init_variables()
  29. try:
  30. self.read_LUTs_Kd()
  31. except:
  32. print "Failed to read %s LUTs" % (idSensor[2:])
  33. raise
  34. sys.exit()
  35. else:
  36. print "invalid Sensor ID : %s\n valid values : idSEAWIFS, idMODIS, idMERIS" % (idSensor)
  37. sys.exit()
  38. # -- METHOD init_variables ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  39. # Sensor dependants variables and Perceptron parameters setting
  40. # --
  41. def init_variables(self):
  42. print "class KdJamet.init_variables Method"
  43. # =======================================================================================================
  44. # MERIS / OLCI
  45. if self.sensor == 'idMERIS':
  46. self.WL = np.array([413, 443, 490, 510, 560, 665])
  47. # Variables definiton for network Rrs490/Rrs555 >= .85 -------------------------------------------------
  48. # hidden layer number
  49. self.rsup_NC=2
  50. # input data number
  51. self.rsup_NE=5
  52. # number of neuron in the first hidden layer
  53. self.rsup_NC1=7
  54. # number of neuron in the second hidden layer
  55. self.rsup_NC2=4
  56. # nb output
  57. self.rsup_NS=1
  58. # nb input + nb output*/
  59. self.rsup_NES=6
  60. # LUTs file names
  61. self.rsup_LUT_POIDS = "KdOLCI_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_555_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_7_4_switch_110416.sn"
  62. self.rsup_LUT_MOY = "Moy_KdOLCI_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_ss_620.dat"
  63. self.rsup_LUT_ECART = "Ecart_KdOLCI_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_ss_620.dat"
  64. # Variables definiton for network < .85 -----------------------------------
  65. # nombre de couches cachees - hidden layer number
  66. self.rinf_NC=2
  67. # input data number*/
  68. self.rinf_NE=6
  69. # number of neuron in the first hidden layer
  70. self.rinf_NC1=5
  71. # number of neuron in the second hidden layer
  72. self.rinf_NC2=5
  73. # nb output
  74. self.rinf_NS=1
  75. # nb input + nb output
  76. self.rinf_NES=7
  77. #LUTs file names
  78. self.rinf_LUT_POIDS = "KdOLCI_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_670_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_5_5_switch_110416.sn"
  79. self.rinf_LUT_MOY = "Moy_KdOLCI_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_ss_620.dat"
  80. self.rinf_LUT_ECART = "Ecart_KdOLCI_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_ss_620.dat"
  81. # =======================================================================================================
  82. # MODIS
  83. if self.sensor == 'idMODIS':
  84. self.WL = np.array([412, 443, 488, 531, 547, 667])
  85. # Variables definiton for network Rrs490/Rrs555 >= .85 -------------------------------------------------
  86. # hidden layer number
  87. self.rsup_NC=2
  88. # input data number
  89. self.rsup_NE=5
  90. # number of neuron in the first hidden layer
  91. self.rsup_NC1=5
  92. # number of neuron in the second hidden layer
  93. self.rsup_NC2=4
  94. # nb output
  95. self.rsup_NS=1
  96. # nb input + nb output*/
  97. self.rsup_NES=6
  98. # LUTs file names
  99. self.rsup_LUT_POIDS = "KdMODIS_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_555_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_5_4_publi_CSIRO.sn"
  100. self.rsup_LUT_MOY = "Moy_KdMODIS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_555_log_Kd_lambda_merge_seuil_15_ss_645_publi_sup_CSIRO.dat"
  101. self.rsup_LUT_ECART = "Ecart_KdMODIS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_555_log_Kd_lambda_merge_seuil_15_ss_645_publi_sup_CSIRO.dat"
  102. # Variables definiton for network < .85 -----------------------------------
  103. # nombre de couches cachees - hidden layer number
  104. self.rinf_NC=2
  105. # input data number*/
  106. self.rinf_NE=6
  107. # number of neuron in the first hidden layer
  108. self.rinf_NC1=10
  109. # number of neuron in the second hidden layer
  110. self.rinf_NC2=7
  111. # nb output
  112. self.rinf_NS=1
  113. # nb input + nb output
  114. self.rinf_NES=7
  115. #LUTs file names
  116. self.rinf_LUT_POIDS = "KdMODIS_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_670_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_10_7_publi_CSIRO.sn"
  117. self.rinf_LUT_MOY = "Moy_KdMODIS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_ss_645_publi_inf_CSIRO.dat"
  118. self.rinf_LUT_ECART = "Ecart_KdMODIS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_ss_645_publi_inf_CSIRO.dat"
  119. # =======================================================================================================
  120. # SEAWIFS
  121. if self.sensor == 'idSEAWIFS':
  122. self.WL = np.array([412, 443, 490, 510, 555, 670])
  123. # Variables definiton for network Rrs490/Rrs555 >= .85 -------------------------------------------------
  124. # hidden layer number
  125. self.rsup_NC=2
  126. # input data number
  127. self.rsup_NE=5
  128. # number of neuron in the first hidden layer
  129. self.rsup_NC1=4
  130. # number of neuron in the second hidden layer
  131. self.rsup_NC2=4
  132. # nb output
  133. self.rsup_NS=1
  134. # nb input + nb output*/
  135. self.rsup_NES=6
  136. # LUTs file names
  137. self.rsup_LUT_POIDS = "KdSeaWiFS_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_555_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_4_4_publi_200715.sn"
  138. self.rsup_LUT_MOY = "Moy_KdSeaWiFS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_160715.dat"
  139. self.rsup_LUT_ECART = "Ecart_KdSeaWiFS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_160715.dat"
  140. # Variables definiton for network < .85 -----------------------------------
  141. # nombre de couches cachees - hidden layer number
  142. self.rinf_NC=2
  143. # input data number*/
  144. self.rinf_NE=6
  145. # number of neuron in the first hidden layer
  146. self.rinf_NC1=5
  147. # number of neuron in the second hidden layer
  148. self.rinf_NC2=5
  149. # nb output
  150. self.rinf_NS=1
  151. # nb input + nb output
  152. self.rinf_NES=7
  153. #LUTs file names
  154. self.rinf_LUT_POIDS = "KdSeaWiFS_poids_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_443_to_670_log_Kd_lambda_merge_seuil_15_angle_ascii_6x1_hh_5_5_publi_160715_ter.sn"
  155. self.rinf_LUT_MOY = "Moy_KdSeaWiFS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_160715.dat"
  156. self.rinf_LUT_ECART = "Ecart_KdSeaWiFS_IOCCG_NOMAD_BOUM_ss_12_COASTCOLOUR_412_to_670_log_Kd_lambda_merge_seuil_15_angle_publi_160715.dat"
  157. # LUTs arrays definition
  158. self.rsup_b1 = np.zeros((self.rsup_NC1), dtype=np.float64)
  159. self.rsup_b2 = np.zeros((self.rsup_NC2), dtype=np.float64)
  160. self.rsup_b3 = 0.
  161. self.rsup_w1 = np.zeros((self.rsup_NE,self.rsup_NC1), dtype=np.float64)
  162. self.rsup_w2 = np.zeros((self.rsup_NC1,self.rsup_NC2), dtype=np.float64)
  163. self.rsup_w3 = np.zeros((self.rsup_NC2), dtype=np.float64)
  164. self.rsup_moy = np.zeros((self.rsup_NES), dtype=np.float64)
  165. self.rsup_ecart = np.zeros((self.rsup_NES), dtype=np.float64)
  166. self.rinf_b1 = np.zeros((self.rinf_NC1), dtype=np.float64)
  167. self.rinf_b2 = np.zeros((self.rinf_NC2), dtype=np.float64)
  168. self.rinf_b3 = 0.
  169. self.rinf_w1 = np.zeros((self.rinf_NE,self.rinf_NC1), dtype=np.float64)
  170. self.rinf_w2 = np.zeros((self.rinf_NC1,self.rinf_NC2), dtype=np.float64)
  171. self.rinf_w3 = np.zeros((self.rinf_NC2), dtype=np.float64)
  172. self.rinf_moy = np.zeros((self.rinf_NES), dtype=np.float64)
  173. self.rinf_ecart = np.zeros((self.rinf_NES), dtype=np.float64)
  174. # -- METHOD read_LUTs_Kd ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  175. # Perceptron LUTs reading
  176. # LUTS are expected to be located in the directory defined by environment variable $KD_LUTS_PATH (cf: __init__)
  177. # --
  178. def read_LUTs_Kd(self):
  179. print "class KdJamet.read_LUTs_Kd Method"
  180. # ----------------------------------------------------------------------------------
  181. # < .85 weights settings
  182. #print " KdJamet.read_LUTs_Kd : read weights file for Rrs%d/Rrs%d < .85: %s/%s" % (self.WL[2], self.WL[4],self.lutpath, self.rinf_LUT_POIDS)
  183. weights = np.loadtxt( "%s/%s" % (self.lutpath, self.rinf_LUT_POIDS), skiprows=1)
  184. base = 0
  185. self.rinf_b1 = weights[base:base+self.rinf_NC1,2]
  186. base = base+self.rinf_NC1
  187. self.rinf_b2 = weights[base:base+self.rinf_NC2,2]
  188. base = base+self.rinf_NC2
  189. self.rinf_b3 = weights[base:base+self.rinf_NS,2]
  190. base = base+self.rinf_NS
  191. for i in range(self.rinf_NE):
  192. self.rinf_w1[i,:] = weights[base:base+self.rinf_NC1,2]
  193. base = base+self.rinf_NC1
  194. for i in range(self.rinf_NC1):
  195. self.rinf_w2[i,:] = weights[base:base+self.rinf_NC2,2]
  196. base = base+self.rinf_NC2
  197. self.rinf_w3 = weights[base:base+self.rinf_NC2,2]
  198. usedData = np.array([1,2,3,4,5,6,8])
  199. if self.sensor == 'idMODIS':
  200. usedData = np.array([1,2,3,4,6,7,9])
  201. #print " KdJamet.read_LUTs_Kd : read mean file : %s/%s" % (self.lutpath, self.rinf_LUT_MOY)
  202. moy = np.loadtxt( "%s/%s" % (self.lutpath, self.rinf_LUT_MOY))
  203. self.rinf_moy = moy[usedData]
  204. #print " class KdJamet.read_LUTs_Kd : read ecart file : %s/%s" % (self.lutpath, self.rinf_LUT_ECART)
  205. ecart = np.loadtxt( "%s/%s" % (self.lutpath, self.rinf_LUT_ECART))
  206. self.rinf_ecart = ecart[usedData]
  207. # ----------------------------------------------------------------------------------
  208. # >= .85 weights settings
  209. #print " KdJamet.read_LUTs_Kd : read weights file for Rrs%d/Rrs%d >= .85: %s/%s" % (self.WL[2], self.WL[4], self.lutpath, self.rsup_LUT_POIDS)
  210. weights = np.loadtxt( "%s/%s" % (self.lutpath, self.rsup_LUT_POIDS), skiprows=1)
  211. base = 0
  212. self.rsup_b1 = weights[base:base+self.rsup_NC1,2]
  213. base = base+self.rsup_NC1
  214. self.rsup_b2 = weights[base:base+self.rsup_NC2,2]
  215. base = base+self.rsup_NC2
  216. self.rsup_b3 = weights[base:base+self.rsup_NS,2]
  217. base = base+self.rsup_NS
  218. for i in range(self.rsup_NE):
  219. self.rsup_w1[i,:] = weights[base:base+self.rsup_NC1,2]
  220. base = base+self.rsup_NC1
  221. for i in range(self.rsup_NC1):
  222. self.rsup_w2[i,:] = weights[base:base+self.rsup_NC2,2]
  223. base = base+self.rsup_NC2
  224. self.rsup_w3 = weights[base:base+self.rsup_NC2,2]
  225. usedData = np.array([1,2,3,4,6,8])
  226. if self.sensor == 'idMODIS':
  227. usedData = np.array([1,2,3,4,7,9])
  228. moy = np.loadtxt( "%s/%s" % (self.lutpath, self.rsup_LUT_MOY))
  229. self.rsup_moy = moy[usedData]
  230. ecart = np.loadtxt( "%s/%s" % (self.lutpath, self.rsup_LUT_ECART))
  231. self.rsup_ecart = ecart[usedData]
  232. # -- METHOD compute_allKd ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  233. # Kd computing at all Sensor WaveLength (vary for each sensor)
  234. # Inputs -
  235. # - indata : array[] [ Rrs443, Rrs490, Rrs510, Rrs555, Rrs665 ]
  236. # Output -
  237. # - return Kd[] at Sensor Wave Length
  238. # --
  239. def compute_allKd(self, inRrs):
  240. outKd = np.zeros(NBWL,dtype=np.float32)
  241. if inRrs[2] / inRrs[4] < .85 :
  242. indata = np.zeros(self.rinf_NE,dtype=np.float32)
  243. indata[0:self.rinf_NE-1] = inRrs[1:self.rinf_NE]
  244. idLambda = self.rinf_NE-1
  245. FLAGinf = True
  246. else:
  247. indata = np.zeros(self.rsup_NE,dtype=np.float32)
  248. indata[0:self.rsup_NE-1] = inRrs[1:self.rsup_NE]
  249. idLambda = self.rsup_NE-1
  250. FLAGinf = False
  251. inc = 0
  252. # compute Kd for each Wave Length of sensor used as iput
  253. for wl in self.WL:
  254. indata[idLambda] = wl
  255. if FLAGinf == True:
  256. outKd[inc] = self.passe_avant_inf(indata)
  257. else:
  258. outKd[inc] = self.passe_avant_sup(indata)
  259. inc = inc+1
  260. return outKd
  261. # -- METHOD passe_avant_sup ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  262. # Kd computing for Rrs490/Rrs55 >= .85 at ONE WaveLength
  263. # Inputs -
  264. # - indata : array[rinf_NE] [ Rrs443, Rrs490, Rrs510, Rrs55, Lambda ]
  265. # Output -
  266. # - return y (Kd) at indata Wave Length {the wave length can be any value between min and max Wave length
  267. # used for the sensor }
  268. # --
  269. def passe_avant_sup(self, indata):
  270. # Normalization
  271. x = ((2/3.) * (indata[:] - self.rsup_moy[0:self.rsup_NE])) / self.rsup_ecart[0:self.rsup_NE]
  272. a = np.zeros(self.rsup_NC1,dtype=np.float64)
  273. for i in range(self.rsup_NC1):
  274. for j in range(self.rsup_NE):
  275. a[i] = a[i] + x[j] * self.rsup_w1[j][i]
  276. a[:] = 1.715905*np.tanh((2./3.)*(a[:] + self.rsup_b1[:]))
  277. b = np.zeros(self.rsup_NC2,dtype=np.float64)
  278. for i in range(self.rsup_NC2):
  279. for j in range(self.rsup_NC1):
  280. b[i] = b[i] + a[j] * self.rsup_w2[j][i]
  281. b[:] = 1.715905*np.tanh((2./3.)*(b[:] + self.rsup_b2[:]))
  282. y = np.sum(b[:] * self.rsup_w3[:])
  283. # Denormalisation
  284. y = 1.5*(y + self.rsup_b3)*self.rsup_ecart[self.rsup_NES-1] + self.rsup_moy[self.rsup_NES-1]
  285. y = 10.**y
  286. return(y)
  287. # -- METHOD passe_avant_inf ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  288. # Kd computing for Rrs490/Rrs55 < .85 at ONE WaveLength
  289. # Inputs
  290. # - indata : array[rinf_NE] [ Rrs443, Rrs490, Rrs510, Rrs55, Rrs665, Lambda ]
  291. # Output -
  292. # - return y (Kd) at indata Wave Length {the wave length can be any value between min and max Wave length
  293. # used for the sensor }
  294. # --
  295. def passe_avant_inf(self, indata):
  296. # Normalization
  297. x = ((2/3.) * (indata[:] - self.rinf_moy[0:self.rinf_NE])) / self.rinf_ecart[0:self.rinf_NE]
  298. a = np.zeros(self.rinf_NC1,dtype=np.float64)
  299. for i in range(self.rinf_NC1):
  300. for j in range(self.rinf_NE):
  301. a[i] = a[i] + x[j] * self.rinf_w1[j][i]
  302. a[:] = 1.715905*np.tanh((2./3.)*(a[:] + self.rinf_b1[:]))
  303. b = np.zeros(self.rinf_NC2,dtype=np.float64)
  304. for i in range(self.rinf_NC2):
  305. for j in range(self.rinf_NC1):
  306. b[i] = b[i] + a[j] * self.rinf_w2[j][i]
  307. b[:] = 1.715905*np.tanh((2./3.)*(b[:] + self.rinf_b2[:]))
  308. y = np.sum(b[:] * self.rinf_w3[:])
  309. # Denormalisation
  310. y = 1.5*(y + self.rinf_b3)*self.rinf_ecart[self.rinf_NES-1] + self.rinf_moy[self.rinf_NES-1]
  311. y = 10.**y
  312. return(y)