Beispiel #1
0
def min_max_heights_from_bbx(im, lon_m, lon_M, lat_m, lat_M, rpc):
    """
    Compute min, max heights from bounding box

    Args:
        im: path to an image file
        lon_m, lon_M, lat_m, lat_M: bounding box

    Returns:
        hmin, hmax: min, max heights
    """
    # open image
    dataset = rasterio.open(im, 'r')

    # convert lon/lat to im projection
    x_im_proj, y_im_proj = geographiclib.pyproj_transform([lon_m, lon_M],
                                                          [lat_m, lat_M],
                                                          4326,
                                                          dataset.crs.to_epsg())

    # convert im projection to pixel
    pts = []
    pts.append(~dataset.transform * (x_im_proj[0], y_im_proj[0]))
    pts.append(~dataset.transform * (x_im_proj[1], y_im_proj[1]))
    px = [p[0] for p in pts]
    py = [p[1] for p in pts]

    # get footprint
    [px_min, px_max, py_min, py_max] = map(int, [np.amin(px),
                                                 np.amax(px)+1,
                                                 np.amin(py),
                                                 np.amax(py)+1])

    # limits of im extract
    x, y, w, h = px_min, py_min, px_max - px_min + 1, py_max - py_min + 1
    sizey, sizex = dataset.shape
    x0 = np.clip(x, 0, sizex-1)
    y0 = np.clip(y, 0, sizey-1)
    w -= (x0-x)
    h -= (y0-y)
    w = np.clip(w, 0, sizex - 1 - x0)
    h = np.clip(h, 0, sizey - 1 - y0)

    # get value for each pixel
    if (w != 0) and (h != 0):
        array = dataset.read(1, window=((y0, y0 + h), (x0, x0 + w))).astype(float)
        array[array == -32768] = np.nan
        hmin = np.nanmin(array)
        hmax = np.nanmax(array)

        if cfg['exogenous_dem_geoid_mode'] is True:
            offset = geographiclib.geoid_to_ellipsoid((lat_m + lat_M)/2, (lon_m + lon_M)/2, 0)
            hmin += offset
            hmax += offset
        return hmin, hmax
    else:
        print("WARNING: rpc_utils.min_max_heights_from_bbx: access window out of range")
        print("returning coarse range from rpc")
        return altitude_range_coarse(rpc, cfg['rpc_alt_range_scale_factor'])
Beispiel #2
0
def stereo_corresp_to_xyz(rpc1, rpc2, pts1, pts2, out_crs=None):
    """
    Compute a point cloud from stereo correspondences between two images using RPC camera models.
    No need to go through the disparity map

    Args:
        rpc1, rpc2 (rpcm.RPCModel): camera models
        pts1, pts2 (arrays): 2D arrays of shape (N, 2) containing the image coordinates of
            N 2d keypoints matched beween im1 and im2,
            i.e. cooridnates in the same row of these arrays back-project to the same 3D point
        out_crs (pyproj.crs.CRS): object defining the desired coordinate reference system for the
            output xyz map
    Returns:
        xyz: array of shape (h, w, 3) where each pixel contains the 3D
            coordinates of the triangulated point in the coordinate system
            defined by `out_crs`
        err: array of shape (h, w) where each pixel contains the triangulation
            error
    """
    # copy rpc coefficients to an RPCStruct object
    rpc1_c_struct = RPCStruct(rpc1, delta=0.1)
    rpc2_c_struct = RPCStruct(rpc2, delta=0.1)

    # get number of points to triangulate
    n = pts1.shape[0]

    # define the argument types of the stereo_corresp_to_lonlatalt function from disp_to_h.so
    lib.stereo_corresp_to_lonlatalt.argtypes = (ndpointer(dtype=c_double,
                                                          shape=(n, 3)),
                                                ndpointer(dtype=c_float,
                                                          shape=(n, 1)),
                                                ndpointer(dtype=c_float,
                                                          shape=(n, 2)),
                                                ndpointer(dtype=c_float,
                                                          shape=(n, 2)), c_int,
                                                POINTER(RPCStruct),
                                                POINTER(RPCStruct))

    # call the stereo_corresp_to_lonlatalt function from disp_to_h.so
    lonlatalt = np.zeros((n, 3), dtype='float64')
    err = np.zeros((n, 1), dtype='float32')
    lib.stereo_corresp_to_lonlatalt(lonlatalt, err, pts1.astype('float32'),
                                    pts2.astype('float32'), n,
                                    byref(rpc1_c_struct), byref(rpc2_c_struct))

    # output CRS conversion
    in_crs = geographiclib.pyproj_crs("epsg:4979")

    if out_crs and out_crs != in_crs:

        x, y, z = geographiclib.pyproj_transform(lonlatalt[:, 0],
                                                 lonlatalt[:, 1], in_crs,
                                                 out_crs, lonlatalt[:, 2])

        xyz_array = np.column_stack((x, y, z)).astype(np.float32)
    else:
        xyz_array = lonlatalt

    return xyz_array, err
