การหาขอบเขตขอบเขตขั้นต่ำของค่าพิกเซลที่กำหนดภายในแรสเตอร์?


9

ฉันสงสัยว่ามีวิธีหาขอบเขตขอบเขตขั้นต่ำสำหรับแรสเตอร์ที่มีค่าเฉพาะหรือไม่ ฉันตัดแรสเตอร์จากภาพทั่วโลกและกำหนดขอบเขตเป็นระดับโลกที่มีพื้นที่ NoData จำนวนมาก ฉันต้องการลบพื้นที่ NoData ออกจากแรสเตอร์นี้และเก็บเฉพาะพื้นที่ที่มีพิกเซลของค่าเฉพาะ ฉันจะทำสิ่งนี้ได้อย่างไร

นี่คือตัวอย่างของฉัน: ฉันต้องการแยก value = 1 (พื้นที่สีน้ำเงิน) และใช้ขอบเขตของพื้นที่สีน้ำเงินมากกว่าโลกทั้งโลกเพื่อการประมวลผลเพิ่มเติม

ภาพตัวอย่าง


คุณช่วยโพสต์ตัวอย่างได้ไหม?
แอรอน

"ฉันต้องการลบแถวและคอลัมน์ว่างสำหรับแรสเตอร์นี้" สิ่งนี้หมายความว่าอย่างไร ฉันไม่เข้าใจว่าผลลัพธ์ที่ต้องการคืออะไร
blah238

โดย "ขอบเขตขอบเขตขั้นต่ำ" คุณกำลังมองหาขอบเขตสี่เหลี่ยมหรือรอยเท้ารูปหลายเหลี่ยมที่แสดงพื้นที่ของรูปภาพด้วยข้อมูลหรือไม่
blah238

1
@Tomek ผู้ใช้ OP ต้องการหาขอบเขตไม่จำเป็นต้องสร้างมันเอง
blah238

1
หากสิ่งใดเป็นเกมที่ยุติธรรมซอฟต์แวร์บางตัวก็มีคำสั่งในตัวเพื่อทำสิ่งนี้ ดูreference.wolfram.com/mathematica/ref/ImageCrop.htmlเป็นต้น
whuber

คำตอบ:


6

ถ้าฉันเข้าใจคำถามอย่างถูกต้องดูเหมือนว่าคุณต้องการรู้ค่าขอบเขตขั้นต่ำของค่าที่ไม่ใช่ค่า null บางทีคุณสามารถแปลงแรสเตอร์เป็นรูปหลายเหลี่ยมเลือกรูปหลายเหลี่ยมที่คุณสนใจแล้วแปลงกลับเป็นแรสเตอร์ จากนั้นคุณสามารถดูค่าคุณสมบัติที่ควรให้กล่องขอบเขต minium


1
ทั้งหมดบอกว่านี่อาจเป็นวิธีที่ดีที่สุดเนื่องจากขอบเขตของกระบวนการประมวลผลแรสเตอร์ ArcGIS
blah238

ฉันทำสิ่งนี้อย่างแน่นอน มีวิธีอัตโนมัติไหม? ฉันคิดว่าอัลกอริทึมแรสเตอร์ถึงรูปหลายเหลี่ยมนั้นมีขั้นตอนในการแยกกล่องขอบเขตขั้นต่ำของแรสเตอร์
เห็น

คุณเป็นโซลูชันไพ ธ อนไหม
dango

8

เคล็ดลับคือการคำนวณขีด จำกัด ของข้อมูลที่มีค่า บางทีวิธีที่เร็วที่สุดเป็นธรรมชาติที่สุดและเป็นวิธีทั่วไปที่สุดในการรับสิ่งเหล่านี้คือสรุปโดยใช้เซลล์ที่ไม่ใช่ NoData ทั้งหมดสำหรับโซน, นาทีโซนและค่าสูงสุดของกริดที่มีพิกัด X และ Y จะให้ขอบเขตเต็มที่

ESRI ยังคงเปลี่ยนแปลงวิธีการที่การคำนวณเหล่านี้สามารถทำได้ ตัวอย่างเช่นการแสดงออกในตัวสำหรับกริดพิกัดถูกทิ้งไว้กับ ArcGIS 8 และดูเหมือนจะไม่กลับมา เพื่อความสนุกนี่คือชุดการคำนวณที่ง่ายและรวดเร็วที่จะทำงานได้ไม่ว่าอะไรจะเกิดขึ้น

  1. แปลงกริดให้เป็นโซนเดียวโดยการสร้างมันขึ้นมาเอง

    "My grid" == "My grid"

  2. สร้างกริดดัชนีคอลัมน์โดยการสะสมกริดคงที่ที่มีค่า 1 (ดัชนีจะเริ่มต้นด้วย 0) หากต้องการให้คูณจำนวนนี้ด้วยเซลล์และเพิ่มพิกัด x ของต้นกำเนิดเพื่อรับกริดพิกัด x " X "(แสดงด้านล่าง)

  3. ในทำนองเดียวกันสร้างตารางดัชนีแถว ( แล้วตารางพิกัด y "Y") โดยการสะสมกระแสคงที่ตารางที่มีค่า 64

  4. ใช้กริดโซนจากขั้นตอน (1) เพื่อคำนวณค่าต่ำสุดและสูงสุดของ "X" และ "Y" : ตอนนี้คุณมีขอบเขตที่ต้องการ

