Raster içinde verilen piksel değerinin minimum sınırlama kapsamını mı arıyorsunuz?


9

Belirli bir değere sahip bir tarama için minimum sınırlama kapsamını bulmanın bir yolu olup olmadığını merak ediyorum. Küresel bir görüntüden bir raster kırptım ve kapsamı birçok NoData alanı ile küresel kapsam olarak ayarlandı. Bu raster NoData alanını kaldırmak ve yalnızca belirli bir değerin piksel içeren alanı korumak istiyorum. Bunu nasıl yapabilirim?

İşte benim örnek: değer = 1 (Mavi alan) ayıklamak ve daha fazla işlem için tüm dünya yerine mavi alanın kapsamını kullanmak istiyorum.

Örnek Resim


Bir örnek gönderebilir misiniz?
Aaron

"Bu tarama için boş satırları ve sütunları silmek istiyorum." Bu tam olarak ne anlama geliyor? İstenen son sonucun ne olduğunu anlamıyorum.
blah238

"Minimum sınırlama derecesi" ile, görüntünün alanını veri ile temsil eden dikdörtgen bir boyut veya çokgen bir ayak izi mi arıyorsunuz?
blah238

1
@Tomek, OP kapsamı bulmak istiyor , manuel olarak oluşturmak zorunda değil.
blah238

1
Kelimenin tam anlamıyla bir şey adil bir oyunsa, bazı yazılımların bunu yapmak için yerleşik komutları vardır; bkz. reference.wolfram.com/mathematica/ref/ImageCrop.html .
whuber

Yanıtlar:


6

Soruyu doğru anladıysam, boş olmayan değerlerin minimum sınırlama kutusunu bilmek istediğiniz gibi geliyorsa, rasterleri çokgenlere dönüştürebilir, ilgilendiğiniz çokgenleri seçebilir ve daha sonra bir raster haline dönüştürebilirsiniz. Daha sonra, size minium sınırlama kutusunu vermesi gereken özellik değerlerine bakabilirsiniz.


1
Hepsi, ArcGIS raster işleme iş akışının sınırları göz önüne alındığında, bunun muhtemelen en iyi yaklaşım olduğunu söyledi.
blah238

Bunu tam olarak yaptım. Otomatik bir yol var mı? Raster-çokgen algoritmasının, rasterin minimum sınırlayıcı kutusunu ayıklamak için bir adımı olduğunu düşünüyorum.
görüldü

Python çözümünden sonra mısın?
dango

8

İşin püf noktası, değerleri olan verilerin sınırlarını hesaplamaktır. Belki de bunları elde etmenin en hızlı, en doğal ve en genel yolu bölgesel özetlerdir: bölge için tüm NoData olmayan hücreleri kullanarak, X ve Y koordinatlarını içeren ızgaraların minimum ve maksimum ızgaraları tam olarak sağlayacaktır.

ESRI bu hesaplamaların yapılma yollarını değiştirmeye devam ediyor; örneğin, koordinat ızgaraları için yerleşik ifadeler ArcGIS 8 ile düşürüldü ve geri dönmemiş gibi görünüyor. Sadece eğlence için, iş ne olursa olsun işi yapacak bir dizi hızlı, basit hesaplamalar.

  1. Izgarayı olduğu gibi kendisiyle eşitleyerek tek bir bölgeye dönüştürün

    "Izgaram" == "Izgaram"

  2. 1 değerine sahip sabit bir ızgara akışını biriktirerek bir sütun dizin ızgarası oluşturun. (Dizinler 0 ile başlayacaktır.) İsterseniz, bunu hücre boyutuyla çarpın ve x koordinat ızgarası elde etmek için başlangıç ​​noktasının x koordinatını ekleyin " X "(aşağıda gösterilmiştir).

  3. Benzer şekilde, 64 değerine sahip sabit bir ızgara akışını biriktirerek bir satır dizin ızgarası ( ve ardından bir y koordinat ızgarası "Y") oluşturun.

  4. "X" ve "Y" bölgelerinin min ve maks'lerini hesaplamak için adım (1) 'deki bölge ızgarasını kullanın : artık istediğiniz boyuta sahipsiniz.

Son görüntü

