乐于分享
好东西不私藏

GeoAI 切片源码解析

GeoAI 切片源码解析

本文是沿着源码,把 GeoAI 里“遥感影像 → 训练 chip”的真实执行路径一层一层扒开。可以把它当成一篇“读完就能自己实现一套切片引擎”的技术博客。


从大幅影像到训练样本的完整机制

在 GeoAI 的整个流程中,“数据切片”不是一个简单的预处理步骤,而是连接 GIS 数据与深度学习模型之间最关键的一座桥。因为模型从来不直接理解:

  • • GeoTIFF(大幅遥感影像)
  • • Shapefile(矢量标签)

它只理解:

  • • 固定大小的图像(如 256×256)
  • • 对应的标签(mask / bbox)

GeoAI 的 chip 生成,本质上完成了这样一个转换:

大幅遥感影像 + 矢量标签  →  小图块(image chip)+ mask

而这个过程,在源码中并不是一个函数,而是一整套逻辑链条。


切片不是裁剪,而是空间计算过程

GeoAI 的切片流程,可以抽象成如下步骤:

读取影像
构建滑窗网格
空间裁剪
标签对齐
过滤无效样本
输出数据集

对应 GIS 逻辑:

Raster影像
空间滑窗Window / Tile
Vector标签
裁剪 Raster+ Clip Vector
生成 mask / label
保存为训练数据

关键点在于:每一个 chip 都是一个“空间对象”,不是简单的数组切片


chip 生成的基本调用方式

在 GeoAI 中,切片通常封装为高层接口(类似如下逻辑):

create_chips(    image_path,    label_path,    chip_size=256,    stride=128,    output_dir="chips/")

虽然源码分散在不同模块中,但核心逻辑集中在三件事:

  1. 1. 滑窗生成(Window Generator)
  2. 2. 栅格裁剪(Raster Clip)
  3. 3. 标签栅格化(Vector → Mask)

滑窗机制

滑窗生成逻辑

GeoAI 切片的核心不是裁剪函数,而是滑窗生成器,是chip 的本质来源。

典型实现逻辑如下:

for y inrange(0, height, stride):for x inrange(0, width, stride):        window = Window(x, y, chip_size, chip_size)

关键参数说明

  • • chip_size:输出图像大小(如 256)
  • • stride:滑动步长(控制重叠)

为什么必须用滑窗?

如果不用滑窗,而是整图输入:

  • • 显存爆炸
  • • 数据量不可控
  • • 模型无法泛化

滑窗的意义在于:把“空间连续影像”转化为“离散样本集合”


GeoAI 的优化点

GeoAI 在这里通常会做两件优化:

边界处理

if x + chip_size > width:    x = width - chip_size

避免最后一块越界


重叠控制

stride < chip_size:

保证目标不会被切断例如:

chip_size = 256stride = 128

每个像素会被覆盖多次


栅格裁剪

滑窗只是定义空间范围,真正裁剪影像依赖 rasterio:

with rasterio.open(image_path) as src:    chip = src.read(window=window)

关键点解释

1. window 不是像素切片

它是:

(x_offset, y_offset, width, height)

带有空间含义


2. transform 更新

GeoAI 会更新 affine transform:

transform = src.window_transform(window)

如果不做这一步: 后续 mask 会错位


3. 输出 chip

rasterio.open(output_path, "w", ...) as dst:    dst.write(chip)

标签处理

这是 GeoAI 最“值钱”的部分。因为模型训练需要:

image: (256, 256, 3)mask:  (256, 256)

而你原始数据是:

Polygon(建筑物)

1. 空间裁剪标签

clipped_gdf = gdf.cx[minx:maxx, miny:maxy]

只保留当前 chip 内的要素


2. 栅格化(核心)

mask = rasterize(    [(geom, 1for geom in clipped_gdf.geometry],    out_shape=(chip_size, chip_size),    transform=transform)

关键解释

(1)为什么要 rasterize?

因为模型不理解 polygon,只理解像素


(2)transform 为什么重要?

它决定:

地理坐标 → 像素坐标

如果 transform 错了: mask 会偏移


3. 多类别处理

[(geom, class_id) for geom in gdf]

支持语义分割


样本过滤

GeoAI 通常不会保存所有切片,避免无效的clip标签,而是要过滤:

if mask.sum() == 0:continue

为什么必须过滤?

否则会出现:

  • • 大量纯背景图
  • • 模型训练偏移
  • • 精度下降

更高级过滤

if mask_ratio < threshold:    skip

控制正样本比例


数据增强

在 chip 生成阶段,GeoAI 还可能做增强:

flip(chip)rotate(chip)normalize(chip)

特别是遥感数据:

  • • 多尺度
  • • 多方向
  • • 光照差异大

输出结构

最终输出通常为:

dataset/├── images/│   ├── chip_001.tif│   ├── chip_002.tif├── masks/│   ├── chip_001.tif│   ├── chip_002.tif

或 COCO / YOLO 格式

GeoAI 也支持:

  • • bbox(检测)
  • • polygon(实例分割)

GeoAI 的工程设计亮点

1. 空间一致性优先

所有操作都围绕:

CRS + transform

不是简单数组处理


2. Raster + Vector 联动

切片不是:

image → chip

而是:

image + vector → image chip + mask

3. 可扩展 pipeline

你可以插入:

  • • 多时相数据
  • • 多模态数据
  • • DEM / 气象数据

核心最简代码

抽一份“最小可用版本”:

defgenerate_chips(image_path, gdf, chip_size=256, stride=128):with rasterio.open(image_path) as src:for y inrange(0, src.height, stride):for x inrange(0, src.width, stride):                window = Window(x, y, chip_size, chip_size)                chip = src.read(window=window)                transform = src.window_transform(window)                bounds = rasterio.windows.bounds(window, src.transform)                minx, miny, maxx, maxy = bounds                clipped = gdf.cx[minx:maxx, miny:maxy]if clipped.empty:continue                mask = rasterize(                    [(geom, 1for geom in clipped.geometry],                    out_shape=(chip_size, chip_size),                    transform=transform                )yield chip, mask

GeoAI 的切片模块,本质不是“切图工具”,而是:一个把“空间世界”翻译成“机器语言”的过程

如果这一步做得不好: 模型再强也没用。如果这一步做得好: 普通模型也能出奇迹