rsai 2026.5.23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rsai-2026.5.23/PKG-INFO +399 -0
- rsai-2026.5.23/README.md +374 -0
- rsai-2026.5.23/pyproject.toml +26 -0
- rsai-2026.5.23/rsai.egg-info/PKG-INFO +399 -0
- rsai-2026.5.23/rsai.egg-info/SOURCES.txt +27 -0
- rsai-2026.5.23/rsai.egg-info/dependency_links.txt +1 -0
- rsai-2026.5.23/rsai.egg-info/requires.txt +22 -0
- rsai-2026.5.23/rsai.egg-info/top_level.txt +1 -0
- rsai-2026.5.23/rsio/__init__.py +45 -0
- rsai-2026.5.23/rsio/assets/simhei.ttf +0 -0
- rsai-2026.5.23/rsio/backends/__init__.py +31 -0
- rsai-2026.5.23/rsio/backends/base_backend.py +354 -0
- rsai-2026.5.23/rsio/backends/cv2_backend.py +330 -0
- rsai-2026.5.23/rsio/backends/gdal_backend.py +520 -0
- rsai-2026.5.23/rsio/backends/pil_backend.py +220 -0
- rsai-2026.5.23/rsio/backends/rasterio_backend.py +463 -0
- rsai-2026.5.23/rsio/build_samples.py +1287 -0
- rsai-2026.5.23/rsio/enums.py +56 -0
- rsai-2026.5.23/rsio/open_image.py +400 -0
- rsai-2026.5.23/rsio/open_ogc.py +381 -0
- rsai-2026.5.23/rsio/open_ogc_services.py +210 -0
- rsai-2026.5.23/rsio/open_shp.py +269 -0
- rsai-2026.5.23/rsio/utils/__init__.py +31 -0
- rsai-2026.5.23/rsio/utils/converters.py +1139 -0
- rsai-2026.5.23/rsio/utils/decorators.py +156 -0
- rsai-2026.5.23/rsio/utils/exceptions.py +98 -0
- rsai-2026.5.23/rsio/utils/geo.py +177 -0
- rsai-2026.5.23/rsio/utils/misc.py +297 -0
- rsai-2026.5.23/setup.cfg +4 -0
rsai-2026.5.23/PKG-INFO
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rsai
|
|
3
|
+
Version: 2026.5.23
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: gdal==3.4.3
|
|
8
|
+
Requires-Dist: numpy<2.0.0,>=1.24.4
|
|
9
|
+
Requires-Dist: pyproj
|
|
10
|
+
Requires-Dist: rich
|
|
11
|
+
Requires-Dist: loguru
|
|
12
|
+
Requires-Dist: shapely
|
|
13
|
+
Requires-Dist: mercantile>=1.2.1
|
|
14
|
+
Requires-Dist: aiohttp>=3.13.3
|
|
15
|
+
Provides-Extra: cv2
|
|
16
|
+
Requires-Dist: opencv-python; extra == "cv2"
|
|
17
|
+
Provides-Extra: pil
|
|
18
|
+
Requires-Dist: pillow; extra == "pil"
|
|
19
|
+
Provides-Extra: rasterio
|
|
20
|
+
Requires-Dist: rasterio<=1.3.10; extra == "rasterio"
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: opencv-python; extra == "all"
|
|
23
|
+
Requires-Dist: pillow; extra == "all"
|
|
24
|
+
Requires-Dist: rasterio<=1.3.10; extra == "all"
|
|
25
|
+
|
|
26
|
+
# rsai
|
|
27
|
+
|
|
28
|
+
遥感图像 I/O 工具库,支持多后端读写、OGC 地图服务访问、矢量数据处理及训练样本制作。
|
|
29
|
+
|
|
30
|
+
## 安装
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install rsai
|
|
34
|
+
|
|
35
|
+
# 安装可选后端
|
|
36
|
+
pip install "rsai[cv2]" # OpenCV
|
|
37
|
+
pip install "rsai[pil]" # Pillow
|
|
38
|
+
pip install "rsai[rasterio]" # Rasterio
|
|
39
|
+
pip install "rsai[all]" # 全部
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
> 核心依赖:GDAL 3.4.3、NumPy、pyproj、Shapely、mercantile、aiohttp
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 快速开始
|
|
47
|
+
|
|
48
|
+
### 读写本地图像
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from rsio import open_image
|
|
52
|
+
|
|
53
|
+
# 读取(默认 GDAL 后端)
|
|
54
|
+
with open_image("image.tif") as img:
|
|
55
|
+
array = img.read() # 读取全图为 numpy 数组
|
|
56
|
+
print(img.width, img.height) # 图像尺寸
|
|
57
|
+
print(img.channel) # 波段数
|
|
58
|
+
print(img.dtype) # 数据类型
|
|
59
|
+
print(img.geo_transform) # 地理变换参数
|
|
60
|
+
print(img.geo_projection) # 投影信息(WKT)
|
|
61
|
+
print(img.geo_bounds) # 地理范围 (left, top, right, bottom)
|
|
62
|
+
print(img.pixel_size) # 像元大小 (x, y)
|
|
63
|
+
print(img.nodata) # 无效值
|
|
64
|
+
|
|
65
|
+
# 指定后端
|
|
66
|
+
with open_image("image.tif", backend="rasterio") as img:
|
|
67
|
+
array = img.read()
|
|
68
|
+
|
|
69
|
+
# 写入(需传入地理信息)
|
|
70
|
+
OpenImage.write_array(
|
|
71
|
+
"output.tif",
|
|
72
|
+
array,
|
|
73
|
+
backend="gdal",
|
|
74
|
+
geo_transform=geo_transform,
|
|
75
|
+
geo_projection=projection,
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 滑窗切片
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
with open_image("large.tif") as img:
|
|
83
|
+
# 生成器,逐块读取,节省内存
|
|
84
|
+
for tile_array, (x, y) in img.sliding_window_tiles(
|
|
85
|
+
window_size=512, # 也可传 (width, height)
|
|
86
|
+
stride=256, # 步长,默认等于 window_size(无重叠)
|
|
87
|
+
max_tiles=100, # 限制最大切片数
|
|
88
|
+
band_indexes=[1, 2, 3], # 指定波段
|
|
89
|
+
):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
# 预估切片总数
|
|
93
|
+
total = img.count_tiles(stride=256)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 均匀分块
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
with open_image("large.tif") as img:
|
|
100
|
+
# 自动计算最优行列数,使每块尽量接近正方形
|
|
101
|
+
for block_img, (x, y) in img.image_blocks(num_blocks=16):
|
|
102
|
+
array = block_img.read()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 图像裁剪
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
with open_image("image.tif") as img:
|
|
109
|
+
# 按像素坐标裁剪
|
|
110
|
+
img.crop(x_off=100, y_off=100, window_x_size=512, window_y_size=512)
|
|
111
|
+
|
|
112
|
+
# 按地理范围裁剪(left, top, right, bottom)
|
|
113
|
+
img.crop_by_geo_bounds((116.3, 40.1, 116.5, 39.9))
|
|
114
|
+
|
|
115
|
+
# 按 GeoJSON 裁剪(需 rasterio 后端)
|
|
116
|
+
with open_image("image.tif", backend="rasterio") as img:
|
|
117
|
+
img.crop_by_geojson(geojson_dict_or_path)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 重投影
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
with open_image("image.tif") as img:
|
|
124
|
+
# 支持 EPSG 整数、"EPSG:xxxx" 字符串、WKT 字符串、或另一个 OpenImage 对象
|
|
125
|
+
img.reproject(4326)
|
|
126
|
+
img.reproject("EPSG:32650")
|
|
127
|
+
img.reproject(other_img)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 多图像求交集
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from rsio import open_image
|
|
134
|
+
from rsio.open_image import OpenImage
|
|
135
|
+
|
|
136
|
+
img_a = open_image("a.tif")
|
|
137
|
+
img_b = open_image("b.tif")
|
|
138
|
+
|
|
139
|
+
# 自动对齐投影并裁剪到公共地理范围
|
|
140
|
+
img_a, img_b = OpenImage.intersection_image([img_a, img_b])
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 图像 resize
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from rsio.open_image import OpenImage
|
|
147
|
+
import numpy as np
|
|
148
|
+
|
|
149
|
+
array = ... # (H, W, C) 或 (C, H, W)
|
|
150
|
+
|
|
151
|
+
# 直接 resize
|
|
152
|
+
resized = OpenImage.resize_img(array, target_size=(512, 512))
|
|
153
|
+
|
|
154
|
+
# 保持宽高比并 padding
|
|
155
|
+
resized = OpenImage.resize_img(
|
|
156
|
+
array,
|
|
157
|
+
target_size=512,
|
|
158
|
+
keep_ratio=True,
|
|
159
|
+
pad=0, # padding 填充值
|
|
160
|
+
resample=1, # 0=最近邻 1=双线性 2=双三次 3=区域 4=Lanczos
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 有效值掩码
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from rsio.open_image import OpenImage
|
|
168
|
+
|
|
169
|
+
# 多通道不全为 0 且不全为 255 的像素为有效
|
|
170
|
+
mask = OpenImage.valid_mask_of_image(array) # shape: (H, W),bool
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## OGC 地图服务
|
|
176
|
+
|
|
177
|
+
### 通用接口
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from rsio import open_ogc
|
|
181
|
+
|
|
182
|
+
# XYZ 切片服务
|
|
183
|
+
with open_ogc("https://tile.openstreetmap.org/{z}/{x}/{y}.png", service="xyz") as ogc:
|
|
184
|
+
array = ogc.read(bbox=[116.3, 39.9, 116.5, 40.1], zoom=14)
|
|
185
|
+
|
|
186
|
+
# WMTS 服务
|
|
187
|
+
with open_ogc(url, service="wmts", layer="layer_name") as ogc:
|
|
188
|
+
array = ogc.read(bbox=..., zoom=14)
|
|
189
|
+
|
|
190
|
+
# WMS 服务
|
|
191
|
+
with open_ogc(url, service="wms", layer="layer_name") as ogc:
|
|
192
|
+
array = ogc.read(bbox=..., width=512, height=512)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 预置地图服务
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from rsio import open_ogc_service
|
|
199
|
+
|
|
200
|
+
# 天地图矢量底图(需申请 token)
|
|
201
|
+
with open_ogc_service("tianditu_vector", token="your_token") as svc:
|
|
202
|
+
image_array = svc.image.read(bbox=..., zoom=14)
|
|
203
|
+
label_array = svc.label.read(bbox=..., zoom=14) # 标注图层
|
|
204
|
+
shp = svc.shp # 矢量数据
|
|
205
|
+
|
|
206
|
+
# ESRI 影像
|
|
207
|
+
with open_ogc_service("esri_image") as svc:
|
|
208
|
+
array = svc.image.read(bbox=..., zoom=16)
|
|
209
|
+
|
|
210
|
+
# Google 影像
|
|
211
|
+
with open_ogc_service("google_image") as svc:
|
|
212
|
+
array = svc.image.read(bbox=..., zoom=16)
|
|
213
|
+
|
|
214
|
+
# CartoDB 矢量底图
|
|
215
|
+
with open_ogc_service("cartodb_vector") as svc:
|
|
216
|
+
array = svc.image.read(bbox=..., zoom=14)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| 服务名 | 说明 | 需要 token |
|
|
220
|
+
|--------|------|-----------|
|
|
221
|
+
| `tianditu_vector` | 天地图矢量底图 + 标注 | 是 |
|
|
222
|
+
| `cartodb_vector` | CartoDB 矢量底图 | 否 |
|
|
223
|
+
| `esri_image` | ESRI 卫星影像 | 否 |
|
|
224
|
+
| `google_image` | Google 卫星影像 | 否 |
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Shapefile 操作
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from rsio import open_shp
|
|
232
|
+
|
|
233
|
+
# 读取属性
|
|
234
|
+
with open_shp("data.shp") as shp:
|
|
235
|
+
print(shp.feature_count) # 要素数量
|
|
236
|
+
print(shp.extent) # 范围 (min_x, min_y, max_x, max_y)
|
|
237
|
+
print(shp.geo_bounds) # 地理范围
|
|
238
|
+
print(shp.geo_projection) # 投影信息
|
|
239
|
+
for feat in shp.features:
|
|
240
|
+
geom = feat.GetGeometryRef()
|
|
241
|
+
|
|
242
|
+
# 重投影并保存
|
|
243
|
+
with open_shp("data.shp") as shp:
|
|
244
|
+
shp.reproject(target_epsg=4326)
|
|
245
|
+
shp.save("reprojected.shp")
|
|
246
|
+
|
|
247
|
+
# 合并另一个 shp
|
|
248
|
+
with open_shp("base.shp") as shp:
|
|
249
|
+
shp.add_other_shp("extra.shp")
|
|
250
|
+
shp.save("merged.shp")
|
|
251
|
+
|
|
252
|
+
# 简化几何
|
|
253
|
+
with open_shp("data.shp") as shp:
|
|
254
|
+
shp.simplify_shp(tolerance=0.001)
|
|
255
|
+
shp.save("simplified.shp")
|
|
256
|
+
|
|
257
|
+
# 转为栅格图像(以参考图像的范围和分辨率为准)
|
|
258
|
+
with open_shp("data.shp") as shp:
|
|
259
|
+
img = shp.convert_to_open_image(ref_image="ref.tif")
|
|
260
|
+
img.save("rasterized.tif")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 训练样本制作
|
|
266
|
+
|
|
267
|
+
### 语义分割样本
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from rsio.build_samples import SemanticSampleBuilder
|
|
271
|
+
|
|
272
|
+
builder = SemanticSampleBuilder(
|
|
273
|
+
input_images="images/", # 图像目录或路径列表
|
|
274
|
+
input_labels="labels/", # 标签目录或路径列表(支持 tif/shp)
|
|
275
|
+
output_dir="samples/", # 输出目录
|
|
276
|
+
window_size=512, # 切片大小,也可传 (w, h)
|
|
277
|
+
stride=256, # 步长,默认等于 window_size
|
|
278
|
+
max_samples=None, # 最大样本数,None 表示不限
|
|
279
|
+
band_indexes=[1, 2, 3], # 指定波段,None 表示全部
|
|
280
|
+
palette=None, # 颜色映射表(用于彩色标签)
|
|
281
|
+
split_val_ratio=0.1, # 验证集比例
|
|
282
|
+
split_test_ratio=0.1, # 测试集比例
|
|
283
|
+
save_blank_sample_prob=0.05, # 保存全背景样本的概率(0 表示不保存)
|
|
284
|
+
input_mode=0, # 0: 路径列表 1: 目录按文件名匹配 2: 目录按地理范围匹配
|
|
285
|
+
num_workers=4, # 并行进程数
|
|
286
|
+
attribute_field="class_id", # shp 标签的属性字段名
|
|
287
|
+
mapping_field={"road": 1}, # shp 属性值到类别 id 的映射
|
|
288
|
+
image_prefix=None, # 图像文件名前缀过滤
|
|
289
|
+
label_suffix=None, # 标签文件名后缀过滤
|
|
290
|
+
crop=False, # 是否裁剪图像到标签范围
|
|
291
|
+
log_file="build.log", # 日志文件路径
|
|
292
|
+
)
|
|
293
|
+
builder.build()
|
|
294
|
+
|
|
295
|
+
# 生成可视化标签(将类别 id 映射为颜色)
|
|
296
|
+
builder.batch_build_color_label(
|
|
297
|
+
input_labels="labels/",
|
|
298
|
+
output_dir="color_labels/",
|
|
299
|
+
palette={0: (0,0,0), 1: (255,0,0)},
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# 生成叠加可视化图(图像 + 标签半透明叠加)
|
|
303
|
+
builder.batch_build_visualizer_label(
|
|
304
|
+
input_images="images/",
|
|
305
|
+
input_labels="labels/",
|
|
306
|
+
output_dir="visualize/",
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
输出目录结构:
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
samples/
|
|
314
|
+
├── train/
|
|
315
|
+
│ ├── images/
|
|
316
|
+
│ └── labels/
|
|
317
|
+
├── val/
|
|
318
|
+
│ ├── images/
|
|
319
|
+
│ └── labels/
|
|
320
|
+
└── test/
|
|
321
|
+
├── images/
|
|
322
|
+
└── labels/
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 变化检测样本
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from rsio.build_samples import ChangedSampleBuilder
|
|
329
|
+
|
|
330
|
+
builder = ChangedSampleBuilder(
|
|
331
|
+
input_images_a="time_a/", # 时相 A 图像目录
|
|
332
|
+
input_images_b="time_b/", # 时相 B 图像目录
|
|
333
|
+
input_labels="labels/", # 变化标签目录
|
|
334
|
+
output_dir="samples/",
|
|
335
|
+
window_size=512,
|
|
336
|
+
stride=256,
|
|
337
|
+
split_val_ratio=0.1,
|
|
338
|
+
split_test_ratio=0.1,
|
|
339
|
+
num_workers=4,
|
|
340
|
+
)
|
|
341
|
+
builder.build()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
输出目录结构:
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
samples/
|
|
348
|
+
├── train/
|
|
349
|
+
│ ├── images_a/
|
|
350
|
+
│ ├── images_b/
|
|
351
|
+
│ └── labels/
|
|
352
|
+
├── val/
|
|
353
|
+
│ ├── images_a/
|
|
354
|
+
│ ├── images_b/
|
|
355
|
+
│ └── labels/
|
|
356
|
+
└── test/
|
|
357
|
+
├── images_a/
|
|
358
|
+
├── images_b/
|
|
359
|
+
└── labels/
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 后端说明
|
|
365
|
+
|
|
366
|
+
| 后端 | 关键字 | 适用场景 |
|
|
367
|
+
|------|--------|---------|
|
|
368
|
+
| GDAL(默认) | `gdal` | 地理栅格数据,支持完整地理信息,推荐首选 |
|
|
369
|
+
| Rasterio | `rasterio` | 地理栅格数据,支持 GeoJSON 裁剪 |
|
|
370
|
+
| PIL | `pil` | 普通图像,轻量读写,不含地理信息 |
|
|
371
|
+
| OpenCV | `cv2` | 图像处理,BGR 格式,resize/显示 |
|
|
372
|
+
|
|
373
|
+
后端通过 `backend=` 参数指定,所有后端均实现相同的抽象接口(`ImageBackend`),可无缝切换。
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 项目结构
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
rsio/
|
|
381
|
+
├── open_image.py # 本地图像 I/O(OpenImage)
|
|
382
|
+
├── open_ogc.py # OGC 服务(WMS/WMTS/XYZ)
|
|
383
|
+
├── open_ogc_services.py # 预置地图服务
|
|
384
|
+
├── open_shp.py # Shapefile I/O
|
|
385
|
+
├── build_samples.py # 训练样本制作
|
|
386
|
+
├── enums.py # 枚举定义
|
|
387
|
+
├── backends/
|
|
388
|
+
│ ├── base_backend.py # 抽象基类 ImageBackend
|
|
389
|
+
│ ├── gdal_backend.py # GDALImage
|
|
390
|
+
│ ├── rasterio_backend.py # RasterioImage
|
|
391
|
+
│ ├── pil_backend.py # PILImage
|
|
392
|
+
│ └── cv2_backend.py # OpencvImage
|
|
393
|
+
└── utils/
|
|
394
|
+
├── converters.py # 坐标/数组/颜色/BBox 转换
|
|
395
|
+
├── decorators.py # 通用装饰器(类型检查、依赖检查等)
|
|
396
|
+
├── exceptions.py # 统一异常体系
|
|
397
|
+
├── geo.py # 栅格/矢量地理操作
|
|
398
|
+
└── misc.py # 日志、安全导入、列表工具
|
|
399
|
+
```
|