Exemplo n.º 1
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)
Exemplo n.º 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
Exemplo n.º 3
0
def build_cfg(user_cfg):
    """
    Populate a dictionary containing the s2p parameters from a user config file.

    This dictionary is contained in the global variable 'cfg' of the config
    module.

    Args:
        user_cfg: user config dictionary
    """
    # check that all the mandatory arguments are defined
    check_parameters(user_cfg)

    # fill the config module: updates the content of the config.cfg dictionary
    # with the content of the user_cfg dictionary
    cfg.update(user_cfg)

    # set keys 'clr', 'cld' and 'roi' of the reference image to None if they
    # are not already defined. The default values of these optional arguments
    # can not be defined directly in the config.py module. They would be
    # overwritten by the previous update, because they are in a nested dict.
    cfg['images'][0].setdefault('clr')
    cfg['images'][0].setdefault('cld')
    cfg['images'][0].setdefault('roi')
    cfg['images'][0].setdefault('wat')

    # make sure that input data have absolute paths
    for i in range(len(cfg['images'])):
        for d in ['clr', 'cld', 'roi', 'wat', 'img']:
            if d in cfg['images'][i] and cfg['images'][i][
                    d] is not None and not os.path.isabs(cfg['images'][i][d]):
                cfg['images'][i][d] = os.path.abspath(cfg['images'][i][d])

    # get out_crs
    if 'out_crs' not in cfg or cfg['out_crs'] is None:
        x, y, w, h = [cfg['roi'][k] for k in ['x', 'y', 'w', 'h']]
        utm_zone = rpc_utils.utm_zone(cfg['images'][0]['rpcm'], x, y, w, h)
        epsg_code = geographiclib.epsg_code_from_utm_zone(utm_zone)
        cfg['out_crs'] = "epsg:{}".format(epsg_code)
    geographiclib.pyproj_crs(cfg['out_crs'])

    # get image ground sampling distance
    cfg['gsd'] = rpc_utils.gsd_from_rpc(cfg['images'][0]['rpcm'])
Exemplo n.º 4
0
def global_dsm(tiles):
    """
    """
    out_dsm_vrt = os.path.join(cfg['out_dir'], 'dsm.vrt')
    out_dsm_tif = os.path.join(cfg['out_dir'], 'dsm.tif')

    dsms_list = [os.path.join(t['dir'], 'dsm.tif') for t in tiles]
    dsms = '\n'.join(d for d in dsms_list if os.path.exists(d))

    input_file_list = os.path.join(cfg['out_dir'],
                                   'gdalbuildvrt_input_file_list.txt')

    with open(input_file_list, 'w') as f:
        f.write(dsms)

    common.run("gdalbuildvrt -vrtnodata nan -input_file_list %s %s" %
               (input_file_list, out_dsm_vrt))

    res = cfg['dsm_resolution']

    if 'roi_geojson' in cfg:
        ll_poly = geographiclib.read_lon_lat_poly_from_geojson(
            cfg['roi_geojson'])
        pyproj_crs = geographiclib.pyproj_crs(cfg['out_crs'])
        bbx = geographiclib.crs_bbx(ll_poly, pyproj_crs)
        xoff = bbx[0]
        yoff = bbx[3]
        xsize = int(np.ceil((bbx[1] - bbx[0]) / res))
        ysize = int(np.ceil((bbx[3] - bbx[2]) / res))
        projwin = "-projwin {} {} {} {}".format(xoff, yoff, xoff + xsize * res,
                                                yoff - ysize * res)
    else:
        projwin = ""

    common.run(" ".join([
        "gdal_translate", "-co", "TILED=YES", "-co", "COMPRESS=DEFLATE", "-co",
        "PREDICTOR=2", "-co", "BIGTIFF=IF_SAFER", projwin, out_dsm_vrt,
        out_dsm_tif
    ]))

    # EXPORT CONFIDENCE
    out_conf_vrt = os.path.join(cfg['out_dir'], 'confidence.vrt')
    out_conf_tif = os.path.join(cfg['out_dir'], 'confidence.tif')

    dsms_list = [os.path.join(t['dir'], 'confidence.tif') for t in tiles]
    dems_list_ok = [d for d in dsms_list if os.path.exists(d)]
    dsms = '\n'.join(dems_list_ok)

    input_file_list = os.path.join(cfg['out_dir'],
                                   'gdalbuildvrt_input_file_list2.txt')

    if len(dems_list_ok) > 0:

        with open(input_file_list, 'w') as f:
            f.write(dsms)

        common.run("gdalbuildvrt -vrtnodata nan -input_file_list %s %s" %
                   (input_file_list, out_conf_vrt))

        common.run(" ".join([
            "gdal_translate", "-co TILED=YES -co BIGTIFF=IF_SAFER",
            "%s %s %s" % (projwin, out_conf_vrt, out_conf_tif)
        ]))
