Esempio n. 1
0
def plot_matches_low_level(im1, im2, matches):
    """
    Displays two images side by side with matches highlighted

    Args:
        im1, im2: paths to the two input images
        matches: 2D numpy array of size 4xN containing a list of matches (a
            list of pairs of points, each pair being represented by x1, y1, x2,
            y2)

    Returns:
        path to the resulting image, to be displayed
    """
    # load images
    img1 = piio.read(im1).astype(np.uint8)
    img2 = piio.read(im2).astype(np.uint8)

    # if images have more than 3 channels, keep only the first 3
    if img1.shape[2] > 3:
        img1 = img1[:, :, 0:3]
    if img2.shape[2] > 3:
        img2 = img2[:, :, 0:3]

    # build the output image
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    w = w1 + w2
    h = max(h1, h2)
    out = np.zeros((h, w, 3), np.uint8)
    out[:h1, :w1] = img1
    out[:h2, w1:w] = img2

    # define colors, according to min/max intensity values
    out_min = min(np.nanmin(img1), np.nanmin(img2))
    out_max = max(np.nanmax(img1), np.nanmax(img2))
    green = [out_min, out_max, out_min]
    blue = [out_min, out_min, out_max]

    # plot the matches
    for i in range(len(matches)):
        x1 = matches[i, 0]
        y1 = matches[i, 1]
        x2 = matches[i, 2] + w1
        y2 = matches[i, 3]
        # convert endpoints to int (nn interpolation)
        x1, y1, x2, y2 = list(map(int, np.round([x1, y1, x2, y2])))
        plot_line(out, x1, y1, x2, y2, blue)
        try:
            out[y1, x1] = green
            out[y2, x2] = green
        except IndexError:
            pass
    # save the output image, and return its path
    outfile = common.tmpfile('.png')
    piio.write(outfile, out)
    return outfile
Esempio n. 2
0
def merge_n(output, inputs, offsets, averaging='average_if_close', threshold=1):
    """
    Merge n images of equal sizes by taking the median/mean/min/max pixelwise.

    Args:
        inputs: list of paths to the input images
        output: path to the output image
        averaging: string containing the name of a function that accepts
            1D arrays. It is applied to 1D slices of the stack of images along
            the last axis. Possible values are, for instance np.min, np.max,
            np.mean, np.median and their nanproof counterparts, ie np.nanmin,
            np.nanmax, np.nanmean, np.nanmedian
    """
    assert(len(inputs) == len(offsets))

    # get input images size
    if inputs:
        f = gdal.Open(inputs[0])
        w, h = f.RasterXSize, f.RasterYSize
        f = None  # this is the gdal way of closing files

    # read input images and apply offsets
    x = np.empty((h, w, len(inputs)))
    for i, img in enumerate(inputs):
        f = gdal.Open(img)
        x[:, :, i] = f.GetRasterBand(1).ReadAsArray() - offsets[i]
        f = None
        if cfg['debug']:
            piio.write('{}_registered.tif'.format(os.path.splitext(img)[0]),
                       x[:, :, i] + np.mean(offsets))

    # apply the averaging operator
    if averaging.startswith(('np.', 'numpy.')):
        avg = np.apply_along_axis(getattr(sys.modules['numpy'], averaging.split('.')[1]),
                                  axis=2, arr=x)
    elif averaging == 'average_if_close':
        avg = np.apply_along_axis(average_if_close, 2, x, threshold)

    # add the mean offset
    avg += np.mean(offsets)

    # write the average to output
    if inputs:
        shutil.copy(inputs[0], output)  # copy an input file to get the metadata
        f = gdal.Open(output, gdal.GA_Update)
        f.GetRasterBand(1).WriteArray(avg)  # update the output file content
        f = None
