Raster mengklasifikasikan ulang menggunakan python, gdal dan numpy


9

Saya ingin mereklasifikasi file raster dari raster dengan 10 kelas ke raster dengan 8 kelas menggunakan pyhton, gdal dan / atau numpy. Kelas-kelas direpresentasikan sebagai bilangan bulat. Saya telah mencoba mengikuti langkah-langkah dari pos ini. Mengklasifikasi raster menggunakan GDAL dan Python , doc numpy.equal dan juga doc gdal_calc. Namun, sia-sia.

File raster yang akan direklasifikasi memiliki nilai integer mulai dari 0 hingga 11 dan juga menyertakan nilai 100 dan 255. Berikut ini menunjukkan reklasifikasi (dari nilai: ke nilai):

nodata: 4, 0: 4, 1: 1, 2: 2, 3: 3, 4: 3, 5: 4, 6: 5, 7: 5, 8: 6, 9: 7, 10: 8, 100: nodata, 255: nodata,

Apa yang dapat saya lakukan adalah memilih file raster untuk direklasifikasi menggunakan tkinter.FileDialog dan mendapatkan info raster seperti geotransform, dan ukuran piksel dengan reclass = gdal.Open (raster, GA_ReadOnly).

Bagaimana cara saya menyelesaikan masalah di atas.

Mungkin perlu disebutkan bahwa raster yang akan direklasifikasi dapat cukup besar dalam beberapa kasus (500mb hingga 5gb).


Ada contoh lain di GeoExamples Blog
bennos

@bennos, mencoba skrip di blog tetapi mengembalikan kesalahan memori ketika membongkar array.
PyMapr

Saya sarankan Anda mendiskusikan masalah ini dengan Roger Veciana i Rovira, penulis posting, karena dia tahu kode-nya lebih baik daripada saya dan mungkin tahu bagaimana menyelesaikan masalah ini
bennos

Mengubah raster input dari 16Bit yang tidak ditandatangani menjadi 8Bit yang tidak ditandatangani memecahkan masalah memori. Namun, dibutuhkan jumlah waktu yang sama untuk mengklasifikasi ulang seperti skrip dmh126 di bawah ini.
PyMapr

Jawaban:


6

Di sini Anda memiliki skrip python sederhana untuk reklasifikasi, saya menulisnya dan itu berfungsi untuk saya:

from osgeo import gdal

driver = gdal.GetDriverByName('GTiff')
file = gdal.Open('/home/user/workspace/raster.tif')
band = file.GetRasterBand(1)
lista = band.ReadAsArray()

# reclassification
for j in  range(file.RasterXSize):
    for i in  range(file.RasterYSize):
        if lista[i,j] < 200:
            lista[i,j] = 1
        elif 200 < lista[i,j] < 400:
            lista[i,j] = 2
        elif 400 < lista[i,j] < 600:
            lista[i,j] = 3
        elif 600 < lista[i,j] < 800:
            lista[i,j] = 4
        else:
            lista[i,j] = 5

# create new file
file2 = driver.Create( 'raster2.tif', file.RasterXSize , file.RasterYSize , 1)
file2.GetRasterBand(1).WriteArray(lista)

# spatial ref system
proj = file.GetProjection()
georef = file.GetGeoTransform()
file2.SetProjection(proj)
file2.SetGeoTransform(georef)
file2.FlushCache()

Ubah saja rentangnya.

Saya harap ini akan membantu.


3
Anda harus menutup file2dengan del file2atau file2 = Noneuntuk memastikannya ditulis ke disk. .FlushCache()hanya memengaruhi cache ubin internal GDAL.
Kersten

@ dmh126, saya memodifikasi rentang dan skrip berfungsi. Namun, ini tidak terlalu cepat (bisa diperdebatkan dengan cepat). Input raster sekitar 120mb dan membutuhkan waktu sekitar 15 menit untuk menyelesaikannya. Dengan bantuan paket penginderaan jauh komersial, dibutuhkan beberapa detik. Adakah rekomendasi untuk mengurangi waktu pemrosesan?
PyMapr

Saya pikir multithreading dapat membantu. Anda dapat mencoba menggunakan semua inti Anda, ada pertanyaan gis.stackexchange.com/questions/162978/…
dmh126

Tidak masuk akal untuk menggunakan double untuk loop, lihat jawaban di bawah ini
Mattijn

Benar, loop ganda dan reklasifikasi per elemen adalah yang paling lambat dari semua cara yang mungkin untuk melakukan ini. Gunakan bagian kuat numpy seperti ufuncs: docs.scipy.org/doc/numpy-1.10.1/reference/ufuncs.html .
sgillies

16

Alih-alih melakukan reklasifikasi sebagai double for loop yang dijelaskan oleh dmh126, lakukan dengan menggunakan np.where:

# reclassification    
lista[np.where( lista < 200 )] = 1
lista[np.where((200 < lista) & (lista < 400)) ] = 2
lista[np.where((400 < lista) & (lista < 600)) ] = 3
lista[np.where((600 < lista) & (lista < 800)) ] = 4
lista[np.where( lista > 800 )] = 5

Pada array 6163 kali 3537 piksel (41,6mb) klasifikasi dilakukan dalam 1,59 detik, di mana dibutuhkan 12 menit 41d menggunakan double for loop. Total hanya speedup 478x.

Bottomline, jangan pernah gunakan double untuk loop menggunakan numpy


1
Terima kasih atas petunjuknya, tapi saya pikir itu akan menimbulkan masalah jika kelas input tumpang tindih dengan kelas output. Saya tidak ingin nilai baru saya diubah oleh aturan selanjutnya.
etrimaille

@Industri - Hanya mengalami masalah itu di sini.
relima

