Package: orthorectification
Orthorectification Using RPCsThis repository houses some methods and utilities to help perform orthorectification on raw satellite imagery
such as worldview2/3/4(RIP) panchromatic imagery.These algorithms were presented as a deep dive in the Charlottesville Data Science Meetup, February 27th, 2020.
The original presentation material is included under/presentationNote that all of this functionality already exists in libraries like GDAL and others. The goal of this codebase
was to present and deep dive into these subroutines.Nearly all of the code is Numba JIT-able for maximum performance. There is also a pure C++ implementation of
the orthorectification routine that can be optionally imported usingcppyy.Functionsortho_tools.pydef unpack_rpc_parameters(dataset: gdal.Dataset) -> RPCCoeffs:Returns RPC coefficients collection as a NamedTuple
when provided with a GDAL dataset if that dataset contains RPCsdef retrieve_dem( min_lon: float, min_lat: float, degrees_lon: float, degrees_lat: float, sampling_rate: int = 1, output_path: str = "/tmp/elevation.dem", ) -> Tuple[np.ndarray, np.array]:Loads up a DEM tile given an upper-left world coordinate (min_lon, min_lat) and a width/height
in degrees.The return object is a tuple containing the DEM image and its GeoTransform respectively.def lon_lat_to_pixel(lon: float, lat: float, geot: np.array) -> Tuple[float, float]:Reprojects a world coordinate into pixel space using a GeoTransform, (Array of 6 floats)
This is useful for querying DEM information, or finding an image pixel coordinate for an image
that has already been orthorectified.def linear_interp(x: float, y: float, source: np.ndarray, source_height: int) -> int:Given a pixel space coordinate x,y and a source image as a flattened array (with the stride!),
returns a bilinearly interpolated measurement. Useful for DEM interpolation and also interpolation
during the orthorectification process.def lon_lat_alt_to_xy( lon: float, lat: float, alt: float, rpcs: RPCCoeffs, ) -> Tuple[float, float]:Returns an image pixel coordinate (x, y) corresponding to a provided world coordinate (lon, lat, alt) using provided RPC coefficientsdef make_ortho( x1: float, x2: float, y1: float, y2: float, width: int, source: np.ndarray, rpcs: RPCCoeffs, dem: np.ndarray, dem_geot: np.array, ) -> Tuple[np.array, float, float, float]:Creates an ortho with using the provided bounding box (x1, x2, y1, y2) which ismin_lon,max_lon,min_lat,max_lat,
a desired width (number of pixels), a source image, that source image's RPCs, a DEM, and the DEM's affine GeoTransform.scaling_tools.pydef fracture_polygon_north_up(poly: Polygon, factor_x: int, factor_y) -> Sequence[Polygon]:Breaks a Shapely polygon into small north up rectangles, attempts to fill as much area as possible
with smaller squares. The factors control the number of rectangles per dimension (resolution)def fracture_parallelogram(poly: Polygon, factor: int) -> Sequence[Polygon]:Fractures a parallelogram into smaller parallelogram with a non-regular orientation.
The orientation of each individual small parallelogram with respect to the original
parallelogram is maintained.def rescale_elevation_data(elevation_data: np.ndarray) -> np.ndarray:Maps elevation data into a 0-255 8-bit representation that is suitable for viewingdef reproject_with_affine( coords: Sequence[Tuple[float, float]], geo_transform: np.array, resize_factor: float = 1.0, ) -> Sequence[Tuple[float, float]]:Basically the same thing asortho_tools.lon_lat_to_pixel, but works on a sequence of coordinates,
which can be exactly what you get out of ashapely.Polygon.exterior.coordsfor example. You can
optionally pass in aresize_factorthat rescales your result as desired.def overlay_polygon( img: np.ndarray, polygon: Polygon, color: Tuple[int, int, int, int] = (0, 255, 0, 0), opacity: float = 0.2 ) -> np.ndarray:Burns a polygon onto an image using OpenCV.
Example:def generate_triangle_mesh(elevation_data: np.ndarray, reach: int = 10) -> Tuple[np.array, np.array, np.array]:Generates a triangle mesh from elevation data using a very simple and non-optimal but extremely fast procedure
Produces a 3D vertex list as a Tuple of np.array. The resulting data can be visualized using OpenCV or something like that.io_tools.pydef save_raster_as_geotiff(ortho: np.ndarray, ul_lon: float, ul_lat: float, gsd: float, filename: str) -> None:You provide an image, it's upper left world coordinate (ul_lon, ul_lat), and its GSD (ground sampling distance) in degrees,
and it writes out a mapping compatible GeoTIFF file. This can be imported into QGIS/ArcGIS or whatever to
visualize the ortho product on a map.scaling_tools.pydef resize(img: np.array, factor: int) -> np.array:OpenCV resizedef convert_to_8bit(img: np.array) -> np.array:Jams higher bitness image pixels into 0-255 range... not elegant.def gaussian_rescale(img: np.array, bitness=11, stdev_bound=3) -> np.array:Much more elegant way to rescale a >8 bitness image into 0-255 range. Bitness of the
image must be provided. WV3 is usually 11 or 12 bit.See a before and after example:experimental.pyOnly use this if you are brave. Requirescppyywhich can be a pain to install, so it is not marked as a required package.def ortho_cpp( x1: float, x2: float, y1: float, y2: float, width: int, rpcs: RPCCoeffs, source: np.ndarray, dem: np.ndarray, dem_geot: np.array, ):This is the exact same method signature asortho_tools.make_ortho, but it uses pure C++ code as an
accelerated version. Generally about twice as fast as even theNumbaJITted version ofortho_tools.make_orthoComing soon toexperimental.py:
Orthorectify your RPC images with CUDA!