Tìm phạm vi giới hạn tối thiểu của giá trị pixel đã cho trong raster?


9

Tôi tự hỏi nếu có một cách để tìm mức giới hạn tối thiểu cho một raster với một giá trị cụ thể. Tôi đã cắt một raster từ một hình ảnh toàn cầu và phạm vi được đặt là phạm vi toàn cầu với rất nhiều khu vực NoData. Tôi muốn xóa vùng NoData khỏi raster này và chỉ giữ lại vùng chứa các pixel của giá trị cụ thể. Tôi có thể làm cái này như thế nào?

Dưới đây là ví dụ của tôi: Tôi muốn trích xuất value = 1 (Vùng màu xanh) và sử dụng phạm vi của vùng màu xanh thay vì toàn bộ thế giới để xử lý thêm.

Hình ảnh mẫu


Bạn có thể gửi một mẫu?
Aaron

"Tôi muốn xóa các hàng và cột null cho raster này." Chính xác điều này có nghĩa là gì? Tôi không hiểu kết quả cuối cùng mong muốn là gì.
blah238

Theo "phạm vi giới hạn tối thiểu" bạn đang tìm kiếm một phạm vi hình chữ nhật hoặc dấu chân đa giác đại diện cho khu vực của hình ảnh với dữ liệu?
blah238

1
@Tomek, OP đang tìm cách để tìm mức độ, không cần phải tạo ra nó bằng tay.
blah238

1
Nếu nghĩa đen là bất cứ thứ gì là trò chơi công bằng, thì một số phần mềm có các lệnh tích hợp để thực hiện việc này; xem tài liệu tham khảo.wolfram.com/mathIALa/ref/ImageCrop.html chẳng hạn.
whuber

Câu trả lời:


6

NẾU tôi đã hiểu chính xác câu hỏi có vẻ như bạn muốn biết hộp giới hạn tối thiểu của các giá trị không phải là null. Có thể bạn có thể chuyển đổi raster thành đa giác, chọn đa giác bạn quan tâm và sau đó chuyển đổi chúng trở lại raster. Sau đó, bạn có thể xem các giá trị thuộc tính sẽ cung cấp cho bạn hộp giới hạn minium.


1
Tất cả nói rằng đây có lẽ là cách tiếp cận tốt nhất được đưa ra trong giới hạn của quy trình xử lý raster ArcGIS.
blah238

Tôi đã làm điều này chính xác. Có cách nào tự động không? Tôi nghĩ rằng thuật toán raster to polygon có một bước để trích xuất hộp giới hạn tối thiểu của raster.
Nhìn thấy

Bạn có sau một giải pháp python?
dango

8

Bí quyết là tính các giới hạn của dữ liệu có giá trị. Có lẽ cách nhanh nhất, tự nhiên nhất và tổng quát nhất để có được những điều này là với các tóm tắt khu vực: bằng cách sử dụng tất cả các ô không NoData cho vùng, các cực tiểu và tối đa của lưới chứa tọa độ X và Y sẽ cung cấp phạm vi đầy đủ.

ESRI tiếp tục thay đổi cách thức thực hiện các tính toán này; chẳng hạn, các biểu thức tích hợp cho các lưới tọa độ đã bị loại bỏ với ArcGIS 8 và dường như không quay trở lại. Để giải trí, đây là một tập hợp các phép tính đơn giản, nhanh chóng sẽ thực hiện công việc bất kể là gì.

  1. Chuyển đổi lưới thành một vùng duy nhất bằng cách đánh đồng nó với chính nó, như trong

    "Lưới của tôi" == "Lưới của tôi"

  2. Tạo lưới chỉ mục cột bằng cách tích lũy dòng chảy với lưới không đổi có giá trị 1. (Các chỉ mục sẽ bắt đầu bằng 0.) Nếu muốn, nhân số này với các ô và thêm tọa độ x của gốc để có được lưới tọa độ x " X "(hiển thị bên dưới).

  3. Tương tự, tạo lưới chỉ mục hàng ( và sau đó là lưới tọa độ y "Y") bằng cách tích lũy luồng lưới không đổi với giá trị 64.

  4. Sử dụng lưới vùng từ bước (1) để tính toán tối thiểu và tối đa của "X" và "Y" : bây giờ bạn có phạm vi mong muốn.

