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.

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:

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

So far I did not find any straight forward way to achieve this. I would be grateful for any help in this regard.
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:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With