ภาพสุดท้าย

(ขอบเขตดังที่แสดงในตารางสถิติสองตารางจะแสดงด้วยโครงร่างสี่เหลี่ยมผืนผ้าในรูปนี้ Grid "I" เป็นตารางโซนที่ได้รับในขั้นตอน (1))

ในการดำเนินการต่อไปคุณจะต้องแยกตัวเลขสี่ตัวเหล่านี้ออกจากตารางผลลัพธ์และใช้เพื่อ จำกัด ขอบเขตการวิเคราะห์ การคัดลอกกริดเดิมโดยมีขอบเขตการวิเคราะห์ที่ จำกัด อยู่จึงทำให้งานเสร็จสมบูรณ์


8

ต่อไปนี้เป็นรุ่นของวิธีการ @whubersสำหรับ ArcGIS 10.1+ เป็นกล่องเครื่องมือหลาม (.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')

ลุคที่ดีมาก มีอยู่ในตัว runnable ใช้ in_memory และมีความเห็นดีในการบูต ฉันต้องปิดการประมวลผลพื้นหลัง (การประมวลผลทางภูมิศาสตร์> ตัวเลือก> ... ) เพื่อให้มันทำงานได้
matt wilkie

1
ฉันได้อัปเดตสคริปต์และตั้งค่า canRunInBackground = False ฉันจะทราบว่ามันคุ้มค่าที่จะเปลี่ยนสภาพแวดล้อมเวิร์กสเปซ / scratchworkspace เป็นโฟลเดอร์ท้องถิ่น (ไม่ใช่ FGDB) ตามที่ฉันพบเมื่อฉันปล่อยให้เป็นค่าเริ่มต้น (เช่น <โปรไฟล์ผู้ใช้เครือข่าย> \ Default.gdb) สคริปต์ใช้เวลา 9 นาที !!! เพื่อเรียกใช้บนกริดเซลล์ 250x250 เปลี่ยนเป็น FGDB ท้องถิ่นใช้เวลา 9 วินาทีและไปยังโฟลเดอร์ท้องถิ่น 1-2sec ...
2856

จุดดีเกี่ยวกับโฟลเดอร์ในเครื่องและขอขอบคุณสำหรับการแก้ไขพื้นหลังอย่างรวดเร็ว (ดีกว่าการเขียนคำแนะนำสำหรับทุกคนที่ฉันส่งให้) อาจคุ้มค่าที่จะขว้างสิ่งนี้บน bitbucket / github / gcode / etc
matt wilkie

+1 ขอบคุณสำหรับการมีส่วนร่วมนี้ลุค! ฉันซาบซึ้งที่คุณเติมในช่องว่าง (ค่อนข้างใหญ่) ที่เหลืออยู่ในคำตอบของฉัน
whuber

4

ฉันได้คิดวิธีแก้ปัญหาแบบ gdal และ numpy มันแบ่งเมทริกซ์แรสเตอร์ออกเป็นแถวและคอลัมน์แล้วปล่อยแถว / คอลัมน์ว่าง ๆ ในการดำเนินการนี้ "ว่าง" คืออะไรน้อยกว่า 1 และมีเพียง rasters วงเดียวคิด

(ฉันรู้ว่าขณะที่ฉันเขียนว่าวิธีสแกนไลน์นี้เหมาะสำหรับรูปภาพที่มี "ปลอกคอ" เท่านั้นถ้าข้อมูลของคุณเป็นเกาะในทะเลที่มีค่า Null พื้นที่ว่างระหว่างเกาะจะลดลงเช่นกัน Squishing ทุกอย่างเข้าด้วยกัน .)

ส่วนธุรกิจ (ต้องการความแตกต่างไม่สามารถทำงานได้ตามปกติ):

    #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

ในสคริปต์เต็ม:

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)

สคริปต์อยู่ในโค้ดของฉันบน Github หากลิงก์ไป 404 ตามล่าไปหน่อย โฟลเดอร์เหล่านี้จะสุกงอมสำหรับการจัดระเบียบใหม่


