This commit is contained in:
esenke
2025-12-08 22:16:31 +08:00
commit 01adcfdf60
305 changed files with 50879 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
import os
import uuid
import numpy as np
import pyproj as prj
from osgeo import gdal
from time import time
import mercantile
from PIL import Image
import imageio.v2 as iio
from tile_resample import (
get_tile_array,
transfer
)
import argparse
from rich import print
from rich.progress import track
def get_args_parser():
parser = argparse.ArgumentParser(description='WorldView to tiles')
parser.add_argument('--fn_img', help='input file of WorldView image')
parser.add_argument('--save_dir', default='output_wv/', help='output directory')
parser.add_argument('--zoom', type=int, default=16, help='zoom level')
parser.add_argument('--verbose', action='store_true', default=True)
parser.add_argument('--use_gcj02', action='store_true', default=False)
return parser.parse_args()
def get_image_by_approximate_boundary(ds_list, boundary, tr, buf=1):
'''Get image data within a specified boundary
Args:
ds_list: List of GDAL datasets
boundary: List of (lng, lat) coordinates
tr: Geotransformation parameters
buf: Buffer size
'''
arr_lnglat = np.array(boundary)
tr_from_4326 = prj.Transformer.from_crs(4326, ds_list[0].GetProjection(), always_xy=True)
xx, yy = tr_from_4326.transform(arr_lnglat[:, 0], arr_lnglat[:, 1])
nx = ds_list[0].RasterXSize
ny = ds_list[0].RasterYSize
xres = tr[1]
yres = -tr[5]
row_min = int((tr[3] - yy.max()) / yres)
row_max = int((tr[3] - yy.min()) / yres)
col_min = int((xx.min() - tr[0]) / xres)
col_max = int((xx.max() - tr[0]) / xres)
row_min = max(0, row_min - buf)
row_max = min(ny - 1, row_max + buf)
col_min = max(0, col_min - buf)
col_max = min(nx - 1, col_max + buf)
if row_min > row_max or col_min > col_max:
return None
arr_image = np.stack([
ds.ReadAsArray(col_min, row_min, col_max - col_min + 1, row_max - row_min + 1)
for ds in ds_list
])
if np.all(arr_image == 0):
return None
arr_image = arr_image.transpose((1, 2, 0))
arr_xx = tr[0] + np.arange(col_min, col_max + 1) * xres
arr_yy = tr[3] - np.arange(row_min, row_max + 1) * yres
arr_xx, arr_yy = np.meshgrid(arr_xx, arr_yy)
tr_to_4326 = prj.Transformer.from_crs(ds_list[0].GetProjection(), 4326, always_xy=True)
arr_lngs, arr_lats = tr_to_4326.transform(arr_xx, arr_yy)
return arr_image, arr_lngs, arr_lats
def process_wv(args):
t_start = time()
fn_img = args.fn_img
save_dir = args.save_dir
z = args.zoom
verbose = args.verbose
os.makedirs(save_dir, exist_ok=True)
ds = gdal.Open(fn_img)
if ds is None:
raise Exception(f"Cannot open {fn_img}")
bands = [ds.GetRasterBand(i+1) for i in range(ds.RasterCount)]
list_arr = [ds]
nx, ny = ds.RasterXSize, ds.RasterYSize
tr = ds.GetGeoTransform()
if verbose:
print('Input size:', nx, ny)
print(gdal.Info(ds, format='json'))
# Calculate the image range
size_pixel = mercantile.CE / 2 ** z / 256
radius = np.ceil(max(tr[1], -tr[5]) / size_pixel * 1.5)
buf_ext = 1
xmin = tr[0] - buf_ext * tr[1]
ymin = tr[3] + (ny + buf_ext) * tr[5]
xmax = tr[0] + (nx + buf_ext) * tr[1]
ymax = tr[3] - buf_ext * tr[5]
tr_to_4326 = prj.Transformer.from_crs(ds.GetProjection(), 4326, always_xy=True)
arr_lng, arr_lat = tr_to_4326.transform(
np.array([xmin, xmin, xmax, xmax]),
np.array([ymax, ymin, ymin, ymax])
)
if args.use_gcj02:
arr_lng_final, arr_lat_final = transfer.WGS84_to_GCJ02(arr_lng, arr_lat)
else:
arr_lng_final, arr_lat_final = arr_lng, arr_lat
box = (
arr_lng_final.min(),
arr_lat_final.min(),
arr_lng_final.max(),
arr_lat_final.max()
)
if verbose:
coord_system = "GCJ02" if args.use_gcj02 else "WGS84"
print(f'Input extent, {coord_system}: {box}')
# Calculate the tile range to be processed
tile_ul = mercantile.tile(box[0], box[3], z)
tile_lr = mercantile.tile(box[2], box[1], z)
if verbose:
print('Upperleft ', str(tile_ul))
print('Lowerright ', str(tile_lr))
def work(x, y, z):
arr_tile = get_tile_array(
x, y, z,
method='nearest',
func_source=lambda boundary: get_image_by_approximate_boundary(list_arr, boundary, tr),
radius=radius,
use_gc02=args.use_gcj02
)
if arr_tile is not None:
save_path = os.path.join(save_dir, str(z), str(x))
os.makedirs(save_path, exist_ok=True)
# Save as PNG
if arr_tile.shape[2] >= 3:
arr_rgb = arr_tile[:, :, :3]
arr_rgb = np.clip(arr_rgb / 2000. * 255, 0, 255).astype(np.uint8)
image_tile = Image.fromarray(arr_rgb)
png_filename = os.path.join(save_path, f'{y}.png')
image_tile.save(png_filename, format='png')
# Save as NPZ
dict_arr = {f'B{i+1}': arr_tile[:, :, i] for i in range(arr_tile.shape[2])}
npz_filename = os.path.join(save_path, f'{y}.npz')
np.savez_compressed(npz_filename, **dict_arr)
tasks = [
(x, y) for x in range(tile_ul.x, tile_lr.x + 1)
for y in range(tile_ul.y, tile_lr.y + 1)
]
for x, y in track(tasks, description="Converting tiles..."):
work(x, y, z)
print("Time cost:", time() - t_start)
def main():
args = get_args_parser()
process_wv(args)
if __name__ == '__main__':
main()