Configuración inicial:


In [1]:
%matplotlib inline
from osgeo import gdal

import gdalnumeric
import osgeo.gdalconst
import numpy as np

import matplotlib.pyplot as plt

import glob as g
import os

gdal.UseExceptions()

Cargar una escena Landsat, unir todas las bandas en un arreglo y crear un nuevo raster que tenga todas las bandas en un sólo archivo.


In [2]:
landsat_tiff = "/Users/RaulSierra/Ecoinformatica/Divulgacion/Talleres/LUCC_ITAM/datos/ejercicio2.tif"
raster = gdal.Open(landsat_tiff)
print raster.GetGeoTransform()

ls_data = np.zeros((2391, 2429, raster.RasterCount))
for i in range(1, raster.RasterCount):
    #Para ver la imagen
    #Leemos cada banda al arreglo
    ls_data[:,:,i-1] = raster.GetRasterBand(i).ReadAsArray()


(719010.0, 30.0, 0.0, 2477190.0, 0.0, -30.0)

Podemos hacer los mismo que hacemos en QGIS para visualizar (normalizar estadísticamente las bandas y aplastar todos lo valores a más de N desviaciones estándar a que estén exactamente a N*std. En este ejemplo, acotamos a 2 desviaciones estándar.


In [3]:
num_bands = raster.RasterCount - 1
norm_bands = ls_data[:,:,0:num_bands].copy()
print norm_bands.shape

means = norm_bands.reshape(-1, num_bands).mean(0)
sds = norm_bands.reshape(-1, num_bands).std(0)
print means
print sds
norm_bands -= means
norm_bands /= 2*sds
norm_bands[norm_bands > 1] = 1
norm_bands[norm_bands < -1] = -1

print norm_bands.reshape(-1, num_bands).mean(0)
print norm_bands.reshape(-1, num_bands).std(0)
norm_bands -= norm_bands.reshape(-1, num_bands).min(0)
norm_bands /= norm_bands.reshape(-1, num_bands).max(0)

print "Ahora los valores estan entre 0 y 1 para imshow"
print norm_bands.reshape(-1, num_bands).min(0)
print norm_bands.reshape(-1, num_bands).max(0)


(2391, 2429, 9)
[  73.28366357   67.446185     85.51302512   87.61070513  114.04963067
  138.38003206  162.7922231    84.28555364   55.56955641]
[ 12.98198489  16.57346535  26.63467735  20.48206885  32.76522584
   8.21589547  14.79760209  27.49980951  13.98246173]
[-0.00334041 -0.00123855  0.00016846  0.00103586  0.00714379  0.01032645
  0.01038001  0.00294184  0.00307887]
[ 0.48533803  0.49018581  0.49401185  0.46643727  0.48244791  0.47324108
  0.47312048  0.49046868  0.48495174]
Ahora los valores estan entre 0 y 1 para imshow
[ 0.  0.  0.  0.  0.  0.  0.  0.  0.]
[ 1.  1.  1.  1.  1.  1.  1.  1.  1.]

Y visualizamos con color "real"


In [4]:
plt.axis('off')
plt.imshow(norm_bands[:,:,(2, 1, 0)])


Out[4]:
<matplotlib.image.AxesImage at 0x1112e7810>

O con color falso para ver vegetación en rojo:


In [5]:
plt.axis('off')
plt.imshow(norm_bands[:,:,(3, 4, 2)])


Out[5]:
<matplotlib.image.AxesImage at 0x156cb7910>

Manipulación de shapefiles con python usando fiona (http://toblerity.org/fiona/manual.html).

Vamos a convertir el atributo que corresponde al nivel 1 de la clasificación de coberturas de suelo de cadena de caracteres a numérico, con el objetivo de generar un raster que podamos procesar con las imágenes landsat.

Primero abrimos y leemos el shapefile fuente.


In [18]:
import fiona
shape_dir = "../datos/interpretados/CHIPS_AGUASCALIENTES_LANDSAT_2000/"
with fiona.open(os.path.join(shape_dir, "CHIPS_AGUASCALIENTES_LANDSAT_2000.shp")) as src:
    src_driver = src.driver
    src_crs = src.crs
    src_schema = src.schema
    recs = list(src)

print "El esquema actual:  " + str(src_schema['properties'])
src_schema['properties']['level_1'] = "int:8"
print "El nuevo esquema:  " + str(src_schema['properties'])


El esquema actual:  OrderedDict([(u'OBJECTID', 'int:9'), (u'level_1', 'str:30'), (u'level_2', 'str:60'), (u'level_3', 'str:40'), (u'level_4', 'str:15'), (u'Nom_Chip', 'str:150'), (u'SHAPE_Leng', 'float:19.11'), (u'SHAPE_Area', 'float:19.11')])
El nuevo esquema:  OrderedDict([(u'OBJECTID', 'int:9'), (u'level_1', 'int:8'), (u'level_2', 'str:60'), (u'level_3', 'str:40'), (u'level_4', 'str:15'), (u'Nom_Chip', 'str:150'), (u'SHAPE_Leng', 'float:19.11'), (u'SHAPE_Area', 'float:19.11')])

Luego creamos un nuevo shapefile y mapeamos los nombres en el atributo level_1 a valores numéricos


In [7]:
#Queremos que el nivel 1 sea numérico en vez de una cadena de caracteres
src_schema['properties']
with fiona.open(os.path.join(shape_dir, "CHIPS_AGUASCALIENTES_LANDSAT_2000_L1.shp"),
                'w',
                driver=src_driver,
                crs=src_crs,
                schema=src_schema) as c:
    clases_l1 = {u'TIERRAS FORESTALES': 1, 
                 u'INDEFINIDO': 8, 
                 u'PRADERAS': 3, 
                 u'TIERRAS DE USO AGRICOLA': 4, 
                 u'AGUA': 5, 
                 u'HUMEDAL': 2, 
                 u'ASENTAMIENTOS': 6, 
                 u'OTROS': 7
                 }
    for rec in recs:
        clase = rec["properties"]["level_1"]
        if clase not in clases_l1:
            clases_l1[clase] = len(clases_l1.keys())
        rec["properties"]["level_1"] = clases_l1[clase]
        c.write(rec)

print clases_l1
c.closed


{u'TIERRAS FORESTALES': 1, u'INDEFINIDO': 8, u'ASENTAMIENTOS': 6, u'HUMEDAL': 2, u'PRADERAS': 3, u'OTROS': 7, u'TIERRAS DE USO AGRICOLA': 4, u'AGUA': 5}
Out[7]:
True

Para rasterizar el shapefile usamos ogr, que viene junto con gdal en la biblioteca osgeo.

Primero abrimos el shapefile que creamos en el paso anterior.


In [8]:
from osgeo import ogr
interpretados = ogr.Open("../datos/interpretados/CHIPS_AGUASCALIENTES_LANDSAT_2000/CHIPS_AGUASCALIENTES_LANDSAT_2000_L1.shp")

# No confundir features de shapefiles con características en machine learning
layer = interpretados.GetLayer()
featureCount = layer.GetFeatureCount()
print featureCount

lyrDefn = layer.GetLayerDefn()

# Print campos del shapefile
for i in range(lyrDefn.GetFieldCount()):
    print lyrDefn.GetFieldDefn(i).GetName()


5873
OBJECTID
level_1
level_2
level_3
level_4
Nom_Chip
SHAPE_Leng
SHAPE_Area

Tenemos que definir cómo convertir de polígonos a pixeles. Esto se hace definiendo el tamaño que ocupa un pixel de la región que define el shapefile.


In [13]:
# Define pixel_size and NoData value of new raster
pixel_size = 30
NoData_value = 0

# Open the data source and read in the extent
source_srs = layer.GetSpatialRef()
x_min, x_max, y_min, y_max = layer.GetExtent()

print [x_min, x_max, y_min, y_max]

# Create the destination data source
x_res = int((x_max - x_min) / pixel_size)
y_res = int((y_max - y_min) / pixel_size)
print [x_res, y_res]

target_ds = gdal.GetDriverByName('GTiff').Create('../datos/interpretados/CHIPS_AGUASCALIENTES_LANDSAT_2000/CHIPS_AGUASCALIENTES_LANDSAT_2000_L1.tiff', x_res, y_res, gdal.GDT_Byte)
target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
band = target_ds.GetRasterBand(1)
band.SetNoDataValue(NoData_value)

gdal.RasterizeLayer(target_ds, [1], layer, options=["ATTRIBUTE=level_1"])
if source_srs:
    print "Aplicar proyeccion"
    # Make the target raster have the same projection as the source
    target_ds.SetProjection(source_srs.ExportToWkt())


[2412898.1421999997, 2515252.5555999996, 1069293.1630000006, 1156238.544399999]
[3411, 2898]
Aplicar proyeccion

In [14]:
import seaborn as sns
cpal = sns.choose_colorbrewer_palette("qualitative", as_cmap=False)



In [15]:
import matplotlib.cm as cm

clases_im = band.ReadAsArray()
cpal[8] = (1, 1, 1)
snspal = cm.Accent.from_list("Accent", cpal)
clases_im[clases_im == 0] = 8 #para que ponga los nodata blanco


#cerramos el raster
target_ds = None
plt.axis('off')
plt.imshow(clases_im, cmap = snspal)


Out[15]:
<matplotlib.image.AxesImage at 0x16b39b910>

Crear capa raster con IDS por polígono

Con el siguiente código vamos a crear un raster que le asigne IDs a los pixeles de nuestra imagen de tal forma que los pixeles con el mismo ID pertenezcan a la misma área y tengan la misma clase.

Para esto vamos a crear un shapefile a partir de la capa de etiquetas (banda 10) de nuestro raster (ejercicio2.tif). Utilizamos las herramientas de línea de comandos de gdal.


In [ ]:


In [ ]:
src_file = "../datos/ejercicio2.tif"
dst_file = "../datos/ejercicio2_polIDS"

#este comando lo pueden correr directamente en la consola
!gdal_polygonize.py $src_file -f "ESRI Shapefile" -b 10 $dst_file ejercicio2_polIDS Etiqueta

El shapefile que obtenemos está compuesto por polígonos que corresponden a conjuntos de pixeles conectados en el raster que comparten la misma etiqueta, en este nuevo shapefile los polígonos no tienen ID así que les asigaremos a cada uno un ID único en un nuevo campo "OID".

No encontré cómo hacer lo anterior con las herramientas de línea de comando así que lo haré en python.


In [ ]:
ej_shp = ogr.Open("../datos/ejercicio2_polIDS/ejercicio2_polIDS.shp", 1)

# No confundir features de shapefiles con características en machine learning
layer = ej_shp.GetLayer()
featureCount = layer.GetFeatureCount()
print featureCount

idField = ogr.FieldDefn("OID", ogr.OFTInteger)
layer.CreateField(idField)

for i in range(featureCount):
    f = layer.GetFeature(i)
    f.SetField("OID", i)
    layer.SetFeature(f)

ej_shp = None

El código anterior actualizó el shapefile con el nuevo campo 'OID'. Ahora rasterizamos el shape escribiendo los valores OID en los pixeles.


In [ ]:
from osgeo import gdal
src_file = "../datos/ejercicio2_polIDS/ejercicio2_polIDS.shp"
dst_file = "../datos/ejercicio2_polIDS.tif"

raster = gdal.Open("../datos/ejercicio2.tif")
width = raster.RasterXSize
height = raster.RasterYSize

!gdal_rasterize -a OID -a_nodata 0 -ts $width $height -l ejercicio2_polIDS $src_file $dst_file

Toda la lista de drivers para distintos tipo de archivo en GDAL


In [9]:
cnt = gdal.GetDriverCount()
formatsList = []  # Empty List

for i in range(cnt):
    driver = gdal.GetDriver(i)
    driverName = driver.ShortName
    driverLong = driver.LongName
    
    if not driverName in formatsList:
        formatsList.append((driverName, driverLong))
formatsList.sort() # Sorting the messy list of ogr drivers

for i in formatsList:
    print i


('AAIGrid', 'Arc/Info ASCII Grid')
('ACE2', 'ACE2')
('ADRG', 'ARC Digitized Raster Graphics')
('AIG', 'Arc/Info Binary Grid')
('ARG', 'Azavea Raster Grid format')
('AirSAR', 'AirSAR Polarimetric Image')
('BIGGIF', 'Graphics Interchange Format (.gif)')
('BLX', 'Magellan topo (.blx)')
('BMP', 'MS Windows Device Independent Bitmap')
('BSB', 'Maptech BSB Nautical Charts')
('BT', 'VTP .bt (Binary Terrain) 1.3 Format')
('CEOS', 'CEOS Image')
('COASP', 'DRDC COASP SAR Processor Raster')
('COSAR', 'COSAR Annotated Binary Matrix (TerraSAR-X)')
('CPG', 'Convair PolGASP')
('CTG', 'USGS LULC Composite Theme Grid')
('CTable2', 'CTable2 Datum Grid Shift')
('DIMAP', 'SPOT DIMAP')
('DIPEx', 'DIPEx')
('DOQ1', 'USGS DOQ (Old Style)')
('DOQ2', 'USGS DOQ (New Style)')
('DTED', 'DTED Elevation Raster')
('E00GRID', 'Arc/Info Export E00 GRID')
('ECRGTOC', 'ECRG TOC format')
('EHdr', 'ESRI .hdr Labelled')
('EIR', 'Erdas Imagine Raw')
('ELAS', 'ELAS')
('ENVI', 'ENVI .hdr Labelled')
('ERS', 'ERMapper .ers Labelled')
('ESAT', 'Envisat Image Format')
('FAST', 'EOSAT FAST Format')
('FIT', 'FIT Image')
('FujiBAS', 'Fuji BAS Scanner Image')
('GFF', 'Ground-based SAR Applications Testbed File Format (.gff)')
('GIF', 'Graphics Interchange Format (.gif)')
('GRASSASCIIGrid', 'GRASS ASCII Grid')
('GRIB', 'GRIdded Binary (.grb)')
('GS7BG', 'Golden Software 7 Binary Grid (.grd)')
('GSAG', 'Golden Software ASCII Grid (.grd)')
('GSBG', 'Golden Software Binary Grid (.grd)')
('GSC', 'GSC Geogrid')
('GTX', 'NOAA Vertical Datum .GTX')
('GTiff', 'GeoTIFF')
('GXF', 'GeoSoft Grid Exchange Format')
('GenBin', 'Generic Binary (.hdr Labelled)')
('HF2', 'HF2/HFZ heightfield raster')
('HFA', 'Erdas Imagine Images (.img)')
('HTTP', 'HTTP Fetching Wrapper')
('IDA', 'Image Data and Analysis')
('ILWIS', 'ILWIS Raster Map')
('INGR', 'Intergraph Raster')
('IRIS', 'IRIS data (.PPI, .CAPPi etc)')
('ISIS2', 'USGS Astrogeology ISIS cube (Version 2)')
('ISIS3', 'USGS Astrogeology ISIS cube (Version 3)')
('JAXAPALSAR', 'JAXA PALSAR Product Reader (Level 1.1/1.5)')
('JDEM', 'Japanese DEM (.mem)')
('JPEG', 'JPEG JFIF')
('KMLSUPEROVERLAY', 'Kml Super Overlay')
('KRO', 'KOLOR Raw')
('L1B', 'NOAA Polar Orbiter Level 1b Data Set')
('LAN', 'Erdas .LAN/.GIS')
('LCP', 'FARSITE v.4 Landscape File (.lcp)')
('LOSLAS', 'NADCON .los/.las Datum Grid Shift')
('Leveller', 'Leveller heightfield')
('MAP', 'OziExplorer .MAP')
('MBTiles', 'MBTiles')
('MEM', 'In Memory Raster')
('MFF', 'Vexcel MFF Raster')
('MFF2', 'Vexcel MFF2 (HKV) Raster')
('MSGN', 'EUMETSAT Archive native (.nat)')
('NDF', 'NLAPS Data Format')
('NGSGEOID', 'NOAA NGS Geoid Height Grids')
('NITF', 'National Imagery Transmission Format')
('NTv2', 'NTv2 Datum Grid Shift')
('NUMPY', 'Numeric Python Array')
('NWT_GRC', 'Northwood Classified Grid Format .grc/.tab')
('NWT_GRD', 'Northwood Numeric Grid Format .grd/.tab')
('OZI', 'OziExplorer Image File')
('PAux', 'PCI .aux Labelled')
('PCIDSK', 'PCIDSK Database File')
('PCRaster', 'PCRaster Raster File')
('PDF', 'Geospatial PDF')
('PDS', 'NASA Planetary Data System')
('PNG', 'Portable Network Graphics')
('PNM', 'Portable Pixmap Format (netpbm)')
('R', 'R Object Data Store')
('RIK', 'Swedish Grid RIK (.rik)')
('RMF', 'Raster Matrix Format')
('RPFTOC', 'Raster Product Format TOC format')
('RS2', 'RadarSat 2 XML Product')
('RST', 'Idrisi Raster A.1')
('Rasterlite', 'Rasterlite')
('SAGA', 'SAGA GIS Binary Grid (.sdat)')
('SAR_CEOS', 'CEOS SAR Image')
('SDTS', 'SDTS Raster')
('SGI', 'SGI Image File Format 1.0')
('SNODAS', 'Snow Data Assimilation System')
('SRP', 'Standard Raster Product (ASRP/USRP)')
('SRTMHGT', 'SRTMHGT File Format')
('TIL', 'EarthWatch .TIL')
('TSX', 'TerraSAR-X Product')
('Terragen', 'Terragen heightfield')
('USGSDEM', 'USGS Optional ASCII DEM (and CDED)')
('VRT', 'Virtual Raster')
('WCS', 'OGC Web Coverage Service')
('WMS', 'OGC Web Map Service')
('XPM', 'X11 PixMap Format')
('XYZ', 'ASCII Gridded XYZ')
('ZMap', 'ZMap Plus Grid')