The previous article introduced common techniques for making deep learning sample pairs for remote sensing images. However, not all data is so regular in actual use. For example, the input data and labels may have inconsistent spatial ranges. Follow the official account GeodataAnalysis
and reply to 20230526 to get sample data and codes. The codes of this series are all put together, and it is easier to understand the codes by running them.
When the spatial range of the input data and the label is inconsistent, it is necessary to consider whether the pixels of the two have one-to-one correspondence in space, and then use different methods to process the data:
- When one-to-one correspondence is possible: directly extract the overlapping range of the two, and make labels based on the overlapping parts.
- When one-to-one correspondence is not possible: resample to make one-to-one correspondence.
So in general, there are only three key steps, extracting the overlapping range, making labels according to the overlapping part using the method of Tutorial 1, and resampling. The following introduces these three points separately. If you want to know more about it, you can download the code and data and run it yourself. Here only the key steps are introduced, and there are not many specific introductions.
1 Calculate the overlapping range of two images
The two images required here must have one-to-one correspondence between pixels. For example, the neutralization of the sample data is an example where the LC08_L1TP_018030_20150907_20200908_02_T1_B1.TIF
spatial LC08_L1TP_018030_20150923_20200908_02_T1_B1.TIF
range is inconsistent, but the pixels are one-to-one correspondence.
def overlap(src1, src2):
gt = src1.transform
w, h = src1.width, src1.height
src1_extent = (gt[2], gt[2]+gt[0]*w, gt[5], gt[5]+gt[4]*h)
gt = src2.transform
w, h = src2.width, src2.height
src2_extent = (gt[2], gt[2]+gt[0]*w, gt[5], gt[5]+gt[4]*h)
mask_extent = (
max((src1_extent[0], src2_extent[0])), # west
min((src1_extent[1], src2_extent[1])), # east
min((src1_extent[2], src2_extent[2])), # north
max((src1_extent[3], src2_extent[3])) # south
)
min_row, min_col = src1.index(mask_extent[0], mask_extent[2])
max_row, max_col = src1.index(mask_extent[1], mask_extent[3])
src1_window = Window.from_slices((min_row, max_row), (min_col, max_col))
min_row, min_col = src2.index(mask_extent[0], mask_extent[2])
max_row, max_col = src2.index(mask_extent[1], mask_extent[3])
src2_window = Window.from_slices((min_row, max_row), (min_col, max_col))
src1_array = src1.read(1, window=src1_window)
src2_array = src2.read(1, window=src2_window)
src1_mask = np.ones(shape=src1_array.shape[-2:])
mask = src1_array==src1.nodata
src1_mask[mask] = 0
src2_mask = np.ones(shape=src2_array.shape[-2:])
mask = src2_array==src2.nodata
src2_mask[mask] = 0
mask = (src1_mask == 1) & (src2_mask == 1)
transform = from_bounds(mask_extent[0], mask_extent[-1], mask_extent[1],
mask_extent[-2], mask.shape[1], mask.shape[0])
return src1_window, src2_window, mask, transform
2 Make samples based on overlapping ranges
data preparation.
ids = ['LC08_L1TP_018030_20150907_20200908_02_T1', 'LC08_L1TP_018030_20150923_20200908_02_T1']
id_types = ['img', 'label']
img_src = rio.open(f'./data/{
ids[0]}/{
ids[0]}_B1.TIF')
label_src = rio.open(f'./data/{
ids[1]}/{
ids[1]}_B1.TIF')
img_window, label_window, mask, transform = overlap(img_src, label_src)
Sample making.
image_size, slide = 256, 256
# 用于记录每个影像块的位置
df = pd.DataFrame(columns=['patch', 'id', 'row', 'col', 'image_size', 'type'])
patch_num = 0
patch_pair = 1
height, width = mask.shape
for row in range((height - image_size) // slide + 1):
for col in range((width - image_size) // slide + 1):
for window, id, id_type in zip([img_window, label_window], ids, id_types):
patch_row = window.row_off + row*slide
patch_col = window.col_off + col*slide
mask_patch = mask[row*slide: row*slide + image_size,
col*slide: col*slide + image_size]
if not np.all(mask_patch):
break
df.loc[patch_num, 'patch'] = patch_pair
df.loc[patch_num, 'type'] = id_type
df.loc[patch_num, 'id'] = id
df.loc[patch_num, 'row'] = patch_row
df.loc[patch_num, 'col'] = patch_col
if patch_num % 2 == 1:
patch_pair += 1
patch_num += 1
df['image_size'] = image_size
df.head()
3 resampling
The resolution of sample data 1 is 30 meters, and the resolution of sample data 2 is 480 meters (the land cover data downloaded in Tutorial 2). If there is a one-to-one correspondence between the two pixels, one pixel of the sample will cover 16 of the sample 1. pixels, but the current data is biased, so first resample example 2.
read data.
src1 = rio.open(path1)
src2 = rio.open(path2)
Obtain the coordinate information of Example 1.
gt = src1.transform
w, h = src1.width, src1.height
src1_extent = (gt[2], gt[2]+gt[0]*w, gt[5], gt[5]+gt[4]*h)
Computes the number of rows and columns of the example after resampling.
src2_res = src2.transform[0]
src2_w = (src1_extent[1] - src1_extent[0]) // src2_res
src2_h = (src1_extent[2] - src1_extent[3]) // src2_res
Calculate the spatial extent after resampling for Example 2.
src2_extent = [src1_extent[0], src1_extent[0]+src2_w*src2_res,
src1_extent[2], src1_extent[2]-src2_h*src2_res]
Calculate the GeoTransform after resampling in Example 2, that is, the six parameters of the affine transformation.
dst_transform, dst_width, dst_height = calculate_default_transform(
src_crs=src2.crs,
dst_crs=src2.crs,
width=src2_w,
height=src2_h,
left=src2_extent[0],
bottom=src2_extent[3],
right=src2_extent[1],
top=src2_extent[2]
)
Perform resampling, and save as tif.
dst_array = np.empty((dst_height, dst_width), dtype=np.uint8)
reproject(
# 源文件参数
source=src2.read(1),
src_crs=src2.crs,
src_transform=src2.transform,
# 目标文件参数
destination=dst_array,
dst_transform=dst_transform,
dst_crs=src2.crs,
num_threads=1
)
array_to_tif('reproject.tif', dst_array, src2.crs, dst_transform)