Raster phân loại lại bằng python, gdal và numpy


9

Tôi muốn phân loại lại một tệp raster từ một raster có 10 lớp thành một raster với 8 lớp sử dụng pyhton, gdal và / hoặc numpy. Các lớp được biểu diễn dưới dạng số nguyên. Tôi đã thử làm theo các bước từ bài đăng này Phân loại lại các trình quét bằng GDAL và Python , tài liệu numpy.equal và tài liệu gdal_calc. Tuy nhiên, vô ích.

Tệp raster được phân loại lại có các giá trị nguyên nằm trong khoảng từ 0 đến 11 và cũng bao gồm các giá trị 100 và 255. Sau đây hiển thị biểu thức phân loại (từ giá trị: đến giá trị):

gật đầu: 4, 0: 4, 1: 1, 2: 2, 3: 3, 4: 3, 5: 4, 6: 5, 7: 5, 8: 6, 9: 7, 10: 8, 100: nốt, 255: nốt,

Những gì tôi có thể làm là chọn tệp raster sẽ được phân loại lại bằng tkinter.FileDialog và lấy thông tin raster như geotransform và kích thước pixel với reclass = gdal.Open (raster, GA_ReadOnly).

Làm thế nào để tôi đi về giải quyết ở trên.

Điều đáng nói là các raster sẽ được phân loại lại có thể khá lớn trong một số trường hợp (500mb đến 5gb).


Có một ví dụ khác trên Blog
GeoExamples

@bennos, đã thử tập lệnh trên blog nhưng nó trả về lỗi bộ nhớ khi giải nén mảng.
PyMapr

Tôi đề nghị bạn thảo luận vấn đề này với Roger Veciana i Rovira, tác giả của bài đăng, vì anh ta biết mã của mình tốt hơn tôi và có thể biết cách giải quyết vấn đề
bennos

Thay đổi raster đầu vào từ 16Bit không dấu thành 8Bit không dấu đã giải quyết vấn đề bộ nhớ. Tuy nhiên, phải mất khoảng thời gian tương tự để phân loại lại như tập lệnh dmh126 bên dưới.
PyMapr

Câu trả lời:


6

Ở đây bạn có một kịch bản python đơn giản để phân loại lại, tôi đã viết nó và nó hoạt động với tôi:

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()

Chỉ cần thay đổi phạm vi.

Tôi mong nó sẽ có ích.


3
Bạn nên đóng file2bằng del file2hoặc file2 = Noneđể đảm bảo nó được ghi vào đĩa. .FlushCache()chỉ ảnh hưởng đến bộ đệm gạch nội bộ GDALs.
Kersten

@ dmh126, tôi đã sửa đổi các phạm vi và tập lệnh hoạt động. Tuy nhiên, nó không nhanh lắm (nhanh chóng gây tranh cãi). Trình raster đầu vào khoảng 120mb và mất khoảng 15 phút để hoàn thành. Với sự trợ giúp của gói viễn thám thương mại phải mất vài giây. Bất kỳ khuyến nghị về giảm thời gian xử lý?
PyMapr

Tôi nghĩ rằng đa luồng có thể giúp đỡ. Bạn có thể thử sử dụng tất cả các lõi của mình, có một câu hỏi gis.stackexchange.com/questions/162978/ chủ
dmh126

Không có nghĩa gì khi sử dụng vòng lặp đôi, xem câu trả lời bên dưới
Mattijn

Phải, phân loại lại vòng lặp và mỗi phần tử là chậm nhất trong tất cả các cách có thể để làm điều này. Sử dụng các phần mạnh mẽ của numpy như ufuncs: docs.scipy.org/doc/numpy-1.10.1/reference/ufuncs.html .
sgillies

16

Thay vì thực hiện phân loại lại như một vòng lặp đôi được mô tả bởi dmh126, hãy thực hiện bằng cách sử dụng 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

Trên một mảng 6163 x 3537 pixel (41,6mb), việc phân loại được thực hiện trong 1,59 giây, trong đó phải mất 12 phút 41 giây bằng cách sử dụng vòng lặp kép. Tổng cộng chỉ là tăng tốc 478x.

Tóm lại, không bao giờ sử dụng gấp đôi cho vòng lặp sử dụng numpy


Cảm ơn gợi ý, nhưng tôi nghĩ điều đó sẽ dẫn đến một vấn đề nếu các lớp đầu vào trùng với các lớp đầu ra. Tôi không muốn giá trị mới của mình bị thay đổi bởi quy tắc tiếp theo.
etrimaille

@Gustry - Chỉ cần gặp vấn đề ở đây.
Relima

Vì vậy, hãy kiểm tra câu trả lời của tôi dưới đây: gis.stackexchange.com/questions/163007/NH
etrimaille

6

Đây là một ví dụ cơ bản sử dụng rasterio và 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)

2

Chỉ cần hoàn thành câu trả lời từ @Mattijn, tôi nghĩ rằng điều đó sẽ dẫn đến một vấn đề nếu các lớp đầu vào trùng với các lớp đầu ra. Tôi không muốn giá trị mới của mình bị thay đổi bởi quy tắc tiếp theo.

Tôi không biết nếu tôi giảm tốc độ nhưng tôi nên làm một bản sao sâu:

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

Đây là một cách tiếp cận Rasterio khác mà tôi đã hack cùng nhau bằng Rasterio Cookbook và câu trả lời của @ 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

Trong một số trường hợp, số hóa numpy có thể hữu ích để nhanh chóng đi từ phạm vi đến thùng.

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

Với bảng màu raster RGB hỗ trợ:

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

Một sự thay thế hơi khác nhau có thể là như sau:

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()

Kịch bản này chơi với numpy.where ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html ): trong tất cả các bước ngoài bước cuối cùng, thay vì trả về giá trị khi điều kiện không được thỏa mãn, nó trả về np.where khác. Và nó tiếp tục cho đến khi tất cả các giá trị có thể của raster được xem xét.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.