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
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
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
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
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