Beispiel #3
0
def height_map_to_point_cloud(cloud,
                              heights,
                              rpc,
                              off_x=None,
                              off_y=None,
                              crop_colorized=''):
    """
    Computes a color point cloud from a height map.

    Args:
        cloud: path to the output points cloud (ply format)
        heights: height map, sampled on the same grid as the crop_colorized
            image. In particular, its size is the same as crop_colorized.
        rpc: instances of the rpcm.RPCModel class
        off_{x,y} (optional, default None): coordinates of the origin of the crop
            we are dealing with in the pixel coordinates of the original full
            size image
        crop_colorized (optional, default ''): path to a colorized crop of a
            Pleiades image
    """
    with rasterio.open(heights) as src:
        h_map = src.read(1)
        h, w = h_map.shape

    heights = h_map.ravel()
    indices = np.indices((h, w))

    non_nan_ind = np.where(~np.isnan(heights))[0]

    alts = heights[non_nan_ind]
    cols = indices[1].ravel()[non_nan_ind]
    rows = indices[0].ravel()[non_nan_ind]

    if off_x or off_y:
        cols = cols + (off_x or 0)
        rows = rows + (off_y or 0)

    # localize pixels
    lons = np.empty_like(heights, dtype=np.float64)
    lats = np.empty_like(heights, dtype=np.float64)
    lons[non_nan_ind], lats[non_nan_ind] = rpc.localization(cols, rows, alts)

    # output CRS conversion
    in_crs = geographiclib.pyproj_crs("epsg:4979")
    out_crs = geographiclib.pyproj_crs(cfg['out_crs'])
    proj_com = "CRS {}".format(cfg['out_crs'])

    if out_crs != in_crs:
        x, y, z = geographiclib.pyproj_transform(lons, lats, in_crs, out_crs,
                                                 heights)
    else:
        x, y, z = lons, lats, heights

    xyz_array = np.column_stack((x, y, z)).reshape(h, w, 3)

    filter_xyz_and_write_to_ply(cloud, xyz_array, cfg['3d_filtering_r'],
                                cfg['3d_filtering_n'], cfg['gsd'],
                                crop_colorized, proj_com)
Beispiel #4
0
def roi_process(rpc, ll_poly, utm_zone=None, use_srtm=False):
    """
    Convert a longitude/latitude polygon into a rectangular
    bounding box in image coordinates

    Args:
        rpc (rpcm.RPCModel): camera model
        ll_poly (array): 2D array of shape (n, 2) containing the vertices
            (longitude, latitude) of the polygon
        utm_zone: force the zone number to be used when defining `utm_bbx`.
            If not specified, the default UTM zone for the given geography
            is used.
        use_srtm (bool): whether or not to use SRTM DEM to estimate the
            average ground altitude of the ROI.

    Returns:
        x, y, w, h: four integers defining a rectangular region of interest
            (ROI) in the image. (x, y) is the top-left corner, and (w, h)
            are the dimensions of the rectangle.
    """
    if not utm_zone:
        utm_zone = geographiclib.compute_utm_zone(*ll_poly.mean(axis=0))
    cfg['utm_zone'] = utm_zone

    # convert lon lat polygon to utm
    epsg = geographiclib.epsg_code_from_utm_zone(utm_zone)
    easting, northing = geographiclib.pyproj_transform(ll_poly[:, 0],
                                                       ll_poly[:,
                                                               1], 4326, epsg)
    east_min = min(easting)
    east_max = max(easting)
    nort_min = min(northing)
    nort_max = max(northing)
    cfg['utm_bbx'] = (east_min, east_max, nort_min, nort_max)

    # project lon lat vertices into the image
    if use_srtm:
        lon, lat = np.mean(ll_poly, axis=0)
        z = srtm4.srtm4(lon, lat)
        img_pts = rpc.projection(ll_poly[:, 0], ll_poly[:, 1], z)
    else:
        img_pts = rpc.projection(ll_poly[:, 0], ll_poly[:, 1], rpc.alt_offset)

    # return image roi
    x, y, w, h = common.bounding_box2D(list(zip(*img_pts)))
    return {'x': x, 'y': y, 'w': w, 'h': h}