1
Jadi, periksa jawaban saya di bawah ini: gis.stackexchange.com/questions/163007/…
etrimaille

6

Berikut ini contoh dasar menggunakan rasterio dan numpy:

import rasterio as rio
import numpy as np


with rio.open('~/rasterio/tests/data/rgb1.tif') as src:
    # Read the raster into a (rows, cols, depth) array,
    # dstack this into a (depth, rows, cols) array,
    # the sum along the last axis (~= grayscale)
    grey = np.mean(np.dstack(src.read()), axis=2)

    # Read the file profile
    srcprof = src.profile.copy()

classes = 5
# Breaks is an array of the class breaks: [   0.   51.  102.  153.  204.]
breaks = (np.arange(classes) / float(classes)) * grey.max()

# classify the raster
classified = np.sum(np.dstack([(grey < b) for b in breaks]), axis=2).reshape(1, 400, 400).astype(np.int32)

# Update the file opts to one band
srcprof.update(count=1, nodata=None, dtype=classified.dtype)

with rio.open('/tmp/output.tif', 'w', **srcprof) as dst:
    # Write the output
    dst.write(classified)

3

Hanya untuk melengkapi jawaban dari @Mattijn, saya pikir itu akan menimbulkan masalah jika kelas input tumpang tindih dengan kelas output. Saya tidak ingin nilai baru saya diubah oleh aturan selanjutnya.

Saya tidak tahu apakah saya kehilangan kecepatan tetapi saya harus melakukan salinan yang dalam:

list_dest = lista.copy()

list_dest[np.where( lista < 0 )] = 0
list_dest[np.where((0 <= lista) & (lista <= 1)) ] = 1
list_dest[np.where((1 < lista) & (lista <= 5)) ] = 2
list_dest[np.where( 5 < lista )] = 3

1

Berikut ini adalah pendekatan Rasterio lain yang saya retas bersama menggunakan Rasterio Cookbook dan jawaban @ Mattijn.

import rasterio
import numpy as np

with rasterio.open('input_raster.tif') as src:    
    # Read as numpy array
    array = src.read()
    profile = src.profile

    # Reclassify
    array[np.where(array == 0)] = 4 
    array[np.where(array == 2)] = 1
    # and so on ...  

with rasterio.open('output_raster.tif', 'w', **profile) as dst:
    # Write to disk
    dst.write(array)

0

Dalam beberapa kasus, digitalisasi numpy dapat berguna untuk dengan cepat beralih dari rentang ke nampan.

import rasterio
import numpy as np

with rasterio.open('my_raster.tif') as src:    
    array = src.read()
    profile = src.profile
    bins = np.array([-1.,-0.7,-0.4, 0.2, 1]) 
    inds = np.digitize(array, bins)

with rasterio.open('output_raster.tif', 'w', **profile) as dst:
    dst.write(inds)

0

Dengan dukungan tabel warna RGB raster:

import numpy as np
from osgeo import gdal

path_inDs = "/data/OCS_2016.extract.tif"
path_outDs = "/data/OCS_2016.postpython.tif"

driver = gdal.GetDriverByName('GTiff')
file = gdal.Open(path_inDs)

if file is None:
  print ('Could not open image file')
  sys.exit(1)

band = file.GetRasterBand(1)
lista = band.ReadAsArray()


# reclassification
lista[np.where(lista == 31)] = 16

# create new file
file2 = driver.Create(path_outDs, file.RasterXSize , file.RasterYSize , 1, gdal.GPI_RGB)
file2.GetRasterBand(1).WriteArray(lista)

# spatial ref system
proj = file.GetProjection()
georef = file.GetGeoTransform()
meta = file.GetMetadata()
colors = file.GetRasterBand(1).GetRasterColorTable()

file2.SetProjection(proj)
file2.SetGeoTransform(georef)
file2.SetMetadata(meta)
file2.GetRasterBand(1).SetRasterColorTable(colors)

file2.FlushCache()
del file2


0

Alternatif yang sedikit berbeda adalah sebagai berikut:

import numpy as np
from osgeo import gdal

original = gdal.Open('**PATH**\\origianl_raster.tif')



# read the original file

band = original.GetRasterBand(1) # assuming that the file has only one band
band_array = band.ReadAsArray()



#create a new array with reclassified values

new_array = np.where(band_array == np.nan, 4, 
                np.where(band_array == 0, 4,
                    np.where(band_array == 1, 1,
                        np.where(band_array == 2, 2,
                            np.where(band_array == 3, 3,
                                np.where(band_array == 4, 3, 
                                    np.where(band_array == 5, 4,
                                        np.where(band_array == 6, 5,
                                            np.where(band_array == 7, 5,
                                                np.where(band_array == 8, 6, 
                                                    np.where(band_array == 9, 7,
                                                       np.where(band_array == 10, 8,
                                                            np.where(band_array == 100, np.nan, np.nan))))))))))))) 
                                # the last line also includes values equal to 255, as they are the only ones left



# create and save reclassified raster as a new file

outDs = gdal.GetDriverByName('GTiff').Create("**PATH**\\reclassified_raster.tif", original.RasterXSize, original.RasterYSize, 1, gdal.GDT_Float32)

outBand = outDs.GetRasterBand(1)
outBand.WriteArray(new_array)

outDs.SetGeoTransform(original.GetGeoTransform())
outDs.SetProjection(original.GetProjection())


# flush cache

outDs.FlushCache()

Skrip ini diputar dengan numpy.where ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html ): di semua langkah terpisah dari yang terakhir, alih-alih mengembalikan nilai saat kondisi tidak puas, ia mengembalikan np.where lain. Dan itu terus berjalan sampai semua nilai raster yang mungkin dipertimbangkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.