Exemplo n.º 5
0
def disparity_to_ply(tile):
    """
    Compute a point cloud from the disparity map of a pair of image tiles.

    This function is called by s2p.main only if there are two input images (not
    three).

    Args:
        tile: dictionary containing the information needed to process a tile.
    """
    out_dir = tile['dir']
    ply_file = os.path.join(out_dir, 'cloud.ply')
    plyextrema = os.path.join(out_dir, 'plyextrema.txt')
    x, y, w, h = tile['coordinates']
    rpc1 = cfg['images'][0]['rpcm']
    rpc2 = cfg['images'][1]['rpcm']

    print('triangulating tile {} {}...'.format(x, y))
    H_ref = os.path.join(out_dir, 'pair_1', 'H_ref.txt')
    H_sec = os.path.join(out_dir, 'pair_1', 'H_sec.txt')
    pointing = os.path.join(cfg['out_dir'], 'global_pointing_pair_1.txt')
    disp = os.path.join(out_dir, 'pair_1', 'rectified_disp.tif')
    extra = os.path.join(out_dir, 'pair_1', 'rectified_disp_confidence.tif')
    if not os.path.exists(extra):  # confidence file not always generated
        extra = ''
    mask_rect = os.path.join(out_dir, 'pair_1', 'rectified_mask.png')
    mask_orig = os.path.join(out_dir, 'mask.png')

    # prepare the image needed to colorize point cloud
    colors = os.path.join(out_dir, 'rectified_ref.png')
    if cfg['images'][0]['clr']:
        hom = np.loadtxt(H_ref)
        # we want rectified_ref.png and rectified_ref.tif to have the same size
        with rasterio.open(os.path.join(out_dir, 'pair_1',
                                        'rectified_ref.tif')) as f:
            ww, hh = f.width, f.height
        common.image_apply_homography(colors, cfg['images'][0]['clr'], hom, ww,
                                      hh)
    else:
        common.image_qauto(
            os.path.join(out_dir, 'pair_1', 'rectified_ref.tif'), colors)

    # compute the point cloud
    with rasterio.open(disp, 'r') as f:
        disp_img = f.read().squeeze()
    with rasterio.open(mask_rect, 'r') as f:
        mask_rect_img = f.read().squeeze()

    pyproj_out_crs = geographiclib.pyproj_crs(cfg['out_crs'])
    proj_com = "CRS {}".format(cfg['out_crs'])
    xyz_array, err = triangulation.disp_to_xyz(rpc1,
                                               rpc2,
                                               np.loadtxt(H_ref),
                                               np.loadtxt(H_sec),
                                               disp_img,
                                               mask_rect_img,
                                               pyproj_out_crs,
                                               img_bbx=(x, x + w, y, y + h),
                                               A=np.loadtxt(pointing))

    triangulation.filter_xyz_and_write_to_ply(ply_file,
                                              xyz_array,
                                              cfg['3d_filtering_r'],
                                              cfg['3d_filtering_n'],
                                              cfg['gsd'],
                                              colors,
                                              proj_com,
                                              confidence=extra)

    # compute the point cloud extrema (xmin, xmax, xmin, ymax)
    common.run("plyextrema %s %s" % (ply_file, plyextrema))

    if cfg['clean_intermediate']:
        common.remove(H_ref)
        common.remove(H_sec)
        common.remove(disp)
        common.remove(mask_rect)
        common.remove(mask_orig)
        common.remove(colors)
        common.remove(os.path.join(out_dir, 'pair_1', 'rectified_ref.tif'))
Exemplo n.º 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