diff --git a/PHYMOBAT.py b/PHYMOBAT.py index 3a4db08e17f6e22d364b7e263d02ca329198f5dc..972037145e9c508d8547395aa44bb04e84060495 100644 --- a/PHYMOBAT.py +++ b/PHYMOBAT.py @@ -42,7 +42,6 @@ except : import webbrowser import lxml.etree as ET -import os.path import Popup from Processing import Processing @@ -81,8 +80,8 @@ class PHYMOBAT(QtWidgets.QMainWindow, Processing): # Chargement et lancement automatique pour les tests à supprimer # self.open_backup("/media/commandre/8ce2dd88-1425-4161-a3c1-47f566c0285a/data_test/2018.xml") - self.open_backup("/media/commandre/8ce2dd88-1425-4161-a3c1-47f566c0285a/Gironde_Test/gironde_total.xml") - self.ok_button() + # self.open_backup("/media/commandre/8ce2dd88-1425-4161-a3c1-47f566c0285a/Gironde_Test/gironde_total.xml") + # self.ok_button() def initUI(self): @@ -881,6 +880,7 @@ class PHYMOBAT(QtWidgets.QMainWindow, Processing): # Another check box to launch VHRS texture processing. If checked, vs = 1. if self.ui.checkBox_VHRS.isChecked(): vs = 1 + self.i_images_processing(vs) # function to launch the image processing # To launch texture processing only @@ -934,7 +934,7 @@ class PHYMOBAT(QtWidgets.QMainWindow, Processing): # Compute a output slope raster if self.path_mnt : self.i_slope() - + # Save the sample features self.add_sample() @@ -975,12 +975,12 @@ class PHYMOBAT(QtWidgets.QMainWindow, Processing): # in_backup = QtWidgets.QFileDialog.getOpenFileName(self, "Open backup", os.getcwd(), '*.xml')[0] - if test is None : + if test is False : in_backup = QtWidgets.QFileDialog.getOpenFileName(self, "Open backup", os.getcwd(), '*.xml')[0] else : in_backup = test - if in_backup != None and len(in_backup) > 0 : + if in_backup and len(in_backup) > 0 : # Parse the xml file if the user choose a file tree = ET.parse(str(in_backup)) diff --git a/Processing.py b/Processing.py index 7d10993bc9bb55bdfc6153e841efc0c2dc6570be..96a26699a773222b7a5a621185f182dcc83c50f7 100644 --- a/Processing.py +++ b/Processing.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with PHYMOBAT 2.0. If not, see <http://www.gnu.org/licenses/>. -import os, sys, math +import os, sys, math, psutil, glob import numpy as np import subprocess from sklearn.ensemble import RandomForestClassifier @@ -281,8 +281,6 @@ class Processing(object): check_L8.mosaic_by_date(date) # Search cloud's percentage, select image and compute ndvi index - # if cloud_pourcentage < CLOUD_THRESHOLD - if check_L8.pourc_cloud() < Constantes.CLOUD_THRESHOLD : tmp = date check_L8.calcul_ndvi() @@ -292,25 +290,36 @@ class Processing(object): # Compute temporal stats on ndvi index [min, max, std, min-max] spectral_trans = np.transpose(np.array(spectral_out, dtype=object)) - stats_name = ['Min', 'Date', 'Max', 'Std', 'MaxMin'] - stats_ndvi, stats_cloud = current_list.calc_serie_stats(spectral_trans) + del spectral_out + + # stats_name = ['Min', 'Date', 'Max', 'Std', 'MaxMin'] + stats_name = ['Min', 'Max'] + path_stat = current_list.calc_serie_stats(spectral_trans, stats_name, self.folder_processing) + + for idx, i in enumerate(stats_name): + self.out_ndvistats_folder_tab[i] = path_stat[idx] - # Stats cloud raster - stats_cloud_raster = "{0}/Cloud_number_{1}.TIF".format(self.folder_processing, self.captor_project) + # self.logger.debug(self.out_ndvistats_folder_tab) + # raise(BaseException("Tst")) - check_L8.complete_raster(\ - *check_L8.create_empty_raster(stats_cloud_raster, stats_cloud,\ - check_L8.raster_data(self.check_download.list_img[0][4])[1]),\ - stats_cloud) + # stats_ndvi, stats_cloud = current_list.calc_serie_stats(spectral_trans, self.folder_processing) - # Stats ndvi rasters - for stats_index in range(len(stats_ndvi)): - out_ndvistats_raster = "{0}/{1}_{2}.TIF".format(self.folder_processing, stats_name[stats_index], self.captor_project) - self.out_ndvistats_folder_tab[stats_index] = out_ndvistats_raster - check_L8.complete_raster(\ - *check_L8.create_empty_raster(out_ndvistats_raster, stats_ndvi[stats_index],\ - check_L8.raster_data(self.check_download.list_img[0][4])[1]),\ - stats_ndvi[stats_index]) + # # Stats cloud raster + # stats_cloud_raster = "{0}/Cloud_number_{1}.TIF".format(self.folder_processing, self.captor_project) + + # check_L8.complete_raster(\ + # *check_L8.create_empty_raster(stats_cloud_raster, stats_cloud,\ + # check_L8.raster_data(self.check_download.list_img[0][4])[1]),\ + # stats_cloud) + + # # Stats ndvi rasters + # for stats_index in range(len(stats_ndvi)): + # out_ndvistats_raster = "{0}/{1}_{2}.TIF".format(self.folder_processing, stats_name[stats_index], self.captor_project) + # self.out_ndvistats_folder_tab[stats_index] = out_ndvistats_raster + # check_L8.complete_raster(\ + # *check_L8.create_empty_raster(out_ndvistats_raster, stats_ndvi[stats_index],\ + # check_L8.raster_data(self.check_download.list_img[0][4])[1]),\ + # stats_ndvi[stats_index]) check_L8.logger.close() current_list.logger.close() @@ -350,11 +359,12 @@ class Processing(object): current_path_ortho.image = self.path_ortho current_path_ortho.vect = self.path_area current_path_ortho.output = "{0}/MosaiqueOrtho/Clip_{1}".format(self.folder_processing, os.path.basename(self.path_ortho)) - + + self.path_ortho = current_path_ortho.output path_ortho = current_path_ortho.clip_raster() texture_irc = Vhrs(path_ortho, self.mp) - self.out_ndvistats_folder_tab['sfs'] = texture_irc.out_sfs + self.out_ndvistats_folder_tab['sfs'] = texture_irc.out_sfs self.out_ndvistats_folder_tab['haralick'] = texture_irc.out_haralick current_path_ortho.logger.close() @@ -369,37 +379,34 @@ class Processing(object): ######################### Multiprocessing ? ######################### + # self.i_vhrs() + # sys.exit(0) + mgr = BaseManager() mgr.register('defaultdict', defaultdict, DictProxy) mgr.start() - self.out_ndvistats_folder_tab = mgr.defaultdict(list) - - self.i_vhrs() - self.i_img_sat() + self.out_ndvistats_folder_tab = mgr.defaultdict(list) - - # p_img_sat = Process(target=self.i_img_sat) - # p_vhrs = Process(target=self.i_vhrs) + p_img_sat = Process(target=self.i_img_sat) + p_vhrs = Process(target=self.i_vhrs) - # self.logger.debug("Multiprocessing : {0}".format(self.mp)) + self.logger.debug("Multiprocessing : {0}".format(self.mp)) - # if self.mp == Constantes.MULTIPROCESSING_DISABLE: - # p_img_sat.start() - # p_img_sat.join() - # p_vhrs.start() - # p_vhrs.join() - # else : - # p_img_sat.start() - # p_vhrs.start() - # p_img_sat.join() - # p_vhrs.join() - - # raise('i_images_processing') + if self.mp == Constantes.MULTIPROCESSING_DISABLE: + p_img_sat.start() + p_img_sat.join() + p_vhrs.start() + p_vhrs.join() + else : + p_img_sat.start() + p_vhrs.start() + p_img_sat.join() + p_vhrs.join() ################################################################### - self.raster_path["Min"]["PATH"] = self.out_ndvistats_folder_tab[0] + self.raster_path["Min"]["PATH"] = self.out_ndvistats_folder_tab['Min'] self.raster_path["Min"]["BAND"] = 1 for i in range(1,7) : @@ -415,7 +422,7 @@ class Processing(object): self.raster_path["MNT"]["PATH"] = self.path_mnt self.raster_path["MNT"]["BAND"] = 1 - self.raster_path["MAX"]["PATH"] = self.out_ndvistats_folder_tab[2] + self.raster_path["MAX"]["PATH"] = self.out_ndvistats_folder_tab['Max'] self.raster_path["MAX"]["BAND"] = 1 self.logger.info("End of images processing !") @@ -566,10 +573,10 @@ class Processing(object): # Add the validation shapefile self.valid_shp.append([sample_rd[sple].vector_val, kwargs['fieldname'], kwargs['class']]) - self.logger.debug(self.valid_shp) - items = list(self.raster_path.items()) + self.logger.debug("Raster path : {0}".format(self.raster_path.items())) + for lbo in range(len(self.raster_path)): self.logger.debug("lbo : {0}".format(lbo)) kwargs['rank'] = lbo @@ -581,6 +588,9 @@ class Processing(object): # To convert the dictionnary in a list # for key, value in sample_rd[sple].stats_dict.iteritems(): for key, value in sample_rd[sple].stats_dict.items(): + + print("{0} : {1}".format(key, value)) + # X_rf.append([-10000 if (math.isnan(x) or math.isinf(x)) else x for x in value]) # To set the grassland class of the RPG and PIAO (same class) if sple == 2 : @@ -662,15 +672,19 @@ class Processing(object): # Stats backup file file_stats = "{0}/Stats_raster_spectral_texture.stats".format(os.path.dirname(items[ind_th][1]["PATH"])) - if not os.path.exists(file_stats): - exist_stats = 0 # The sats file doesn't exist + """ + TODO : Regarder si le fichier stats contient les infos pas seulement + tester l'existence + """ + if not os.path.exists(file_stats) : # or True: + exist_stats = 0# The sats file doesn't exist # Open a stats backup to avoid computing again (Gain of time) f_out = open(file_stats, "w") p = [] kwargs = {} X_out_rf = [] # Variable list to compute decision tree with random forest method - if exist_stats == 0: + if exist_stats == 0 : out_carto.stats_dict = mgr.defaultdict(list) for i in range(len(multi_process_var)) : @@ -704,7 +718,7 @@ class Processing(object): index_in_stats = index_in_stats + 1 out_carto.stats_dict[index_in_stats] = eval(x_in.strip('\n')) X_out_rf.append(eval(x_in.strip('\n'))) - + predicted_rf = self.rf.predict(X_out_rf) self.logger.debug("Taille sample_name : {0}".format(len(self.sample_name))) @@ -756,7 +770,10 @@ class Processing(object): rpg_tif.clip_vector(os.path.dirname(self.sample_name[0])) rpg_tif.vector_data() + # raise(BaseException("T2")) + kwargs['choice_nb_b'] = 1 + self.logger.debug(self.path_ortho) out_carto.stats_rpg_tif = out_carto.zonal_stats_pp(rpg_tif.layer_rasterization(self.path_ortho, 'CODE_GROUP', **kwargs)) # Final cartography @@ -920,7 +937,7 @@ class Processing(object): opt['class'] = str(class_validate) # Add integer classes # Set the new shapefile - val[0] = val[0][:-4] + '_.shp' + val[0] = "{0}_.shp".format(val[0][:-4]) val[1] = opt['fieldname'] val[2] = opt['class'] @@ -951,7 +968,6 @@ class Processing(object): valid.ex_raster = list(self.raster_path.items())[0][1]["PATH"] # TODO: Call the RasterSat_by_Date class here instead of the Precision_moba class - self.logger.debug("Name moba : {0}".format(self.output_name_moba)) valid.preprocess_to_raster_precision(self.output_name_moba, 'FBPHY_CODE') # To the classification's data diff --git a/RasterSat_by_date.py b/RasterSat_by_date.py index 84ee4ab2f4a2601030d94b34b614e4c5dc02eb13..d87702ca8100c4e0c9eb5d54a3a1632fa4036e73 100644 --- a/RasterSat_by_date.py +++ b/RasterSat_by_date.py @@ -21,6 +21,7 @@ import os, sys import math, subprocess import osgeo.gdal +import otbApplication as otb import Outils import Satellites @@ -179,7 +180,8 @@ class RasterSat_by_date(object): def raster_data(self, img): """ Function to extract raster information. - Return table of pixel values and raster information like line number, pixel size, ... (gdal pointer) + Return table of pixel values and raster information like + line number, pixel size, ... (gdal pointer) @param img : Raster path @type img : str @@ -192,7 +194,7 @@ class RasterSat_by_date(object): # Load Gdal's drivers osgeo.gdal.AllRegister() - self.logger.debug("Img : {0}".format(img)) + self.logger.debug("Image : {0}".format(img)) # Loading input raster self.logger.info('Loading input raster : {0}'.format(os.path.basename(img))) @@ -214,16 +216,19 @@ class RasterSat_by_date(object): # Table's declaration data = [] + for band in range(nbband): canal = in_ds.GetRasterBand(band + 1) # Select a band if nbband == 1: - data = canal.ReadAsArray(0, 0, cols, rows).astype(np.float32) # Assign pixel values at th0e data + # Assign pixel values at the data + data = canal.ReadAsArray(0, 0, cols, rows).astype(np.float16) else: - data.append(canal.ReadAsArray(0, 0, cols, rows).astype(np.float32)) + data.append(canal.ReadAsArray(0, 0, cols, rows).astype(np.float16)) ################################### + # Close input raster _in_ds = in_ds in_ds = None @@ -250,7 +255,7 @@ class RasterSat_by_date(object): data_cloud, info_cloud = self.raster_data(self.cloudiness_pourcentage[1]) # Add cloud pixel value table - self.cloudiness_pourcentage.append(data_cloud) + # self.cloudiness_pourcentage.append(data_cloud) # Extent of the images : # ex : array([ True, True, True, True, False, True, True, True, True], dtype=bool) @@ -273,44 +278,39 @@ class RasterSat_by_date(object): dist = np.sum(cloud) # Computer cloud's percentage with dist (sum of cloud) by sum of the image's extent - try : - nb0 = float(dist)/(np.sum(mask_spec)) - self.logger.info('For {0}, cloudy cover = {1}%'.format(os.path.basename(self.cloudiness_pourcentage[0]), 100 - round(nb0*100, 2) ) ) - except ZeroDivisionError: - nb0 = 0 - self.logger.info("The raster isn\'t in the area !") + nb0 = float(dist)/(np.sum(mask_spec)) if np.sum(mask_spec) != 0 else 0 + self.logger.info('For {0}, cloudy cover = {1}%'.format(os.path.basename(self.cloudiness_pourcentage[0]), 100 - round(nb0*100, 2))) - return 1.0-nb0 + return 1.0 - nb0 def calcul_ndvi(self): """ - Computer NDVI index for a Landsat image. - - NDVI = (PIR - R) / (PIR + R) + Function to compute NDVI index for a Landsat image with OTB BandMathX + - OTB help : + * il : Images list : data and cloud + * exp : Expression for ndvi : (PIR-R)/(PIR+R) + * out : Feature Output Image """ - np.seterr(invalid='ignore') - - # Extract raster's information - data, in_ds = self.raster_data(self.cloudiness_pourcentage[0]) - data_cloud, in_ds_cloud = self.raster_data(self.cloudiness_pourcentage[1]) - canal_PIR = Satellites.SATELLITE[self._class_archive._captor]["PIR"] canal_R = Satellites.SATELLITE[self._class_archive._captor]["R"] - # Computer NDVI - mask = np.greater(data_cloud, -10000) - - # If True, -10000 (NaN) else compute mathematical operation - ndvi = np.choose(mask, (-10000, eval('(data[canal_PIR]-data[canal_R])/(data[canal_PIR]+data[canal_R])'))) - - # Outfile name img_ndvi = "{0}_ndvi.TIF".format(self.cloudiness_pourcentage[0][:-4]) self.cloudiness_pourcentage.append(img_ndvi) # Add ndvi image path - self.cloudiness_pourcentage.append(ndvi) # Add ndvi pixel value table - self.create_empty_raster(img_ndvi, ndvi, in_ds) + otb_MathBand = otb.Registry.CreateApplication("BandMathX") + otb_MathBand.SetParameterStringList("il", [self.cloudiness_pourcentage[0], self.cloudiness_pourcentage[1]]) + # otb_MathBand.SetParameterString("exp",\ + # "(im1b1 > -1000 ? (im2b1 == 0 ? (im1b{0}-im1b{1})/(im1b{0}+im1b{1}) : -1000) : im1b1)"\ + # .format(canal_PIR, canal_R)) + otb_MathBand.SetParameterString("exp",\ + "(im1b1 > -1 ? (im2b1 == 0 ? ndvi(im1b{1}, im1b{0}) : -1) : im1b1)"\ + .format(canal_PIR, canal_R)) + otb_MathBand.SetParameterString("out", img_ndvi) + + otb_MathBand.ExecuteAndWriteOutput() + def create_empty_raster(self, out_raster_path, data, in_ds): """ @@ -330,8 +330,10 @@ class RasterSat_by_date(object): int -- variable **nbband**, Band number of the out layer. int -- variable **e**, Index to know if the raster exists. - If it doesn't exists e = 0 else e = 1 (by default). + If it doesn't exists e = 0 else e = 1 (by default). """ + + # raise(BaseException("TEST")) e = 1 # Raster out exists by default @@ -349,6 +351,7 @@ class RasterSat_by_date(object): if not os.path.exists(out_raster_path): e = 0 + # Create outfile self.out_ds = driver.Create(out_raster_path, in_ds.RasterXSize, in_ds.RasterYSize, nbband, osgeo.gdal.GDT_Float32) @@ -418,7 +421,7 @@ class RasterSat_by_date(object): # Closing and statistics on output raster out_band.FlushCache() - out_band.SetNoDataValue(-10000) + # out_band.SetNoDataValue(-10000) out_band.GetStatistics(-1, 1) out_band = None diff --git a/Sample.py b/Sample.py index cb2d2f12d259bd6f029d8c00ce97afa3388d5f1c..b0ac80a79d00c382eee8a752b310c1b5859f69d8 100644 --- a/Sample.py +++ b/Sample.py @@ -82,6 +82,13 @@ class Sample(Vector): if not os.path.exists(self.output_folder) : os.mkdir(self.output_folder) + + """ + TODO : Enlever random.seed(0) pour retourner en aléatoire + """ + + random.seed(0) + # If the users want select polygons with a certain class name if kw_field and kw_classes: # The random sample with class name selected only @@ -205,7 +212,7 @@ class Sample(Vector): sys.exit(1) # Specific output layer - out_layer = out_ds.CreateLayer(str(output_sample), srsObj, geom_type=ogr.wkbMultiPolygon) + out_layer = out_ds.CreateLayer(output_sample, srsObj, geom_type=ogr.wkbMultiPolygon) # Add existing fields for i in range(0, len(self.field_names)): @@ -229,7 +236,8 @@ class Sample(Vector): in_feature = shp_ogr.SetNextByIndex(cnt) in_feature = shp_ogr.GetNextFeature() - geom = in_feature.GetGeometryRef() # Extract input geometry + # Extract input geometry + geom = in_feature.GetGeometryRef() # Create a new polygon out_feature = ogr.Feature(featureDefn) @@ -237,11 +245,15 @@ class Sample(Vector): # Set the polygon geometry and attribute out_feature.SetGeometry(geom) for i in range(0, len(self.field_names)): - out_feature.SetField(self.field_names[i], in_feature.GetField(self.field_names[i])) + out_feature.SetField(self.field_names[i],\ + float("%.5f" % in_feature.GetField(self.field_names[i])) \ + if type(in_feature.GetField(self.field_names[i])) is float \ + else in_feature.GetField(self.field_names[i])) + # In Option : Add a integer field if add_field == 1: out_feature.SetField(opt_field, opt_class[0]) - + # Append polygon to the output shapefile out_layer.CreateFeature(out_feature) diff --git a/Segmentation.py b/Segmentation.py index de9a55507e205f55989117c7e4af9d5050341450..2ac1c1194fc68f616f48485b49633bbe743f6c7e 100644 --- a/Segmentation.py +++ b/Segmentation.py @@ -152,6 +152,7 @@ class Segmentation(Vector): in_feature = shp_ogr.SetNextByIndex(0) # Polygons initialisation in_feature = shp_ogr.GetNextFeature() + self.logger.debug(self.stats_rpg_tif) # Loop on input polygons while in_feature: @@ -170,6 +171,7 @@ class Segmentation(Vector): # Set the RPG column recouv_crops_RPG = 0 pourc_inter = self.stats_rpg_tif[in_feature.GetFID()]['Maj_count_perc'] + if pourc_inter >= 85: recouv_crops_RPG = self.stats_rpg_tif[in_feature.GetFID()]['Maj_count'] @@ -292,22 +294,14 @@ class Segmentation(Vector): distri_bio = [] distri_den = [] - - # for b in distri: for idx, b in enumerate(distri): if self.out_threshold["PREDICTION"][idx] in [1,2,8,9,10]\ - and b[-1] != -10000 and b[-1] < 1: + and b[-1] is not None and b[-1] != -10000 and b[-1] < 1: distri_bio.append(b) else: distri_den.append(b) - - self.logger.debug(len(distri)) - self.logger.debug(len(distri_bio)) - self.logger.debug(len(distri_den)) - raise(BaseException("Error")) - # Transpose table t_distri_bio = list(map(list, zip(*distri_bio))) t_distri_den = list(map(list, zip(*distri_den))) @@ -316,16 +310,21 @@ class Segmentation(Vector): stdmore = (np.mean(t_distri_bio[2]) + np.std(t_distri_bio[2])) / np.max(t_distri_bio[2]) stdless = (np.mean(t_distri_bio[2]) - np.std(t_distri_bio[2])) / np.max(t_distri_bio[2]) - # self.out_threshold.append('<'+str(stdmore)) - # self.out_threshold.append('') - # self.out_threshold.append('>'+str(stdless)) self.out_threshold["STDMORE"] = stdmore self.out_threshold["STDLESS"] = stdless # Compute density and biomass maximum - self.max_wood_idm = np.max(t_distri_den[1]) - self.max_wood_sfs = np.max(t_distri_den[0]) - self.max_bio = np.max(t_distri_bio[2]) + wood_sfs = np.array(t_distri_den[0], dtype=np.float64) + wood_idm = np.array(t_distri_den[1], dtype=np.float64) + max_bio = np.array(t_distri_den[2], dtype=np.float64) + + self.max_wood_sfs = np.nanmax(wood_sfs) + self.max_wood_idm = np.nanmax(wood_idm) + self.max_bio = np.nanmax(max_bio) + + # self.max_wood_sfs = np.max(t_distri_den[0]) + # self.max_wood_idm = np.max(t_distri_den[1]) + # self.max_bio = np.max(t_distri_bio[2]) def append_scale(self, select_class, form): """ @@ -342,7 +341,7 @@ class Segmentation(Vector): for ind_stats in range(len(self.stats_dict)): # Only valid on the second level try: - if self.class_tab_final[ind_stats][1] == select_class: + if self.class_tab_final[ind_stats][1] == select_class and self.stats_dict[ind_stats][3] is not None: self.class_tab_final[ind_stats].insert(len(self.class_tab_final[ind_stats])-2,eval(form)) # To add phytomasse try : diff --git a/Toolbox.py b/Toolbox.py index e5ccfc9012a239465e7f04dc6e4ef52534a2dbaf..8093cee1fb748c63bfe6043195bad2c14361e080 100644 --- a/Toolbox.py +++ b/Toolbox.py @@ -19,7 +19,9 @@ from datetime import date -import os, sys, subprocess, glob +import otbApplication as otb + +import os, sys, subprocess, glob, gc, psutil import numpy as np import json import osgeo.gdal @@ -77,10 +79,10 @@ class Toolbox(object): return outclip - def calc_serie_stats(self, table): + def calc_serie_stats(self, table, stats_name, output_folder): """ Function to compute stats on temporal cloud and ndvi spectral table - Ndvi stats : min max std max-min + Ndvi stats : min max @param table: Spectral data, cloud raster and ndvi raster @type table: numpy.ndarray @@ -90,68 +92,37 @@ class Toolbox(object): numpy.ndarray -- variable **account_cloud**, pixel number clear on the area. """ - # Compute stats on these indexes - ind = ['np.min(tab_ndvi_masked, axis=2)', 'np.max(tab_ndvi_masked, axis=2)', 'np.std(tab_ndvi_masked, axis=2)', \ - 'np.max(tab_ndvi_masked, axis=2)-np.min(tab_ndvi_masked, axis=2)'] # [Min, Max, Std, Max-Min] + ind = ['min({0})', 'max({0})'] + no_data_value = [99999, -10000] + + stack_ndvi = list(table[5]) + image = "im{0}b1" - # table = ['Année', 'Mois', 'Jour', chemin image', 'chemin image nuage', - # 'données nuage', 'chemin image ndvi', 'données ndvi' ] + otb_MathBand = otb.Registry.CreateApplication("BandMathX") + otb_MathBand.SetParameterStringList("il", stack_ndvi) - # For the cloud map - - # In the input table the cloud floor is the 6th - tab_cloud = np.dstack(table[5]) # Stack cloud table (dimension : 12*X*Y to X*Y*12) - - # if tab_cloud = 0 then True else False / Mask cloud - cloud_true = (tab_cloud == 0) + path_stats = [] - self.logger.debug('Mémoire cloud_true : {0}'.format(sys.getsizeof(cloud_true))) + for idx, i in enumerate(ind): - # Account to tab_cloud if != 0 => Sum of True. (Dimension X*Y) - account_cloud = np.sum(cloud_true, axis=2) + expression = "({0} != {1} ? {0} : im1b1)".format(\ + i.format(",".join("({0}>-1000 ? {0} : {1})".format(\ + image.format(j+1), no_data_value[idx]) for j in range(len(stack_ndvi)))), no_data_value[idx]) - cloud_true = None + out_ndvistats_raster = "{0}/{1}.TIF".format(output_folder, stats_name[idx]) - self.logger.debug('Mémoire cloud_true : {0}'.format(sys.getsizeof(cloud_true))) - - # For the ndvi stats + path_stats.append(out_ndvistats_raster) - # In the input table the ndvi floor is the 8th - stack_ndvi = np.dstack(table[7]) # Like cloud table, stack ndvi table + otb_MathBand.SetParameterString("exp",expression) + otb_MathBand.SetParameterString("out", out_ndvistats_raster) - # Mask values -10000 and > 0 (Not cloud) - mask_cloud = np.ma.masked_where((stack_ndvi == -10000) | (tab_cloud != 0), tab_cloud) + otb_MathBand.SetParameterOutputImagePixelType("out", otb.ImagePixelType_float) - # Ndvi table with clear values - tab_ndvi_masked = np.ma.array(stack_ndvi, mask=mask_cloud.mask) - - # Stats on the indexes defined above - account_stats = [] + otb_MathBand.ExecuteAndWriteOutput() - for i in ind: - i_stats = eval(i) # Compute stats - i_stats.fill_value = -10000 # Substitute default fill value by -10000 - account_stats.append(i_stats.filled()) # Add stats table with true fill value - - # To extract index of the minimum ndvi to find the date - if ind.index(i) == 0: - i_date = eval(i.replace('np.min','np.argmin')) - - # To create a matrix with the date of pxl with a ndvi min - tab_date = np.transpose(np.array(table[:3], dtype=object)) - - # Loop on the temporal sequency - for d in range(len(tab_date)): - mask_data = np.ma.masked_values(i_date, d) - i_date = mask_data.filled(date(int(tab_date[d][0]), int(tab_date[d][1]), int(tab_date[d][2])).toordinal()) # Date = day for year =1 day = 1 and month = 1 - mask_data = None - - account_stats.append(i_date) # Add the date table + return path_stats - mask_cloud = None - tab_ndvi_masked = None - return account_stats, account_cloud def check_proj(self): """ diff --git a/Vector.py b/Vector.py index 4043d2151cfd60e10bb83a3aff6e2f2d9a072319..8725f7c125c5dee6f4933c008165c743c4e814db 100644 --- a/Vector.py +++ b/Vector.py @@ -21,6 +21,8 @@ import os, sys import subprocess import numpy as np import osgeo +from rpy2 import robjects +import time # try : # import ogr, gdal @@ -28,6 +30,7 @@ import osgeo # from osgeo import ogr, gdal import rasterstats +import otbApplication as otb from collections import * import Outils from RasterSat_by_date import RasterSat_by_date @@ -138,17 +141,33 @@ class Vector(): :param (inraster,band): inraster -> Input image path, and band -> band number :type (inraster,band): tuple + :kwargs: **rank** (int) - Zonal stats ranking launch **nb_img** (int) - Number images launched with zonal stats """ + ranking = kwargs['rank'] if kwargs.get('rank') else 0 nb_img = kwargs['nb_img'] if kwargs.get('nb_img') else 1 + time1 = time.time() self.logger.info('Compute {0} stats on {1} band {2}'.format(os.path.split(self.vector_used)[1], os.path.split(inraster)[1], band)) - stats = rasterstats.zonal_stats(self.vector_used, inraster, stats =['mean'], nodata=float('nan'), band=band) + zonal_stats = otb.Registry.CreateApplication("ZonalStatistics") + zonal_stats.SetParameterString("in", inraster) + zonal_stats.SetParameterString("vec", self.vector_used) + zonal_stats.SetParameterInt("band", band-1) + zonal_stats.SetParameterInt("ram", 128) + statistique = ["mean"] + zonal_stats.SetParameterStringList("stats", statistique) + + zonal_stats.Execute() + + stats = list(zonal_stats.GetParameterValue("out")) + + # stats = rasterstats.zonal_stats(self.vector_used, inraster, stats =['mean'], nodata=float('nan'), band=band) + for i in range(len(stats)): temp = defaultdict(lambda : [0]*nb_img) for j in range(nb_img): @@ -157,10 +176,15 @@ class Vector(): except IndexError: pass - temp[0][ranking] = list(stats)[i]['mean'] + # temp[0][ranking] = list(stats)[i]['mean'] + temp[0][ranking] = float(stats[i]) self.stats_dict[i] = temp[0] - - self.logger.info('End of stats on {0}'.format(os.path.split(inraster)[1])) + # self.logger.debug("self.stats_dict[i] : {0}".format(self.stats_dict[i])) + + time2 = time.time() + + self.logger.info('End of stats on {0} band {1}'.format(os.path.split(inraster)[1], band)) + self.logger.debug("Temps d'éxecution {:.3f} ms".format((time2-time1)*1000.0)) def zonal_stats_pp(self, inraster): """ @@ -168,34 +192,76 @@ class Vector(): :param inraster: Input image path :type inraster: str + :returns: dict -- **p_stats** : dictionnary with pxl percent in every polygon. Mainly 'Maj_count' (Majority Value) and 'Maj_count_perc' (Majority Percent) """ - p_stats = rasterstats.zonal_stats(self.vector_used, inraster, stats=['count'], copy_properties=True, categorical=True) - - for i in range(len(p_stats)): - percent = 0.0 - for p in p_stats[i].keys(): - if type(p) == np.float32 and p_stats[i][p]/float(p_stats[i]['count'])*100 > percent: - p_stats[i]['Maj_count'] = p - p_stats[i]['Maj_count_perc'] = p_stats[i][p]/float(p_stats[i]['count'])*100 - percent = p_stats[i]['Maj_count_perc'] - if not 'Maj_count' in p_stats[i].keys() or not 'Maj_count_perc' in p_stats[i].keys(): - p_stats[i]['Maj_count']=0 - p_stats[i]['Maj_count_perc']=0 + # p_stats = rasterstats.zonal_stats(self.vector_used, inraster, stats=['count'], copy_properties=True, categorical=True) + + # for i in range(len(p_stats)): + # percent = 0.0 + # for p in p_stats[i].keys(): + # if type(p) == np.float32 and p_stats[i][p]/float(p_stats[i]['count'])*100 > percent: + # p_stats[i]['Maj_count'] = p + # p_stats[i]['Maj_count_perc'] = p_stats[i][p]/float(p_stats[i]['count'])*100 + # percent = p_stats[i]['Maj_count_perc'] + + # if not 'Maj_count' in p_stats[i].keys() or not 'Maj_count_perc' in p_stats[i].keys(): + # p_stats[i]['Maj_count']=0 + # p_stats[i]['Maj_count_perc']=0 + + # for i in range(len(p_stats)): + # percent = 0.0 + # Maj_count = 0 + # Maj_count_perc = 0 + # for p in p_stats[i].keys(): + # if not isinstance(p, str) and p_stats[i][p]/float(p_stats[i]['count'])*100 > percent: + # Maj_count = p + # Maj_count_perc = p_stats[i][p]/float(p_stats[i]['count'])*100 + # percent = Maj_count_perc + + # p_stats[i]['Maj_count']= Maj_count + # p_stats[i]['Maj_count_perc']= Maj_count_perc + + zonal_stats = otb.Registry.CreateApplication("ZonalStatistics") + zonal_stats.SetParameterString("in", inraster) + zonal_stats.SetParameterString("vec", self.vector_used) + zonal_stats.SetParameterInt("band", 1) + zonal_stats.SetParameterInt("ram", 128) + statistique = ["count"] + zonal_stats.SetParameterStringList("stats", statistique) + + zonal_stats.Execute() + + stats_otb = list(zonal_stats.GetParameterValue("out")) + + p_stats = [] + + for item in stats_otb: + liste_item = item.split() + dico = dict() + + dico['count'] = float(liste_item[0]) + dico['Maj_count'] = float(liste_item[1]) + dico['Maj_count_perc'] = float(liste_item[2]) + p_stats.append(dico) return p_stats def layer_rasterization(self, raster_head, attribute_r, **kwargs): """ - Function to rasterize a vector. Define resolution, projection of the output raster with a raster head. - And complete the gdal pointer empty properties with the layer's information of the vector and a defined field. + Function to rasterize a vector. + Define resolution, projection of the output raster with a raster head. + And complete the gdal pointer empty properties with the layer's information + of the vector and a defined field. If a raster has several band, in option you can choice if you want one band or more. :param raster_head: Raster path that will look like the final raster of the rasterization :type raster_head: str + :param attribute_r: Field name of the shapefile that contains class names :type attribute_r: str + :kwargs: **choice_nb_b** (int) - Output image number of band. If you choice 1, take first band. If you choice 2, take two first band etc... :returns: str -- **valid_raster** : output raster path from the rasterization """ @@ -204,10 +270,12 @@ class Vector(): # for the validation shapefile example_raster = RasterSat_by_date('', '') # Call the raster class example_raster.choice_nb_b = kwargs['choice_nb_b'] if kwargs.get('choice_nb_b') else 0 + raster_info = example_raster.raster_data(raster_head)# Extract data info # Define the validation's vector - valid_raster = self.vector_used[:-3]+'TIF' # Name of the output raster + valid_raster = "{0}.TIF".format(self.vector_used[:-4])# Name of the output raster + if os.path.exists(str(valid_raster)): os.remove(valid_raster) @@ -219,11 +287,12 @@ class Vector(): data_raster = raster_info[0] info_out = example_raster.create_empty_raster(valid_raster, data_raster, raster_info[1]) + self.raster_ds = example_raster.out_ds # Virtual rasterize the vector pt_rast = osgeo.gdal.RasterizeLayer(self.raster_ds, [1], self.data_source.GetLayer(), \ - options=["ATTRIBUTE=" + str(attribute_r)]) + options=["ATTRIBUTE = {0}".format(attribute_r)]) if pt_rast != 0: raise Exception("error rasterizing layer: %s" % pt_rast) diff --git a/Vhrs.py b/Vhrs.py index 99c3102dd8e2d9558c3be15a3442ed7c142fad88..7edd0f452adcc3d66feff45dd808cb5c8eb7ea3b 100644 --- a/Vhrs.py +++ b/Vhrs.py @@ -17,18 +17,17 @@ # You should have received a copy of the GNU General Public License # along with PHYMOBAT 1.2. If not, see <http://www.gnu.org/licenses/>. -import os -import subprocess +import os, sys +import glob import Outils +import psutil import otbApplication as otb from multiprocessing import Process - class Vhrs(): - """ - Class to compute Haralick and SFS textures because of OTB application in command line + Class to compute Haralick and SFS textures with OTB @param imag: The input image path to compute texture image @type imag: str @@ -47,36 +46,32 @@ class Vhrs(): Create a new 'Texture' instance """ - self._imag = imag + self._image = imag self.mp = mp self.logger = Outils.Log('Log', "VHRS") + + self.out_sfs = "{0}_sfs.TIF".format(self._image[:-4]) + self.out_haralick = "{0}_haralick.TIF".format(self._image[:-4]) + + ram_dispo = round((psutil.virtual_memory().available/pow(2,20))/2) + + self.logger.debug("ram_dispo : {0}".format(ram_dispo)) - self.logger.info('SFS image') - self.out_sfs = "{0}_sfs.TIF".format(self._imag[:-4]) - - if not os.path.exists(self.out_sfs): - self.logger.info("SFS image doesn't exist !") - p_sfs = Process(target=self.sfs_texture_extraction) - p_sfs.start() + p_sfs = Process(target=self.sfs_texture_extraction) + p_sfs.start() - if mp == 0: - p_sfs.join() - - self.logger.info('Haralick image') - self.out_haralick = "{0}_haralick.TIF".format(self._imag[:-4]) + if mp == 0: + p_sfs.join() - if not os.path.exists(self.out_haralick): - self.logger.info("Haralick image doesn't exist !") - p_har = Process(target=self.haralick_texture_extraction, args=('simple', )) - p_har.start() + p_har = Process(target=self.haralick_texture_extraction, args=('simple', )) + p_har.start() - if mp == 0: - p_har.join() + if mp == 0: + p_har.join() if mp == 1: - if not os.path.exists(self.out_sfs) and not os.path.exists(self.out_haralick): - p_sfs.join() - p_har.join() + p_sfs.join() + p_har.join() def sfs_texture_extraction(self): """ @@ -92,18 +87,24 @@ class Vhrs(): - parameters.maxcons : Ratio Maximum Consideration Number * out : Feature Output Image """ + + self.logger.info('SFS image') + + if not os.path.exists(self.out_sfs): + + self.logger.info("SFS image doesn't exist !") - otb_SFS = otb.Registry.CreateApplication("SFSTextureExtraction") - otb_SFS.SetParameterString("in", self._imag) - otb_SFS.SetParameterInt("channel", 2) - otb_SFS.SetParameterInt("parameters.spethre", 50) - otb_SFS.SetParameterInt("parameters.spathre", 100) - otb_SFS.SetParameterString("out", self.out_sfs) + otb_SFS = otb.Registry.CreateApplication("SFSTextureExtraction") + otb_SFS.SetParameterString("in", self._image) + otb_SFS.SetParameterInt("channel", 2) + otb_SFS.SetParameterInt("parameters.spethre", 50) + otb_SFS.SetParameterInt("parameters.spathre", 100) + otb_SFS.SetParameterString("out", self.out_sfs) - otb_SFS.ExecuteAndWriteOutput() + otb_SFS.ExecuteAndWriteOutput() + + self.logger.info('SFS image created.') - self.logger.info('SFS image created.') - def haralick_texture_extraction(self, texture_choice): """ Function to compute OTB Haralick texture image @@ -129,15 +130,20 @@ class Vhrs(): @param texture_choice: Order texture choice -> Simple / Advanced / Higher @type texture_choice: str """ - - otb_Haralick = otb.Registry.CreateApplication("HaralickTextureExtraction") - otb_Haralick.SetParameterString("in", self._imag) - otb_Haralick.SetParameterInt("channel", 2) - otb_Haralick.SetParameterInt("parameters.xrad", 3) - otb_Haralick.SetParameterInt("parameters.yrad", 3) - otb_Haralick.SetParameterString("texture", texture_choice) - otb_Haralick.SetParameterString("out", self.out_haralick) + self.logger.info('Haralick image') + + if not os.path.exists(self.out_haralick): + + self.logger.info("Haralick image doesn't exist !") + + otb_Haralick = otb.Registry.CreateApplication("HaralickTextureExtraction") + otb_Haralick.SetParameterString("in", self._image) + otb_Haralick.SetParameterInt("channel", 2) + otb_Haralick.SetParameterInt("parameters.xrad", 3) + otb_Haralick.SetParameterInt("parameters.yrad", 3) + otb_Haralick.SetParameterString("texture", texture_choice) + otb_Haralick.SetParameterString("out", self.out_haralick) - otb_Haralick.ExecuteAndWriteOutput() + otb_Haralick.ExecuteAndWriteOutput() - self.logger.info('Haralick image created.') \ No newline at end of file + self.logger.info('Haralick image created.')