1
สิ่งนี้จะไม่ทำงานสำหรับชุดข้อมูลที่มีขนาดใหญ่จริงๆ ฉันได้รับMemoryError 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

สำหรับทุกพลังการวิเคราะห์ของ ArcGIS ขาดกิจวัตร raster พื้นฐานที่คุณสามารถหากับบรรณาธิการภาพสก์ท็อปแบบดั้งเดิมเช่นGIMP คาดว่าคุณต้องการใช้ขอบเขตการวิเคราะห์เดียวกันสำหรับเอาท์พุทแรสเตอร์ของคุณเป็นอินสแตนซ์อินพุทของคุณเว้นแต่คุณจะแทนที่การตั้งค่าสภาพแวดล้อมเอาท์พุทขอบเขตด้วยตนเอง เนื่องจากสิ่งนี้เป็นสิ่งที่คุณกำลังค้นหาไม่ได้ตั้งค่าวิธีการทำสิ่งต่าง ๆ ของ ArcGIS ก็กำลังเข้ามา

แม้ว่าฉันจะพยายามอย่างดีที่สุดและไม่ต้องใช้การเขียนโปรแกรม แต่ฉันก็ไม่สามารถหาขอบเขตของรูปภาพที่คุณต้องการได้ (โดยไม่ต้องแปลงเป็น raster-to-vector

แต่ฉันใช้ GIMP เพื่อเลือกพื้นที่สีน้ำเงินโดยใช้เครื่องมือเลือกสีจากนั้นกลับรายการที่เลือกกด Delete เพื่อลบส่วนที่เหลือของพิกเซลกลับรายการที่เลือกกลับไปตัดส่วนที่เลือกแล้วตัดออกเป็น PNG GIMP บันทึกเป็นภาพความลึก 1 บิต ผลลัพธ์อยู่ด้านล่าง:

เอาท์พุต

แน่นอนเนื่องจากภาพตัวอย่างของคุณไม่มีองค์ประกอบอ้างอิงเชิงพื้นที่และ GIMP ไม่ทราบเชิงพื้นที่ภาพที่ส่งออกจึงมีประโยชน์มากเท่ากับอินพุตตัวอย่างของคุณ คุณจะต้องระบุตำแหน่งทางภูมิศาสตร์เพื่อใช้ในการวิเคราะห์เชิงพื้นที่


ที่จริงแล้วการดำเนินการนี้เคยเป็นเรื่องง่ายใน Spatial Analyst รุ่นก่อนหน้านี้: ค่าสูงสุดและค่าต่ำสุดของพิกัดกริดทั้งสอง (X และ Y) โดยใช้คุณลักษณะเป็นโซนให้ขอบเขตอย่างแท้จริง (คุณอาจต้องการขยายขนาดเซลล์ครึ่งหนึ่งในสี่ทิศทาง) ใน ArcGIS 10 คุณต้องสร้างสรรค์ (หรือใช้ Python) เพื่อสร้างตารางพิกัด ไม่ว่าทั้งหมดสามารถทำได้ทั้งหมดภายใน SA โดยใช้การทำงานแบบกริดเท่านั้นและไม่มีการแทรกแซงด้วยตนเอง
whuber

@ เมื่อฉันเห็นโซลูชันของคุณที่อื่น แต่ก็ยังไม่แน่ใจว่าฉันจะใช้วิธีการของคุณได้อย่างไร คุณช่วยแสดงรายละเอียดเพิ่มเติมให้ฉันดูได้ไหม
เห็น

@Seen ค้นหาอย่างรวดเร็วของเว็บไซต์นี้พบบัญชีของวิธีการที่ได้gis.stackexchange.com/a/13467
whuber

1

นี่คือความเป็นไปได้ประการหนึ่งที่ใช้ SAGA GIS: http://permalink.gmane.org/gmane.comp.gis.gdal.devel/33021

ใน SAGA GIS มีโมดูล "ครอบตัดข้อมูล" (ในไลบรารีโมดูลเครื่องมือกริด) ซึ่งทำงาน

แต่สิ่งนี้จะทำให้คุณต้องนำเข้า Geotif ของคุณด้วยโมดูลการนำเข้า GDAL ประมวลผลใน SAGA และส่งออกเป็น Geotif อีกครั้งด้วย GDAL export module

ความเป็นไปได้อีกอย่างหนึ่งที่ใช้เครื่องมือ ArcGIS GP เพียงอย่างเดียวคือสร้าง TIN จากแรสเตอร์ของคุณโดยใช้Raster ถึง TINคำนวณขอบเขตโดยใช้TIN Domainและคลิปแรสเตอร์ของคุณตามขอบเขต (หรือซองจดหมายโดยใช้Feature Envelope to Polygon )

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.