Hình ảnh cuối cùng

(Phạm vi, như thể hiện trong hai bảng thống kê khu vực, được mô tả bằng đường viền hình chữ nhật trong hình này. Lưới "I" là lưới vùng thu được ở bước (1).)

Để đi xa hơn, bạn sẽ cần trích xuất bốn số này từ các bảng đầu ra của chúng và sử dụng chúng để giới hạn phạm vi phân tích. Sao chép lưới ban đầu, với phạm vi phân tích hạn chế tại chỗ, hoàn thành nhiệm vụ.


8

Đây là phiên bản của phương thức @whubers cho ArcGIS 10.1+ dưới dạng hộp công cụ python (.pyt).

import arcpy

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Raster Toolbox"
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [ClipNoData]


class ClipNoData(object):
    def __init__(self):
        """Clip raster extent to the data that have values"""
        self.label = "Clip NoData"
        self.description = "Clip raster extent to the data that have values. "
        self.description += "Method by Bill Huber - https://gis.stackexchange.com/a/55150/2856"

        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        params = []

        # First parameter
        params+=[arcpy.Parameter(
            displayName="Input Raster",
            name="in_raster",
            datatype='GPRasterLayer',
            parameterType="Required",
            direction="Input")
        ]

        # Second parameter
        params+=[arcpy.Parameter(
            displayName="Output Raster",
            name="out_raster",
            datatype="DERasterDataset",
            parameterType="Required",
            direction="Output")
        ]

        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return arcpy.CheckExtension('spatial')==u'Available'

    def execute(self, parameters, messages):
        """See https://gis.stackexchange.com/a/55150/2856
           ##Code comments paraphrased from @whubers GIS StackExchange answer
        """
        try:
            #Setup
            arcpy.CheckOutExtension('spatial')
            from arcpy.sa import *
            in_raster = parameters[0].valueAsText
            out_raster = parameters[1].valueAsText

            dsc=arcpy.Describe(in_raster)
            xmin=dsc.extent.XMin
            ymin=dsc.extent.YMin
            mx=dsc.meanCellWidth
            my=dsc.meanCellHeight
            arcpy.env.extent=in_raster
            arcpy.env.cellSize=in_raster
            arcpy.AddMessage(out_raster)

            ## 1. Convert the grid into a single zone by equating it with itself
            arcpy.AddMessage(r'1. Convert the grid into a single zone by equating it with itself...')
            zones = Raster(in_raster) == Raster(in_raster)

            ## 2. Create a column index grid by flow-accumulating a constant grid
            ##    with value 1. (The indexes will start with 0.) Multiply this by
            ##    the cellsize and add the x-coordinate of the origin to obtain
            ##    an x-coordinate grid.
            arcpy.AddMessage(r'Create a constant grid...')
            const = CreateConstantRaster(1)

            arcpy.AddMessage(r'2. Create an x-coordinate grid...')
            xmap = (FlowAccumulation(const)) * mx + xmin

            ## 3. Similarly, create a y-coordinate grid by flow-accumulating a
            ##    constant grid with value 64.
            arcpy.AddMessage(r'3. Create a y-coordinate grid...')
            ymap = (FlowAccumulation(const * 64)) * my + ymin

            ## 4. Use the zone grid from step (1) to compute the zonal min and
            ##    max of "X" and "Y"
            arcpy.AddMessage(r'4. Use the zone grid from step (1) to compute the zonal min and max of "X" and "Y"...')

            xminmax=ZonalStatisticsAsTable(zones, "value", xmap,r"IN_MEMORY\xrange", "NODATA", "MIN_MAX")
            xmin,xmax=arcpy.da.SearchCursor(r"IN_MEMORY\xrange", ["MIN","MAX"]).next()

            yminmax=ZonalStatisticsAsTable(zones, "value", ymap,r"IN_MEMORY\yrange", "NODATA", "MIN_MAX")
            ymin,ymax=arcpy.da.SearchCursor(r"IN_MEMORY\yrange", ["MIN","MAX"]).next()

            arcpy.Delete_management(r"IN_MEMORY\xrange")
            arcpy.Delete_management(r"IN_MEMORY\yrange")

            # Write out the reduced raster
            arcpy.env.extent = arcpy.Extent(xmin,ymin,xmax+mx,ymax+my)
            output = Raster(in_raster) * 1
            output.save(out_raster)

        except:raise
        finally:arcpy.CheckInExtension('spatial')

