11

Trying to process a large satellite image (~10GB). For memory efficient processing chunk of image (block/tile) is being loaded into memory in each iteration.

Tiled Image

The sample code for this as below:

def process_image(src_img, dst_img, band_id=1):
    with rasterio.open(src_img) as src:
        kwargs = src.meta
        tiles = src.block_windows(band_id)
        with rasterio.open(dst_img, 'w', **kwargs) as dst:
            for idx, window in tiles:
                print("Processing Block: ", idx[0]+1, ", ", idx[1]+1)
                src_data = src.read(band_id, window=window)
                dst_data = src_data ** 2 # Do the Processing Here
                dst.write_band( band_id, dst_data, window=window)
    return 0

However, for any kind of processing which requires kernel wise operation (such as any convolve filter like smoothing) this leads to an issue in processing near the edges of the blocks. To address this, each block should be slightly overlapped as shown below:

Overlapped Zoomed

My objective is to load the blocks as the following where the amount of overlap can be adjusted according to the need.

Overlapped Tile

So far I did not find any straight forward way to achieve this. I would be grateful for any help in this regard.

tachyon
  • 471
  • 3
  • 14

1 Answers1

7

You can just expand your windows:

import rasterio as rio
from rasterio import windows

def overlapping_blocks(src, overlap=0, band=1):
    nols, nrows = src.meta['width'], src.meta['height']
    big_window = windows.Window(col_off=0, row_off=0, width=nols, height=nrows)
    for ji, window in src.block_windows(band):

        if overlap == 0:
            yield ji, window

        else:
            col_off = window.col_off - overlap
            row_off = window.row_off - overlap
            width = window.width + overlap * 2
            height = window.height + overlap * 2
            yield ji, windows.Window(col_off, row_off, width, height).intersection(big_window)

And you would use it like so:

def process_image(src_img, dst_img, band_id=1):
    with rasterio.open(src_img) as src:
        kwargs = src.meta
        with rasterio.open(dst_img, 'w', **kwargs) as dst:
            for idx, window in overlapping_block_windows(src, overlap=1, band=band_id):
                print("Processing Block: ", idx[0]+1, ", ", idx[1]+1)
                src_data = src.read(band_id, window=window)
                dst_data = src_data ** 2 # Do the Processing Here
                dst.write_band( band_id, dst_data, window=window)
    return 0

And here is a way to overlap both block windows and non-block windows, with an additional argument boundless to specify whether to extend windows beyond the raster extent, useful for boundless reading:

from itertools import product
import rasterio as rio
from rasterio import windows


def overlapping_windows(src, overlap, width, height, boundless=False):
    """"width & height not including overlap i.e requesting a 256x256 window with 
        1px overlap will return a 258x258 window (for non edge windows)"""
    offsets = product(range(0, src.meta['width'], width), range(0, src.meta['height'], height))
    big_window = windows.Window(col_off=0, row_off=0, width=src.meta['width'], height=src.meta['height'])
    for col_off, row_off in offsets:

        window = windows.Window(
            col_off=col_off - overlap,
            row_off=row_off - overlap,
            width=width + overlap * 2,
            height=height + overlap * 2)

        if boundless:
            yield window
        else:
            yield window.intersection(big_window)


def overlapping_blocks(src, overlap=0, band=1, boundless=False):

    big_window = windows.Window(col_off=0, row_off=0, width=src.meta['width'], height=src.meta['height'])
    for ji, window in src.block_windows(band):

        if overlap == 0:
            yield window

        else:
            window = windows.Window(
                col_off=window.col_off - overlap,
                row_off=window.row_off - overlap,
                width=window.width + overlap * 2,
                height=window.height + overlap * 2)
            if boundless:
                yield window
            else:
                yield window.intersection(big_window)

Output windows: enter image description here

user2856
  • 1,145
  • 1
  • 10
  • 24
  • 1
    Since neither `GDAL` nor `rasterio` provides an convenient way to do it, this is the work around for now I guess. I started a [thread](https://rasterio.groups.io/g/main/message/143) in [rasterio main group](https://rasterio.groups.io/g/main) for discussion regarding implementing this feature into `rasterio`. – tachyon Feb 05 '19 at 06:51
  • @tachyon - see bugfix edit. Original version was clipping off some pixels from right and lowr edges. – user2856 Feb 06 '19 at 23:58