Esempio n. 3
0
def tiles_full_info(tw, th, tiles_txt, create_masks=False):
    """
    List the tiles to process and prepare their output directories structures.

    Most of the time is spent discarding tiles that are masked by water
    (according to SRTM data).

    Returns:
        a list of dictionaries. Each dictionary contains the image coordinates
        and the output directory path of a tile.
    """
    rpc = cfg['images'][0]['rpc']
    roi_msk = cfg['images'][0]['roi']
    cld_msk = cfg['images'][0]['cld']
    wat_msk = cfg['images'][0]['wat']
    rx = cfg['roi']['x']
    ry = cfg['roi']['y']
    rw = cfg['roi']['w']
    rh = cfg['roi']['h']

    # build a tile dictionary for all non-masked tiles and store them in a list
    tiles = []
    # list tiles coordinates
    tiles_coords, neighborhood_coords_dict = compute_tiles_coordinates(
        rx, ry, rw, rh, tw, th)

    if os.path.exists(tiles_txt) is False or create_masks is True:
        print('\ndiscarding masked tiles...')
        # compute all masks in parallel as numpy arrays
        tiles_masks = parallel.launch_calls_simple(
            masking.cloud_water_image_domain, tiles_coords,
            cfg['max_processes'], rpc, roi_msk, cld_msk, wat_msk,
            cfg['use_srtm_for_water'])
        for coords, mask in zip(tiles_coords, tiles_masks):
            if mask.any():  # there's at least one non-masked pixel in the tile
                tile = create_tile(coords, neighborhood_coords_dict)
                tiles.append(tile)

                # make tiles directories and store json configuration dumps
                common.mkdir_p(tile['dir'])
                for i in range(1, len(cfg['images'])):
                    common.mkdir_p(
                        os.path.join(tile['dir'], 'pair_{}'.format(i)))

                # save a json dump of the tile configuration
                tile_cfg = copy.deepcopy(cfg)
                x, y, w, h = tile['coordinates']
                tile_cfg['roi'] = {'x': x, 'y': y, 'w': w, 'h': h}
                tile_cfg['full_img'] = False
                tile_cfg['max_processes'] = 1
                tile_cfg['omp_num_threads'] = 1
                tile_cfg['neighborhood_dirs'] = tile['neighborhood_dirs']
                tile_cfg['out_dir'] = '../../..'

                with open(os.path.join(cfg['out_dir'], tile['json']),
                          'w') as f:
                    json.dump(tile_cfg,
                              f,
                              indent=2,
                              default=workaround_json_int64)

                # save the mask
                piio.write(
                    os.path.join(tile['dir'],
                                 'cloud_water_image_domain_mask.png'),
                    mask.astype(np.uint8))
    else:
        if len(tiles_coords) == 1:
            tiles.append(create_tile(tiles_coords[0],
                                     neighborhood_coords_dict))
        else:
            with open(tiles_txt, 'r') as f_tiles:
                for config_json in f_tiles:
                    tile = {}
                    with open(
                            os.path.join(cfg['out_dir'],
                                         config_json.rstrip(os.linesep)),
                            'r') as f_config:
                        tile_cfg = json.load(f_config)
                        roi = tile_cfg['roi']
                        coords = roi['x'], roi['y'], roi['w'], roi['h']
                        tiles.append(
                            create_tile(coords, neighborhood_coords_dict))

    return tiles