Beispiel #5
0
def roi_process(rpc,
                ll_poly,
                use_srtm=False,
                exogenous_dem=None,
                exogenous_dem_geoid_mode=True):
    """
    Convert a (lon, lat) polygon into a rectangular bounding box in image space.

    Args:
        rpc (rpcm.RPCModel): camera model
        ll_poly (array): 2D array of shape (n, 2) containing the vertices
            (longitude, latitude) of the polygon
        use_srtm (bool): whether or not to use SRTM DEM to estimate the
            average ground altitude of the ROI.

    Returns:
        x, y, w, h: four integers defining a rectangular region of interest
            (ROI) in the image. (x, y) is the top-left corner, and (w, h)
            are the dimensions of the rectangle.
    """
    if use_srtm and (exogenous_dem is not None):
        raise ValueError("use_srtm and exogenous_dem are mutually exclusive")

    # project lon lat vertices into the image
    lon, lat = np.mean(ll_poly, axis=0)
    if exogenous_dem is not None:
        with rasterio.open(exogenous_dem) as src:
            x, y = geographiclib.pyproj_transform(lon, lat, 4326,
                                                  src.crs.to_epsg())
            z = list(src.sample([(x, y)]))[0][0]
            if exogenous_dem_geoid_mode is True:
                z = geographiclib.geoid_to_ellipsoid(lat, lon, z)
    elif use_srtm:
        z = srtm4.srtm4(lon, lat)
    else:
        z = rpc.alt_offset
    img_pts = rpc.projection(ll_poly[:, 0], ll_poly[:, 1], z)

    # return image roi
    x, y, w, h = common.bounding_box2D(list(zip(*img_pts)))
    return {'x': x, 'y': y, 'w': w, 'h': h}
Beispiel #6
0
def disp_to_xyz(rpc1,
                rpc2,
                H1,
                H2,
                disp,
                mask,
                out_crs=None,
                img_bbx=None,
                A=None):
    """
    Compute a height map from a disparity map, using RPC camera models.

    Args:
        rpc1, rpc2 (rpcm.RPCModel): camera models
        H1, H2 (arrays): 3x3 numpy arrays defining the rectifying homographies
        disp, mask (array): 2D arrays of shape (h, w) representing the diparity
            and mask maps
        out_crs (pyproj.crs.CRS): object defining the desired coordinate reference system for the
            output xyz map
        img_bbx (4-tuple): col_min, col_max, row_min, row_max defining the
            unrectified image domain to process.
        A (array): 3x3 array with the pointing correction matrix for im2

    Returns:
        xyz: array of shape (h, w, 3) where each pixel contains the 3D
            coordinates of the triangulated point in the coordinate system 
            defined by `out_crs`
        err: array of shape (h, w) where each pixel contains the triangulation
            error
    """
    # copy rpc coefficients to an RPCStruct object
    rpc1_c_struct = RPCStruct(rpc1)
    rpc2_c_struct = RPCStruct(rpc2)

    # handle optional arguments
    if A is not None:  # apply pointing correction
        H2 = np.dot(H2, np.linalg.inv(A))

    if img_bbx is None:
        img_bbx = (-np.inf, np.inf, -np.inf, np.inf)

    # define the argument types of the disp_to_lonlatalt function from disp_to_h.so
    h, w = disp.shape
    lib.disp_to_lonlatalt.argtypes = (ndpointer(dtype=c_double,
                                                shape=(h, w, 3)),
                                      ndpointer(dtype=c_float, shape=(h, w)),
                                      ndpointer(dtype=c_float, shape=(h, w)),
                                      ndpointer(dtype=c_float, shape=(h, w)),
                                      ndpointer(dtype=c_float,
                                                shape=(h, w)), c_int, c_int,
                                      ndpointer(dtype=c_double, shape=(9, )),
                                      ndpointer(dtype=c_double, shape=(9, )),
                                      POINTER(RPCStruct), POINTER(RPCStruct),
                                      ndpointer(dtype=c_float, shape=(4, )))

    # call the disp_to_lonlatalt function from disp_to_h.so
    lonlatalt = np.zeros((h, w, 3), dtype='float64')
    err = np.zeros((h, w), dtype='float32')
    dispx = disp.astype('float32')
    dispy = np.zeros((h, w), dtype='float32')
    msk = mask.astype('float32')
    lib.disp_to_lonlatalt(lonlatalt, err, dispx, dispy, msk, w, h,
                          H1.flatten(), H2.flatten(), byref(rpc1_c_struct),
                          byref(rpc2_c_struct),
                          np.asarray(img_bbx, dtype='float32'))

    # output CRS conversion
    in_crs = geographiclib.pyproj_crs("epsg:4979")

    if out_crs and out_crs != in_crs:

        # reshape the lonlatlat array into a 3-column 2D-array
        lonlatalt = lonlatalt.reshape(-1, 3)

        x, y, z = geographiclib.pyproj_transform(lonlatalt[:, 0],
                                                 lonlatalt[:, 1], in_crs,
                                                 out_crs, lonlatalt[:, 2])

        xyz_array = np.column_stack((x, y, z)).reshape(h, w,
                                                       3).astype(np.float32)
    else:
        xyz_array = lonlatalt

    return xyz_array, err