(İki bölgesel istatistik tablosunda gösterildiği gibi, bu şekilde dikdörtgen bir taslakla gösterilmiştir. Izgara "I", adım (1) 'de elde edilen bölge ızgarasıdır.)

Daha ileri gitmek için, bu dört sayıyı çıktı tablolarından çıkarmanız ve analiz kapsamını sınırlamak için kullanmanız gerekir. Orijinal ızgarayı, sınırlı analiz kapsamı yerinde olacak şekilde kopyalamak görevi tamamlar.


8

İşte bir python araç kutusu (.pyt) olarak ArcGIS 10.1+ için @whubers yönteminin bir sürümü .

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

Çok iyi Luke. Bağımsız, çalıştırılabilir, in_memory kullanır ve önyükleme yapmak için iyi yorum yapar. Çalışması için arka plan işlemeyi ( Geoprocessing> options> ... ) kapatmak zorunda kaldım .
matt wilkie

1
Komut dosyasını güncelledim ve canRunInBackground = False ayarladım. Onları varsayılan olarak bıraktığımda bulduğum gibi çalışma alanı / scratchworkspace ortamlarını yerel bir klasöre (FGDB değil) değiştirmeye değer olduğuna dikkat edeceğim (yani <ağ kullanıcı profili> \ Default.gdb) komut dosyası 9 dakika sürdü !!! 250x250 hücre ızgarasında çalıştırmak için. Yerel bir FGDB değiştirme 9 saniye ve yerel bir klasör 1-2 saniye aldı ...
user2856

Yerel klasörler hakkında iyi bir noktaya ve hızlı arka plan düzeltmesi için teşekkürler (ilettiğim herkes için talimatlar yazmaktan çok daha iyi). Bu bitbucket / github / gcode / etc üzerine atmaya değer olabilir.
matt wilkie

+1 Bu katkı için teşekkür ederiz Luke! Cevabımda kalan (oldukça büyük) boşluğu doldurduğunuz için teşekkür ederim.
whuber

4

Gdal ve numpy tabanlı bir çözüm tasarladım. Raster matrisini satırlara ve sütunlara böler ve boş satırları / sütunları bırakır. Bu uygulamada "boş" 1'den az bir şeydir ve sadece tek bant rasters hesaplanmıştır.

(Bu tarama çizgisi yaklaşımının yalnızca nodata "yaka" içeren görüntüler için uygun olduğunu anladım. Verileriniz null denizlerindeki adalarsa, adalar arasındaki boşluk da düşecek, her şeyi bir araya getirecek ve coğrafi referansı tamamen bozacak. .)

İş bölümleri (ete ihtiyacı var, olduğu gibi çalışmaz):

    #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

Tam komut dosyasında:

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)

Senaryo olduğunu kodum zulası bağlantı biraz etrafına 404 avı giderse, Github; bu klasörler bazı yeniden düzenlemeler için olgunlaşmıştır.


1
Bu gerçekten büyük veri kümeleri için işe yaramayacak. AnladımMemoryError 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

Tüm analitik gücü için ArcGIS, GIMP gibi geleneksel masaüstü görüntü editörleriyle bulabileceğiniz temel tarama işlemlerinden yoksundur . Çıktı Genişletme ortamı ayarını el ile geçersiz kılmadığınız sürece, çıktı rasterinizle aynı analiz kapsamını kullanmak istediğinizi bekler . Bu tam olarak bulmak istediğiniz şey olduğundan, ayarlanmadığından, ArcGIS'in bir şeyler yapma şekli engel oluyor.

En iyi çabalarıma rağmen ve programlamaya başvurmadan, görüntünün istediğiniz alt kümesinin kapsamını elde etmenin bir yolunu bulamadım (hesapsal olarak boşa giden raster-vektör dönüşümü olmadan).

Bunun yerine, GIMP'i kullanarak renk seçim aracını kullanarak mavi alanı seçmek için kullandım ve ardından seçimi tersine çevirdim, piksellerin geri kalanını kaldırmak için Sil'e basın, seçimi tekrar ters çevirdim, görüntüyü seçime kırpıp en sonunda tekrar dışa aktardım PNG. GIMP 1 bit derinlikli bir görüntü olarak kaydetti. Sonuç aşağıdadır:

Çıktı

Elbette, örnek görüntünüzde uzamsal bir referans bileşeni bulunmadığından ve GIMP uzamsal olarak farkında olmadığından, çıktı görüntüsü örnek girişiniz kadar faydalıdır. Bir uzamsal analizde kullanılabilmesi için coğrafi referans almanız gerekecektir .


Aslında, bu işlem kullanılan Spatial Analyst önceki versiyonları kolay olması için: iki bölgesel maksimum ve minimum, bölge olarak özelliğini kullanarak, ızgaralar (X ve Y) koordinatı tam ölçüde verir. (Eh, dört yönde de hücre boyutunun yarısı kadar genişletmek isteyebilirsiniz.) ArcGIS 10'da, bir koordinat ızgarası oluşturmak için yaratıcı olmanız (veya Python kullanmanız) gerekir. Ne olursa olsun, her şey olabilir sadece ızgara işlemleri ve hiçbir manuel müdahale kullanarak SA içerisinde tamamlanabilir.
whuber

@whuber Çözümünüzü başka bir yerde gördüm, ancak yönteminizi nasıl uygulayabileceğimden hala emin değilim. Bunun hakkında biraz daha ayrıntı verebilir misiniz?
Görülme

@Seen Bu siteyi hızlı bir şekilde aramak, yöntemin bir hesabını gis.stackexchange.com/a/13467 adresinde bulur .
whuber

1

İşte SAGA GIS'i kullanmanın bir yolu: http://permalink.gmane.org/gmane.comp.gis.gdal.devel/33021

SAGA GIS'de görevi gerçekleştiren bir "Verilere Kırp" modülü (Izgara Araçları modülü kitaplığında) vardır.

Ancak bu, Geotif'inizi GDAL içe aktarma modülüyle içe aktarmanızı, SAGA'da işlemenizi ve son olarak tekrar GDAL dışa aktarma modülüyle Geotif olarak dışa aktarmanızı gerektirir.

Sadece ArcGIS GP araçlarını kullanarak başka olasılık kullanarak rasterdan TIN kurmanın ne TENEKE Raster , kullanarak sınır hesaplamak TENEKE Domain ve Klip sınır (veya kullanarak zarf ederek raster Poligon için Özelliği Zarf ).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.