Luke rất tốt. Tự chứa, có thể chạy, sử dụng in_memory và nhận xét tốt để khởi động. Tôi đã phải tắt xử lý nền (Xử lý địa lý > tùy chọn> ... ) để nó hoạt động.
matt wilkie

1
Tôi đã cập nhật tập lệnh và đặt canRunInBackground = false. Tôi sẽ lưu ý rằng đáng để thay đổi môi trường không gian làm việc / Scratchworkspace thành thư mục cục bộ (không phải FGDB) như tôi đã tìm thấy khi tôi để chúng làm mặc định (ví dụ: <hồ sơ người dùng mạng> \ Default.gdb) tập lệnh mất 9 phút !!! để chạy trên lưới ô 250x250. Thay đổi thành FGDB cục bộ, mất 9 giây và vào thư mục cục bộ 1-2 giây ...
user2856 22/03/13

điểm tốt về các thư mục cục bộ, và cảm ơn vì đã sửa lỗi nền nhanh (tốt hơn nhiều so với hướng dẫn viết cho mọi người tôi chuyển qua). Có thể đáng để ném nó lên bitbucket / github / gcode / vv.
matt wilkie 22/03/13

+1 Cảm ơn sự đóng góp này, Luke! Tôi đánh giá cao bạn điền vào khoảng trống (khá lớn) còn lại trong câu trả lời của tôi.
whuber

4

Tôi đã nghĩ ra một giải pháp dựa trên gdal và numpy. Nó phá vỡ ma trận raster thành các hàng và cột và thả bất kỳ hàng / cột trống nào. Trong triển khai này, "trống" là bất cứ thứ gì nhỏ hơn 1 và chỉ có các trình raster băng đơn.

(Tôi nhận ra khi tôi viết rằng cách tiếp cận quét này chỉ phù hợp với hình ảnh có "vòng cổ". Nếu dữ liệu của bạn là các đảo trong biển rỗng, không gian giữa các đảo cũng sẽ bị hủy bỏ, xâu chuỗi mọi thứ lại với nhau và làm rối tung cuộc hội thảo .)

Các bộ phận kinh doanh (cần làm sáng tỏ, sẽ không hoạt động như vậy):

    #read raster into a numpy array
    data = np.array(gdal.Open(src_raster).ReadAsArray())
    #scan for data
    non_empty_columns = np.where(data.max(axis=0)>0)[0]
    non_empty_rows = np.where(data.max(axis=1)>0)[0]
        # assumes data is any value greater than zero
    crop_box = (min(non_empty_rows), max(non_empty_rows),
        min(non_empty_columns), max(non_empty_columns))

    # retrieve source geo reference info
    georef = raster.GetGeoTransform()
    xmin, ymax = georef[0], georef[3]
    xcell, ycell = georef[1], georef[5]

    # Calculate cropped geo referencing
    new_xmin = xmin + (xcell * crop_box[0]) + xcell
    new_ymax = ymax + (ycell * crop_box[2]) - ycell
    cropped_transform = new_xmin, xcell, 0.0, new_ymax, 0.0, ycell

    # crop
    new_data = data[crop_box[0]:crop_box[1]+1, crop_box[2]:crop_box[3]+1]

    # write to disk
    band = out_raster.GetRasterBand(1)
    band.WriteArray(new_data)
    band.FlushCache()
    out_raster = None