Esempio n. 4
0
def tiles_full_info(tw, th):
    """
    List the tiles to process and prepare their output directories structures.

    Most of the time is spent discarding tiles that are masked by water
    (according to SRTM data).

    Returns:
        a list of dictionaries. Each dictionary contains the image coordinates
        and the output directory path of a tile.
    """
    rpc = cfg['images'][0]['rpc']
    roi_msk = cfg['images'][0]['roi']
    cld_msk = cfg['images'][0]['cld']
    wat_msk = cfg['images'][0]['wat']
    z =  cfg['subsampling_factor']
    rx = cfg['roi']['x']
    ry = cfg['roi']['y']
    rw = cfg['roi']['w']
    rh = cfg['roi']['h']

    # list tiles coordinates
    tiles_coords, neighborhood_coords_dict = compute_tiles_coordinates(rx, ry, rw, rh, tw, th, z)

    # compute all masks in parallel as numpy arrays
    tiles_masks = parallel.launch_calls_simple(masking.cloud_water_image_domain,
                                               tiles_coords,
                                               cfg['max_processes'], rpc,
                                               roi_msk, cld_msk, wat_msk,
                                               cfg['use_srtm_for_water'])

    # build a tile dictionary for all non-masked tiles and store them in a list
    tiles = []
    for coords, mask in zip(tiles_coords,
                            tiles_masks):
        if mask.any():  # there's at least one non-masked pixel in the tile
            tile = {}
            x, y, w, h = coords
            tile['dir'] = os.path.join(cfg['out_dir'],get_tile_dir(x, y, w, h))
            tile['coordinates'] = coords
            tile['mask'] = mask
            tile['neighborhood_dirs'] = list()
            key = str((x, y, w, h))

            if 'neighborhood_dirs' in cfg:
                tile['neighborhood_dirs'] = cfg['neighborhood_dirs']
            elif key in neighborhood_coords_dict:
                for coords2 in neighborhood_coords_dict[key]:
                    x2, y2, w2, h2 = coords2
                    tile['neighborhood_dirs'].append(os.path.join('../../..',get_tile_dir(x2,
                                                                  y2,
                                                                  w2,
                                                                  h2)))
            tiles.append(tile)

    # make tiles directories and store json configuration dumps
    for tile in tiles:
        common.mkdir_p(tile['dir'])
        for i in range(1, len(cfg['images'])):
            common.mkdir_p(os.path.join(tile['dir'], 'pair_{}'.format(i)))

        # save a json dump of the tile configuration
        tile_cfg = copy.deepcopy(cfg)
        x, y, w, h = tile['coordinates']
        tile_cfg['roi'] = {'x': x, 'y': y, 'w': w, 'h': h}
        tile_cfg['full_img'] = False
        tile_cfg['max_processes'] = 1
        tile_cfg['omp_num_threads'] = 1
        tile_cfg['neighborhood_dirs'] = tile['neighborhood_dirs']
        tile_cfg['out_dir']='../../..'

        tile_json = os.path.join(get_tile_dir(x,y,w,h),'config.json')
        tile['json'] = tile_json

        with open(os.path.join(cfg['out_dir'],tile_json), 'w') as f:
            json.dump(tile_cfg, f, indent=2)

        # save the mask
        piio.write(os.path.join(tile['dir'],
                                'cloud_water_image_domain_mask.png'),
                   tile['mask'].astype(np.uint8))

    return tiles
Esempio n. 5
0
def tiles_full_info(tw, th):
    """
    List the tiles to process and prepare their output directories structures.

    Most of the time is spent discarding tiles that are masked by water
    (according to SRTM data).

    Returns:
        a list of dictionaries. Each dictionary contains the image coordinates
        and the output directory path of a tile.
    """
    rpc = cfg['images'][0]['rpc']
    roi_msk = cfg['images'][0]['roi']
    cld_msk = cfg['images'][0]['cld']
    wat_msk = cfg['images'][0]['wat']
    z =  cfg['subsampling_factor']
    rx = cfg['roi']['x']
    ry = cfg['roi']['y']
    rw = cfg['roi']['w']
    rh = cfg['roi']['h']

    # list tiles coordinates
    tiles_coords = compute_tiles_coordinates(rx, ry, rw, rh, tw, th, z)

    # compute all masks in parallel as numpy arrays
    tiles_masks = parallel.launch_calls_simple(masking.cloud_water_image_domain,
                                               tiles_coords,
                                               cfg['max_processes'], rpc,
                                               roi_msk, cld_msk, wat_msk,
                                               cfg['use_srtm_for_water'])

    # build a tile dictionary for all non-masked tiles and store them in a list
    tiles = []
    for coords, mask in zip(tiles_coords, tiles_masks):
        if mask.any():  # there's at least one non-masked pixel in the tile
            tile = {}
            x, y, w, h = coords
            tile['dir'] = os.path.join(cfg['out_dir'],
                                       'tiles_row_{}_height_{}'.format(y, h),
                                       'col_{}_width_{}'.format(x, w))
            tile['coordinates'] = coords
            tile['mask'] = mask
            tiles.append(tile)

    # make tiles directories and store json configuration dumps
    for tile in tiles:
        common.mkdir_p(tile['dir'])
        for i in range(1, len(cfg['images'])):
            common.mkdir_p(os.path.join(tile['dir'], 'pair_{}'.format(i)))

        # save a json dump of the tile configuration
        tile_cfg = copy.deepcopy(cfg)
        x, y, w, h = tile['coordinates']
        tile_cfg['roi'] = {'x': x, 'y': y, 'w': w, 'h': h}
        tile_cfg['max_processes'] = 1
        tile_cfg['omp_num_threads'] = 1

        tile_json = os.path.join(tile['dir'], 'config.json')
        tile['json'] = tile_json

        with open(tile_json, 'w') as f:
            json.dump(tile_cfg, f, indent=2)

        # save the mask
        piio.write(os.path.join(tile['dir'],
                                'cloud_water_image_domain_mask.png'),
                   tile['mask'].astype(np.uint8))

    return tiles