kopia lustrzana https://github.com/OpenDroneMap/WebODM
118 wiersze
5.1 KiB
Python
118 wiersze
5.1 KiB
Python
from rasterio.io import MemoryFile
|
|
from rasterio.transform import from_origin
|
|
from rasterio.warp import aligned_target, reproject
|
|
import rasterio as rio
|
|
import numpy as np
|
|
|
|
def align(reference, other, *more_others, **kwargs):
|
|
others = [other] + list(more_others)
|
|
assert_same_crs(reference, others)
|
|
reference, others = build_complex_rasters(reference, others)
|
|
match_pixel_size(reference, others, kwargs)
|
|
intersect_rasters(reference, others)
|
|
return [reference.raster] + [other.raster for other in others]
|
|
|
|
def align_altitudes(reference, other, *more_others):
|
|
others = [other] + list(more_others)
|
|
reference, others = build_complex_rasters(reference, others)
|
|
|
|
reference.align_altitude_to_zero()
|
|
for other in others:
|
|
other.align_altitude_to_zero()
|
|
|
|
return [reference.raster] + [other.raster for other in others]
|
|
|
|
def assert_same_crs(reference, others):
|
|
for other in others:
|
|
assert reference.crs == other.crs, "All rasters should have the same CRS."
|
|
|
|
def build_complex_rasters(reference, others):
|
|
"""Build Raster objects from the rasterio rasters"""
|
|
return Raster(reference), [Raster(other) for other in others]
|
|
|
|
def match_pixel_size(reference, others, kwargs):
|
|
"""Take two or more rasters and modify them so that they have the same pixel size"""
|
|
rasters = [reference] + others
|
|
max_xres = max([raster.xres for raster in rasters])
|
|
max_yres = max([raster.yres for raster in rasters])
|
|
|
|
if 'resolution' in kwargs:
|
|
max_xres = max(max_xres, kwargs['resolution'])
|
|
max_yres = max(max_yres, kwargs['resolution'])
|
|
|
|
reference.match_pixel_size(max_xres, max_yres)
|
|
for other in others:
|
|
other.match_pixel_size(max_xres, max_yres)
|
|
|
|
def intersect_rasters(reference, others):
|
|
"""Take two or more rasters with the same size per pixel, and calculate the areas where they intersect, based on their position. Then, we keep only those areas, discarding the other pixels."""
|
|
final_bounds = reference.get_bounds()
|
|
|
|
for other in others:
|
|
final_bounds = final_bounds.intersection(other.get_bounds())
|
|
|
|
reference.reduce_to_bounds(final_bounds)
|
|
for other in others:
|
|
other.reduce_to_bounds(final_bounds)
|
|
|
|
|
|
class Raster:
|
|
def __init__(self, raster):
|
|
self.raster = raster
|
|
self.xres, self.yres = raster.res
|
|
|
|
def get_bounds(self):
|
|
(left, bottom, right, top) = self.raster.bounds
|
|
return Bounds(left, bottom, right, top)
|
|
|
|
def get_window(self):
|
|
print(self.raster.bounds)
|
|
(left, bottom, right, top) = self.raster.bounds
|
|
return self.raster.window(left, bottom, right, top)
|
|
|
|
def match_pixel_size(self, xres, yres):
|
|
dst_transform, dst_width, dst_height = aligned_target(self.raster.transform, self.raster.width, self.raster.height, (xres, yres))
|
|
with MemoryFile() as mem_file:
|
|
aligned = mem_file.open(driver = 'GTiff', height = dst_height, width = dst_width, count = self.raster.count, dtype = self.raster.dtypes[0], crs = self.raster.crs, transform = dst_transform, nodata = self.raster.nodata)
|
|
for band in range(1, self.raster.count + 1):
|
|
reproject(rio.band(self.raster, band), rio.band(aligned, band))
|
|
self.raster = aligned
|
|
|
|
def reduce_to_bounds(self, bounds):
|
|
"""Take some bounds and remove the pixels outside of it"""
|
|
(left, bottom, right, top) = bounds.as_tuple()
|
|
window = self.raster.window(left, bottom, right, top)
|
|
with MemoryFile() as mem_file:
|
|
raster = mem_file.open(driver = 'GTiff', height = window.height, width = window.width, count = self.raster.count, dtype = self.raster.dtypes[0], crs = self.raster.crs, transform = self.raster.window_transform(window), nodata = self.raster.nodata)
|
|
for band in range(1, self.raster.count + 1):
|
|
band_array = self.raster.read(band, window = window)
|
|
raster.write(band_array, band)
|
|
self.raster = raster
|
|
|
|
def align_altitude_to_zero(self):
|
|
with MemoryFile() as mem_file:
|
|
raster = mem_file.open(driver = 'GTiff', height = self.raster.height, width = self.raster.width, count = self.raster.count, dtype = self.raster.dtypes[0], crs = self.raster.crs, transform = self.raster.transform, nodata = self.raster.nodata)
|
|
for band in range(1, self.raster.count + 1):
|
|
band_array = self.raster.read(band, masked = True)
|
|
min = band_array.min()
|
|
aligned = band_array - min
|
|
raster.write(aligned, band)
|
|
self.raster = raster
|
|
|
|
class Bounds:
|
|
def __init__(self, left, bottom, right, top):
|
|
self.left = left
|
|
self.bottom = bottom
|
|
self.right = right
|
|
self.top = top
|
|
|
|
def intersection(self, other_bounds):
|
|
max_left = max(self.left, other_bounds.left)
|
|
max_bottom = max(self.bottom, other_bounds.bottom)
|
|
min_right = min(self.right, other_bounds.right)
|
|
min_top = min(self.top, other_bounds.top)
|
|
return Bounds(max_left, max_bottom, min_right, min_top)
|
|
|
|
def as_tuple(self):
|
|
return (self.left, self.bottom, self.right, self.top)
|