Trong một kịch bản đầy đủ:

import os
import sys
import numpy as np
from osgeo import gdal

if len(sys.argv) < 2:
    print '\n{} [infile] [outfile]'.format(os.path.basename(sys.argv[0]))
    sys.exit(1)

src_raster = sys.argv[1]
out_raster = sys.argv[2]

def main(src_raster):
    raster = gdal.Open(src_raster)

    # Read georeferencing, oriented from top-left
    # ref:GDAL Tutorial, Getting Dataset Information
    georef = raster.GetGeoTransform()
    print '\nSource raster (geo units):'
    xmin, ymax = georef[0], georef[3]
    xcell, ycell = georef[1], georef[5]
    cols, rows = raster.RasterYSize, raster.RasterXSize
    print '  Origin (top left): {:10}, {:10}'.format(xmin, ymax)
    print '  Pixel size (x,-y): {:10}, {:10}'.format(xcell, ycell)
    print '  Columns, rows    : {:10}, {:10}'.format(cols, rows)

    # Transfer to numpy and scan for data
    # oriented from bottom-left
    data = np.array(raster.ReadAsArray())
    non_empty_columns = np.where(data.max(axis=0)>0)[0]
    non_empty_rows = np.where(data.max(axis=1)>0)[0]
    crop_box = (min(non_empty_rows), max(non_empty_rows),
        min(non_empty_columns), max(non_empty_columns))

    # Calculate cropped geo referencing
    new_xmin = xmin + (xcell * crop_box[0]) + xcell
    new_ymax = ymax + (ycell * crop_box[2]) - ycell
    cropped_transform = new_xmin, xcell, 0.0, new_ymax, 0.0, ycell

    # crop
    new_data = data[crop_box[0]:crop_box[1]+1, crop_box[2]:crop_box[3]+1]

    new_rows, new_cols = new_data.shape # note: inverted relative to geo units
    #print cropped_transform

    print '\nCrop box (pixel units):', crop_box
    print '  Stripped columns : {:10}'.format(cols - new_cols)
    print '  Stripped rows    : {:10}'.format(rows - new_rows)

    print '\nCropped raster (geo units):'
    print '  Origin (top left): {:10}, {:10}'.format(new_xmin, new_ymax)
    print '  Columns, rows    : {:10}, {:10}'.format(new_cols, new_rows)

    raster = None
    return new_data, cropped_transform


def write_raster(template, array, transform, filename):
    '''Create a new raster from an array.

        template = raster dataset to copy projection info from
        array = numpy array of a raster
        transform = geo referencing (x,y origin and pixel dimensions)
        filename = path to output image (will be overwritten)
    '''
    template = gdal.Open(template)
    driver = template.GetDriver()
    rows,cols = array.shape
    out_raster = driver.Create(filename, cols, rows, gdal.GDT_Byte)
    out_raster.SetGeoTransform(transform)
    out_raster.SetProjection(template.GetProjection())
    band = out_raster.GetRasterBand(1)
    band.WriteArray(array)
    band.FlushCache()
    out_raster = None
    template = None

if __name__ == '__main__':
    cropped_raster, cropped_transform = main(src_raster)
    write_raster(src_raster, cropped_raster, cropped_transform, out_raster)

Kịch bản nằm trong mã của tôi trên Github, nếu liên kết đi tìm 404 một chút; các thư mục này đã chín muồi cho một số tổ chức lại.


1
Điều này sẽ làm việc cho các bộ dữ liệu thực sự lớn. Tôi nhận được mộtMemoryError Source raster (geo units): Origin (top left): 2519950.0001220703, 5520150.00012207 Pixel size (x,-y): 100.0, -100.0 Columns, rows : 42000, 43200 Traceback (most recent call last): File "D:/11202067_COACCH/local_checkout/crop_raster.py", line 72, in <module> cropped_raster, cropped_transform = main(src_raster) File "D:/11202067_COACCH/local_checkout/crop_raster.py", line 22, in main data = np.array(raster.ReadAsArray()) MemoryError
user32882

2

Đối với tất cả sức mạnh phân tích của nó, ArcGIS thiếu các thao tác raster cơ bản mà bạn có thể tìm thấy với các trình chỉnh sửa hình ảnh máy tính để bàn truyền thống như GIMP . Chúng tôi hy vọng rằng bạn muốn sử dụng cùng một mức độ phân tích cho raster đầu ra của bạn như raster đầu vào của bạn, trừ khi bạn ghi đè thủ công cài đặt môi trường Extent đầu ra . Vì đây chính xác là những gì bạn đang tìm kiếm, không được đặt, nên cách làm của ArcGIS đang cản trở.

Bất chấp những nỗ lực tốt nhất của tôi, và không cần dùng đến lập trình, tôi không thể tìm ra cách nào để có được phạm vi tập hợp con mong muốn của bạn (mà không chuyển đổi raster sang vector gây lãng phí về mặt tính toán).

Thay vào đó, tôi đã sử dụng GIMP để chọn vùng màu xanh bằng cách sử dụng công cụ chọn màu và sau đó đảo ngược vùng chọn, nhấn Xóa để xóa phần còn lại của pixel, đảo ngược vùng chọn một lần nữa, cắt hình ảnh thành vùng chọn và cuối cùng xuất ra lại PNG. GIMP đã lưu nó dưới dạng hình ảnh có độ sâu 1 bit. Kết quả như sau:

Đầu ra

Tất nhiên, vì hình ảnh mẫu của bạn thiếu thành phần tham chiếu không gian và GIMP không nhận thức được về mặt không gian, hình ảnh đầu ra cũng hữu ích như đầu vào mẫu của bạn. Bạn sẽ cần định vị địa lý để sử dụng nó trong phân tích không gian.


Trên thực tế, thao tác này được sử dụng dễ dàng trong các phiên bản trước của Spatial Analyst: max zonal và min của hai lưới tọa độ (X và Y), sử dụng tính năng này làm vùng, đưa ra mức độ chính xác. (Chà, bạn có thể muốn mở rộng nó thêm một nửa ô theo cả bốn hướng.) Trong ArcGIS 10, bạn cần sáng tạo (hoặc sử dụng Python) để tạo lưới tọa độ. Bất kể, toàn bộ điều có thể được thực hiện hoàn toàn trong SA chỉ bằng các hoạt động lưới và không có sự can thiệp thủ công.
whuber

@whuber Tôi thấy giải pháp của bạn ở một nơi khác, nhưng vẫn không chắc làm thế nào tôi có thể thực hiện phương pháp của bạn. Bạn có thể chỉ cho tôi một số chi tiết hơn về điều này?
Nhìn thấy

@Seen Một tìm kiếm nhanh của trang web này tìm thấy một tài khoản của phương thức tại gis.stackexchange.com/a/13467 .
whuber

1

Đây là một khả năng sử dụng SAGA GIS: http://permalink.gmane.org/gmane.comp.gis.gdal.devel/33021

Trong SAGA GIS có mô-đun "Cắt dữ liệu" (trong thư viện mô-đun Công cụ lưới), thực hiện nhiệm vụ.

Nhưng điều này sẽ yêu cầu bạn nhập Geotif bằng mô đun nhập GDAL, xử lý nó trong SAGA và cuối cùng xuất lại dưới dạng Geotif một lần nữa với mô đun xuất GDAL.

Một khả năng khác chỉ sử dụng các công cụ ArcGIS GP là xây dựng TIN từ raster của bạn bằng Raster sang TIN , tính toán ranh giới của nó bằng TIN TINClip raster của bạn theo ranh giới (hoặc đường bao của nó sử dụng Envel Feature to Polygon ).

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.