Exemple #1
0
def compute_dsm(args):
    """
    Compute the DSMs

    Args:
         - args  ( <==> [config_file,number_of_tiles,current_tile])
    """
    list_of_tiles_dir = os.path.join(cfg['out_dir'],'list_of_tiles.txt')

    config_file,number_of_tiles,current_tile = args

    dsm_dir = os.path.join(cfg['out_dir'],'dsm')
    out_dsm = os.path.join(dsm_dir,'dsm_%d.tif' % (current_tile) )

    extremaxy = np.loadtxt(os.path.join(cfg['out_dir'], 'global_extent.txt'))

    global_xmin,global_xmax,global_ymin,global_ymax = extremaxy

    global_y_diff = global_ymax-global_ymin
    tile_y_size = (global_y_diff)/(number_of_tiles)

    # horizontal cuts
    ymin = global_ymin + current_tile*tile_y_size
    ymax = ymin + tile_y_size

    # cutting info
    x, y, w, h, z, ov, tw, th, nb_pairs = initialization.cutting(config_file)
    range_y = np.arange(y, y + h - ov, th - ov)
    range_x = np.arange(x, x + w - ov, tw - ov)
    colmin, rowmin, tw, th = common.round_roi_to_nearest_multiple(z, range_x[0], range_y[0], tw, th)
    colmax, rowmax, tw, th = common.round_roi_to_nearest_multiple(z, range_x[-1], range_y[-1], tw, th)
    cutsinf = '%d %d %d %d %d %d %d %d' % (rowmin, th - ov, rowmax, colmin, tw - ov, colmax, tw, th)

    flags = {}
    flags['average-orig'] = 0
    flags['average'] = 1
    flags['variance'] = 2
    flags['min'] = 3
    flags['max'] = 4
    flags['median'] = 5
    flag = "-flag %d" % (flags.get(cfg['dsm_option'], 0))

    if (ymax <= global_ymax):
        common.run("plytodsm %s %f %s %f %f %f %f %s %s" % (flag,
                                                            cfg['dsm_resolution'],
                                                            out_dsm,
                                                            global_xmin,
                                                            global_xmax, ymin,
                                                            ymax, cutsinf,
                                                            cfg['out_dir']))
Exemple #2
0
def init_roi(config_file):
    """
    1) Loads configuration file
    2) Checks parameters
    3) Selects the ROI
    4) Checks the zoom factor
    """
	
    # read the json configuration file
    f = open(config_file)
    user_cfg = json.load(f)
    f.close()

    # Check that all the mandatory arguments are defined, and warn about
    # 'unknown' params
    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)

    # sets 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')

    # update roi definition if the full_img flag is set to true
    if ('full_img' in cfg) and cfg['full_img']:
        sz = common.image_size_tiffinfo(cfg['images'][0]['img'])
        cfg['roi'] = {}
        cfg['roi']['x'] = 0
        cfg['roi']['y'] = 0
        cfg['roi']['w'] = sz[0]
        cfg['roi']['h'] = sz[1]

    # check that the roi is well defined
    if 'roi' not in cfg or any(p not in cfg['roi'] for p in ['x', 'y', 'w',
                                                             'h']):
        print "missing or incomplete ROI definition"
        print "ROI will be redefined by interactive selection"
        x, y, w, h = common.get_roi_coordinates(cfg['images'][0]['img'],
                                                cfg['images'][0]['prv'])
        cfg['roi'] = {}
        cfg['roi']['x'] = x
        cfg['roi']['y'] = y
        cfg['roi']['w'] = w
        cfg['roi']['h'] = h
    else :
        x = cfg['roi']['x']    
        y = cfg['roi']['y']
        w = cfg['roi']['w']
        h = cfg['roi']['h']

    try:
        print "ROI x, y, w, h = %d, %d, %d, %d" % (x, y, w, h)
    except TypeError:
        print 'Neither a ROI nor a preview file are defined. Aborting.'
        return

    # check the zoom factor
    z = cfg['subsampling_factor']
    assert(z > 0 and z == np.floor(z))
       
    # ensure that the coordinates of the ROI are multiples of the zoom factor,
    # to avoid bad registration of tiles due to rounding problems.          
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)
    cfg['roi']['x'] = x    
    cfg['roi']['y'] = y
    cfg['roi']['w'] = w
    cfg['roi']['h'] = h
Exemple #3
0
def init_tiles_full_info(config_file):
    """
    Prepare the entire process.

    1) Make sure coordinates of the ROI are multiples of the zoom factor
    2) Compute optimal size for tiles, get the number of pairs
    3) Build tiles_full_info: a list of dictionaries, one per tile, providing all you need to process a tile
       * col/row : position of the tile (upper left corner)
       * tw/th : size of the tile
       * ov : size of the overlapping
       * i/j : relative position of the tile
       * pos : position inside the ROI : UL for a tile place at th Upper Left corner, M for the ones placed in the middle, and so forth.
       * x/y/w/h : information about the ROI
       * images : a dictionary directly given by the json config file, that store the information about all the involved images, their rpc, and so forth.
       * nb_pairs : number of pairs
       * cld_msk/roi_msk : path to a gml file containing a cloud mask/ defining the area contained in the full image

    Args:
         config_file: path to a json configuration file

    Returns:
        tiles_full_info: list containing dictionaries
    """

    init_roi(config_file)

    #Get ROI
    x = cfg['roi']['x']    
    y = cfg['roi']['y']
    w = cfg['roi']['w']
    h = cfg['roi']['h']    
    z = cfg['subsampling_factor']

    # Automatically compute optimal size for tiles
    # tw, th : dimensions of the tiles
    # ov : width of overlapping bands between tiles
    ov = z * 100
    if w <= z * cfg['tile_size']:
        tw = w
    else:
        tw = z * cfg['tile_size']
    if h <= z * cfg['tile_size']:
        th = h
    else:
        th = z * cfg['tile_size']

    ntx = np.ceil(float(w - ov) / (tw - ov))
    nty = np.ceil(float(h - ov) / (th - ov))
    nt = ntx * nty

    print 'tiles size: (%d, %d)' % (tw, th)
    print 'total number of tiles: %d (%d x %d)' % (nt, ntx, nty)
    nb_pairs = len(cfg['images']) - 1
    print 'total number of pairs: %d' % nb_pairs

    # build tile_info dictionaries and store them in a list
    tiles_full_info = list()
    range_y = np.arange(y, y + h - ov, th - ov)
    range_x = np.arange(x, x + w - ov, tw - ov)
    rowmin, rowmax = range_y[0], range_y[-1]
    colmin, colmax = range_x[0], range_x[-1]

    for i, row in enumerate(range_y):
        for j, col in enumerate(range_x):
            # ensure that tile coordinates are multiples of the zoom factor
            col, row, tw, th = common.round_roi_to_nearest_multiple(z, col, row,
                                                                    tw, th)
            tile_dir = os.path.join(cfg['out_dir'], 'tile_%d_%d_row_%d' % (tw,
                                                                           th,
                                                                           row),
                                    'col_%d' % col)

            if row == rowmin and col == colmin:
                pos = 'UL'
            elif row == rowmin and col == colmax:
                pos = 'UR'
            elif row == rowmax and col == colmax:
                pos = 'BR'
            elif row == rowmax and col == colmin:
                pos = 'BL'
            elif row == rowmin and col > colmin:
                pos = 'U'
            elif col == colmin and row > rowmin:
                pos = 'L'
            elif row == rowmax and col > colmin:
                pos = 'B'
            elif col == colmax and row > rowmin:
                pos = 'R'
            else:
                pos = 'M'

            tile_info = {}
            tile_info['directory'] = tile_dir
            tile_info['coordinates'] = (col, row, tw, th)
            tile_info['index_in_roi'] = (i, j)
            tile_info['position_type'] = pos
            tile_info['roi_coordinates'] = (x, y, w, h)
            tile_info['overlap'] = ov
            tile_info['number_of_pairs'] = nb_pairs
            tile_info['images'] = cfg['images']
            tiles_full_info.append(tile_info)

    if len(tiles_full_info) == 1:
        tiles_full_info[0]['position_type'] = 'Single'

    return tiles_full_info
Exemple #4
0
def init_roi(config_file):
    """
    1) Loads configuration file
    2) Checks parameters
    3) Selects the ROI
    4) Checks the zoom factor
    """

    # read the json configuration file
    f = open(config_file)
    user_cfg = json.load(f)
    f.close()

    # Check that all the mandatory arguments are defined, and warn about
    # 'unknown' params
    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)

    # sets 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')

    # update roi definition if the full_img flag is set to true
    if ('full_img' in cfg) and cfg['full_img']:
        sz = common.image_size_tiffinfo(cfg['images'][0]['img'])
        cfg['roi'] = {}
        cfg['roi']['x'] = 0
        cfg['roi']['y'] = 0
        cfg['roi']['w'] = sz[0]
        cfg['roi']['h'] = sz[1]

    # check that the roi is well defined
    if 'roi' not in cfg or any(p not in cfg['roi']
                               for p in ['x', 'y', 'w', 'h']):
        print "missing or incomplete ROI definition"
        print "ROI will be redefined by interactive selection"
        x, y, w, h = common.get_roi_coordinates(cfg['images'][0]['img'],
                                                cfg['images'][0]['prv'])
        cfg['roi'] = {}
        cfg['roi']['x'] = x
        cfg['roi']['y'] = y
        cfg['roi']['w'] = w
        cfg['roi']['h'] = h
    else:
        x = cfg['roi']['x']
        y = cfg['roi']['y']
        w = cfg['roi']['w']
        h = cfg['roi']['h']

    try:
        print "ROI x, y, w, h = %d, %d, %d, %d" % (x, y, w, h)
    except TypeError:
        print 'Neither a ROI nor a preview file are defined. Aborting.'
        return

    # check the zoom factor
    z = cfg['subsampling_factor']
    assert (z > 0 and z == np.floor(z))

    # ensure that the coordinates of the ROI are multiples of the zoom factor,
    # to avoid bad registration of tiles due to rounding problems.
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)
    cfg['roi']['x'] = x
    cfg['roi']['y'] = y
    cfg['roi']['w'] = w
    cfg['roi']['h'] = h

    # get utm zone
    utm_zone = rpc_utils.utm_zone(
        cfg['images'][0]['rpc'],
        *[cfg['roi'][v] for v in ['x', 'y', 'w', 'h']])
    cfg['utm_zone'] = utm_zone
Exemple #5
0
def init_tiles_full_info(config_file):
    """
    Prepare the entire process.

    Build tiles_full_info: a list of dictionaries, one per tile, providing all you need to process a tile
       * col/row : position of the tile (upper left corner)
       * tw/th : size of the tile
       * ov : size of the overlapping
       * i/j : relative position of the tile
       * pos : position inside the ROI : UL for a tile place at th Upper Left corner, M for the ones placed in the middle, and so forth.
       * x/y/w/h : information about the ROI
       * images : a dictionary directly given by the json config file, that store the information about all the involved images, their rpc, and so forth.
       * nb_pairs : number of pairs
       * cld_msk/roi_msk : path to a gml file containing a cloud mask/ defining the area contained in the full image

    Args:
         config_file: path to a json configuration file

    Returns:
        tiles_full_info: list containing dictionaries
    """

    x, y, w, h, z, ov, tw, th, nb_pairs = cutting(config_file)

    # build tile_info dictionaries and store them in a list
    tiles_full_info = list()
    range_y = np.arange(y, y + h - ov, th - ov)
    range_x = np.arange(x, x + w - ov, tw - ov)
    rowmin, rowmax = range_y[0], range_y[-1]
    colmin, colmax = range_x[0], range_x[-1]

    for i, row in enumerate(range_y):
        for j, col in enumerate(range_x):
            # ensure that tile coordinates are multiples of the zoom factor
            col, row, tw, th = common.round_roi_to_nearest_multiple(
                z, col, row, tw, th)
            tile_dir = os.path.join(cfg['out_dir'],
                                    'tile_%d_%d_row_%d' % (tw, th, row),
                                    'col_%d' % col)

            if row == rowmin and col == colmin:
                pos = 'UL'
            elif row == rowmin and col == colmax:
                pos = 'UR'
            elif row == rowmax and col == colmax:
                pos = 'BR'
            elif row == rowmax and col == colmin:
                pos = 'BL'
            elif row == rowmin and col > colmin:
                pos = 'U'
            elif col == colmin and row > rowmin:
                pos = 'L'
            elif row == rowmax and col > colmin:
                pos = 'B'
            elif col == colmax and row > rowmin:
                pos = 'R'
            else:
                pos = 'M'

            tile_info = {}
            tile_info['directory'] = tile_dir
            tile_info['coordinates'] = (col, row, tw, th)
            tile_info['index_in_roi'] = (i, j)
            tile_info['position_type'] = pos
            tile_info['roi_coordinates'] = (x, y, w, h)
            tile_info['overlap'] = ov
            tile_info['number_of_pairs'] = nb_pairs
            tile_info['images'] = cfg['images']
            tiles_full_info.append(tile_info)

    if len(tiles_full_info) == 1:
        tiles_full_info[0]['position_type'] = 'Single'

    return tiles_full_info
Exemple #6
0
def generate_cloud(out_dir, height_map, rpc1, x, y, w, h, im1, clr,
                   do_offset=False):
    """
    Args:
        out_dir: output directory. The file cloud.ply will be written there
        height_map: path to the height map, produced by the process_pair
            or process_triplet function
        rpc1: path to the xml file containing rpc coefficients for the
            reference image
        x, y, w, h: four integers defining the rectangular ROI in the original
            panchro image. (x, y) is the top-left corner, and (w, h) are the
            dimensions of the rectangle.
        im1:  path to the panchro reference image
        clr:  path to the xs (multispectral, ie color) reference image
        do_offset (optional, default: False): boolean flag to decide wether the
            x, y coordinates of points in the ply file will be translated or
            not (translated to be close to 0, to avoid precision loss due to
            huge numbers)
    """
    print "\nComputing point cloud..."

    # output files
    crop_ref = '%s/roi_ref.tif' % out_dir
    cloud = '%s/cloud.ply' % out_dir
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)

    # ensure that the coordinates of the ROI are multiples of the zoom factor,
    # to avoid bad registration of tiles due to rounding problems.
    z = cfg['subsampling_factor']
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)

    # build the matrix of the zoom + translation transformation
    if cfg['full_img'] and z == 1:
        trans = None
    else:
        A = common.matrix_translation(-x, -y)
        f = 1.0/z
        Z = np.diag([f, f, 1])
        A = np.dot(Z, A)
        trans = '%s/trans.txt' % out_dir
        np.savetxt(trans, A)

    # compute offset
    if do_offset:
        r = rpc_model.RPCModel(rpc1)
        lat = r.latOff
        lon = r.lonOff
        off_x, off_y = geographiclib.geodetic_to_utm(lat, lon)[0:2]
    else:
        off_x, off_y = 0, 0

    # crop the ROI in ref image, then zoom
    if cfg['full_img'] and z == 1:
        crop_ref = im1
    else:
        if z == 1:
            common.image_crop_TIFF(im1, x, y, w, h, crop_ref)
        else:
            # gdal is used for the zoom because it handles BigTIFF files, and
            # before the zoom out the image may be that big
            tmp_crop = common.image_crop_TIFF(im1, x, y, w, h)
            common.image_zoom_gdal(tmp_crop, z, crop_ref, w, h)

    if cfg['color_ply']:
        crop_color = '%s/roi_color_ref.tif' % out_dir
        if clr is not None:
            print 'colorizing...'
            triangulation.colorize(crop_ref, clr, x, y, z, crop_color)
        elif common.image_pix_dim_tiffinfo(crop_ref) == 4:
            print 'the image is pansharpened fusioned'
            tmp = common.rgbi_to_rgb(crop_ref, out=None, tilewise=True)
            common.image_qauto(tmp, crop_color, tilewise=False)
        else:
            print 'no color data'
            common.image_qauto(crop_ref, crop_color, tilewise=False)
    else:
        crop_color = ''

    triangulation.compute_point_cloud(cloud, height_map, rpc1, trans, crop_color,
                                      off_x, off_y)
    common.garbage_cleanup()
Exemple #7
0
def process_pair_single_tile(out_dir, img1, rpc1, img2, rpc2, x=None, y=None,
                             w=None, h=None, prv1=None, cld_msk=None,
                             roi_msk=None, A=None):
    """
    Computes a disparity map from a Pair of Pleiades images, without tiling

    Args:
        out_dir: path to the output directory
        img1: path to the reference image.
        rpc1: paths to the xml file containing the rpc coefficients of the
            reference image
        img2: path to the secondary image.
        rpc2: paths to the xml file containing the rpc coefficients of the
            secondary image
        x, y, w, h: four integers defining the rectangular ROI in the reference
            image. (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle.
        prv1 (optional): path to a preview of the reference image
        cld_msk (optional): path to a gml file containing a cloud mask
        roi_msk (optional): path to a gml file containing a mask defining the
            area contained in the full image.
        A (optional, default None): pointing correction matrix. If None, it
            will be estimated by this function.

    Returns:
        nothing
    """
    # create a directory for the experiment
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)

    # output files
    rect1 = '%s/rectified_ref.tif' % (out_dir)
    rect2 = '%s/rectified_sec.tif' % (out_dir)
    disp = '%s/rectified_disp.tif' % (out_dir)
    mask = '%s/rectified_mask.png' % (out_dir)
    cwid_msk = '%s/cloud_water_image_domain_mask.png' % (out_dir)
    subsampling = '%s/subsampling.txt' % (out_dir)
    pointing = '%s/pointing.txt' % out_dir
    center = '%s/center_keypts_sec.txt' % out_dir
    sift_matches = '%s/sift_matches.txt' % out_dir
    sift_matches_plot = '%s/sift_matches_plot.png' % out_dir
    H_ref = '%s/H_ref.txt' % out_dir
    H_sec = '%s/H_sec.txt' % out_dir
    disp_min_max = '%s/disp_min_max.txt' % out_dir
    config = '%s/config.json' % out_dir

    # select ROI
    try:
        print "ROI x, y, w, h = %d, %d, %d, %d" % (x, y, w, h)
    except TypeError:
        if prv1:
            x, y, w, h = common.get_roi_coordinates(img1, prv1)
        else:
            print 'Neither a ROI nor a preview file are defined. Aborting.'
            return

    # redirect stdout and stderr to log file
    if not cfg['debug']:
        fout = open('%s/stdout.log' % out_dir, 'w', 0)  # '0' for no buffering
        sys.stdout = fout
        sys.stderr = fout

    # debug print
    print 'tile %d %d running on process %s' % (x, y,
                                                multiprocessing.current_process())

    # ensure that the coordinates of the ROI are multiples of the zoom factor
    z = cfg['subsampling_factor']
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)

    # check if the ROI is completely masked (water, or outside the image domain)
    H = np.array([[1, 0, -x], [0, 1, -y], [0, 0, 1]])
    if masking.cloud_water_image_domain(cwid_msk, w, h, H, rpc1, roi_msk,
                                        cld_msk):
        print "Tile masked by water or outside definition domain, skip"
        open("%s/this_tile_is_masked.txt" % out_dir, 'a').close()
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__
        if not cfg['debug']:
            fout.close()
        return

    # correct pointing error
    # A is the correction matrix and m is the list of sift matches
    if A is None:
        A, m = pointing_accuracy.compute_correction(img1, rpc1, img2, rpc2, x,
                                                    y, w, h)
        if A is not None:
            np.savetxt(pointing, A)
        if m is not None:
            np.savetxt(sift_matches, m)
            np.savetxt(center, np.mean(m[:, 2:4], 0))
            visualisation.plot_matches_pleiades(img1, img2, rpc1, rpc2, m, x, y,
                                                w, h, sift_matches_plot)
    else:
        m = None

    # rectification
    H1, H2, disp_min, disp_max = rectification.rectify_pair(img1, img2, rpc1,
                                                            rpc2, x, y, w, h,
                                                            rect1, rect2, A, m)

    # block-matching
    if cfg['disp_min'] is not None:
        disp_min = cfg['disp_min']
    if cfg['disp_max'] is not None:
        disp_max = cfg['disp_max']
    block_matching.compute_disparity_map(rect1, rect2, disp, mask,
                                         cfg['matching_algorithm'], disp_min,
                                         disp_max)

    # intersect mask with the cloud_water_image_domain mask (recomputed here to
    # get to be sampled on the epipolar grid)
    ww, hh = common.image_size(rect1)
    masking.cloud_water_image_domain(cwid_msk, ww, hh, H1, rpc1, roi_msk,
                                     cld_msk)
    try:
        masking.intersection(mask, mask, cwid_msk)
        masking.erosion(mask, mask, cfg['msk_erosion'])
    except OSError:
        print "file %s not produced" % mask

    # save the subsampling factor, the rectifying homographies and the
    # disparity bounds.
    # ATTENTION if subsampling_factor is > 1 the rectified images will be
    # smaller, and the homography matrices and disparity range will reflect
    # this fact
    np.savetxt(subsampling, np.array([z]))
    np.savetxt(H_ref, H1)
    np.savetxt(H_sec, H2)
    np.savetxt(disp_min_max, np.array([disp_min, disp_max]))

    # save json file with all the parameters needed to reproduce this tile
    tile_cfg = copy.deepcopy(cfg)
    tile_cfg['roi'] = {'x': x, 'y': y, 'w': w, 'h': h}
    f = open(config, 'w')
    json.dump(tile_cfg, f, indent=2)
    f.close()

    # close logs
    common.garbage_cleanup()
    if not cfg['debug']:
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__
        fout.close()

    return
Exemple #8
0
def process_pair(out_dir, img1, rpc1, img2, rpc2, x, y, w, h, tw=None, th=None,
                 ov=None, cld_msk=None, roi_msk=None):
    """
    Computes a height map from a Pair of pushbroom images, using tiles.

    Args:
        out_dir: path to the output directory
        img1: path to the reference image.
        rpc1: paths to the xml file containing the rpc coefficients of the
            reference image
        img2: path to the secondary image.
        rpc2: paths to the xml file containing the rpc coefficients of the
            secondary image
        x, y, w, h: four integers defining the rectangular ROI in the reference
            image. (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle. The ROI may be as big as you want, as it will be
            cutted into small tiles for processing.
        tw, th: dimensions of the tiles
        ov: width of overlapping bands between tiles
        cld_msk (optional): path to a gml file containing a cloud mask
        roi_msk (optional): path to a gml file containing a mask defining the
            area contained in the full image.

    Returns:
        path to height map tif file
    """
    # create a directory for the experiment
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)

    # duplicate stdout and stderr to log file
    tee.Tee('%s/stdout.log' % out_dir, 'w')

    # ensure that the coordinates of the ROI are multiples of the zoom factor,
    # to avoid bad registration of tiles due to rounding problems.
    z = cfg['subsampling_factor']
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)

    # TODO: automatically compute optimal size for tiles
    if tw is None and th is None and ov is None:
        ov = z * 100
        if w <= z * cfg['tile_size']:
            tw = w
        else:
            tw = z * cfg['tile_size']
        if h <= z * cfg['tile_size']:
            th = h
        else:
            th = z * cfg['tile_size']
    ntx = np.ceil(float(w - ov) / (tw - ov))
    nty = np.ceil(float(h - ov) / (th - ov))
    nt = ntx * nty

    print 'tiles size: (%d, %d)' % (tw, th)
    print 'total number of tiles: %d (%d x %d)' % (nt, ntx, nty)

    # create pool with less workers than available cores
    nb_workers = multiprocessing.cpu_count()
    if cfg['max_nb_threads']:
        nb_workers = min(nb_workers, cfg['max_nb_threads'])
    pool = multiprocessing.Pool(nb_workers)

    # process the tiles
    # don't parallellize if in debug mode
    tiles = []
    results = []
    show_progress.counter = 0
    print 'Computing disparity maps tile by tile...'
    try:
        for row in np.arange(y, y + h - ov, th - ov):
            for col in np.arange(x, x + w - ov, tw - ov):
                tile_dir = '%s/tile_%06d_%06d_%04d_%04d' % (out_dir, col, row,
                                                            tw, th)
                # check if the tile is already done, or masked
                if os.path.isfile('%s/rectified_disp.tif' % tile_dir):
                    if cfg['skip_existing']:
                        print "stereo on tile %d %d already done, skip" % (col,
                                                                           row)
                        tiles.append(tile_dir)
                        continue
                if os.path.isfile('%s/this_tile_is_masked.txt' % tile_dir):
                    print "tile %d %d already masked, skip" % (col, row)
                    tiles.append(tile_dir)
                    continue

                # process the tile
                if cfg['debug']:
                    process_pair_single_tile(tile_dir, img1, rpc1, img2, rpc2,
                                             col, row, tw, th, None, cld_msk,
                                             roi_msk)
                else:
                    p = pool.apply_async(process_pair_single_tile,
                                         args=(tile_dir, img1, rpc1, img2, rpc2,
                                               col, row, tw, th, None, cld_msk,
                                               roi_msk), callback=show_progress)
                    results.append(p)
                tiles.append(tile_dir)

        for r in results:
            try:
                r.get(3600)  # wait at most one hour per tile
            except multiprocessing.TimeoutError:
                print "Timeout while computing tile "+str(r)

    except KeyboardInterrupt:
        pool.terminate()
        sys.exit(1)

    except common.RunFailure as e:
        print "FAILED call: ", e.args[0]["command"]
        print "output: ", e.args[0]["output"]


    # compute global pointing correction
    print 'Computing global pointing correction...'
    A_global = pointing_accuracy.global_from_local(tiles)
    np.savetxt('%s/pointing.txt' % out_dir, A_global)

    # Check if all tiles were computed
    # The only cause of a tile failure is a lack of sift matches, which breaks
    # the pointing correction step. Thus it is enough to check if the pointing
    # correction matrix was computed.
    results = []
    for i, row in enumerate(np.arange(y, y + h - ov, th - ov)):
        for j, col in enumerate(np.arange(x, x + w - ov, tw - ov)):
            tile_dir = '%s/tile_%06d_%06d_%04d_%04d' % (out_dir, col, row, tw,
                                                        th)
            if not os.path.isfile('%s/this_tile_is_masked.txt' % tile_dir):
                if not os.path.isfile('%s/pointing.txt' % tile_dir):
                    print "%s retrying pointing corr..." % tile_dir
                    # estimate pointing correction matrix from neighbors, if it
                    # fails use A_global, then rerun the disparity map
                    # computation
                    A = pointing_accuracy.from_next_tiles(tiles, ntx, nty, j, i)
                    if A is None:
                        A = A_global
                    if cfg['debug']:
                        process_pair_single_tile(tile_dir, img1, rpc1, img2,
                                                 rpc2, col, row, tw, th, None,
                                                 cld_msk, roi_msk, A)
                    else:
                        p = pool.apply_async(process_pair_single_tile,
                                             args=(tile_dir, img1, rpc1, img2,
                                                   rpc2, col, row, tw, th, None,
                                                   cld_msk, roi_msk, A),
                                             callback=show_progress)
                        results.append(p)

    try:
        for r in results:
            try:
                r.get(3600)  # wait at most one hour per tile
            except multiprocessing.TimeoutError:
                print "Timeout while computing tile "+str(r)  

    except KeyboardInterrupt:
        pool.terminate()
        sys.exit(1)

    except common.RunFailure as e:
        print "FAILED call: ", e.args[0]["command"]
        print "output: ", e.args[0]["output"]


    # triangulation
    processes = []
    results = []
    show_progress.counter = 0
    print 'Computing height maps tile by tile...'
    try:
        for row in np.arange(y, y + h - ov, th - ov):
            for col in np.arange(x, x + w - ov, tw - ov):
                tile = '%s/tile_%06d_%06d_%04d_%04d' % (out_dir, col, row, tw, th)
                H1 = '%s/H_ref.txt' % tile
                H2 = '%s/H_sec.txt' % tile
                disp = '%s/rectified_disp.tif' % tile
                mask = '%s/rectified_mask.png' % tile
                rpc_err = '%s/rpc_err.tif' % tile
                height_map = '%s/height_map.tif' % tile

                # check if the tile is already done, or masked
                if os.path.isfile(height_map):
                    if cfg['skip_existing']:
                        print "triangulation on tile %d %d is done, skip" % (col,
                                                                             row)
                        continue
                if os.path.isfile('%s/this_tile_is_masked.txt' % tile):
                    print "tile %d %d already masked, skip" % (col, row)
                    continue

                # process the tile
                if cfg['debug']:
                    triangulation.compute_dem(height_map, col, row, tw, th, z,
                                              rpc1, rpc2, H1, H2, disp, mask,
                                              rpc_err, A_global)
                else:
                    p = pool.apply_async(triangulation.compute_dem,
                                         args=(height_map, col, row, tw, th, z,
                                               rpc1, rpc2, H1, H2, disp, mask,
                                               rpc_err, A_global),
                                         callback=show_progress)
                    processes.append(p)
        for p in processes:
            try:
                results.append(p.get(3600))  # wait at most one hour per tile
            except multiprocessing.TimeoutError:
                print "Timeout while computing tile "+str(r)

    except KeyboardInterrupt:
        pool.terminate()
        sys.exit(1)

    # tiles composition
    out = '%s/height_map.tif' % out_dir
    tmp = ['%s/height_map.tif' % t for t in tiles]
    if not os.path.isfile(out) or not cfg['skip_existing']:
        print "Mosaicing tiles with %s..." % cfg['mosaic_method']
        if cfg['mosaic_method'] == 'gdal':
            tile_composer.mosaic_gdal(out, w/z, h/z, tmp, tw/z, th/z, ov/z)
        else:
            tile_composer.mosaic(out, w/z, h/z, tmp, tw/z, th/z, ov/z)
    common.garbage_cleanup()

    return out
Exemple #9
0
def init_tiles_full_info(config_file):
    """
    Prepare the entire process.

    1) Make sure coordinates of the ROI are multiples of the zoom factor
    2) Compute optimal size for tiles, get the number of pairs
    3) Build tiles_full_info: a list of dictionaries, one per tile, providing all you need to process a tile
       * col/row : position of the tile (upper left corner)
       * tw/th : size of the tile
       * ov : size of the overlapping
       * i/j : relative position of the tile
       * pos : position inside the ROI : UL for a tile place at th Upper Left corner, M for the ones placed in the middle, and so forth.
       * x/y/w/h : information about the ROI
       * images : a dictionary directly given by the json config file, that store the information about all the involved images, their rpc, and so forth.
       * nb_pairs : number of pairs
       * cld_msk/roi_msk : path to a gml file containing a cloud mask/ defining the area contained in the full image

    Args:
         config_file: path to a json configuration file

    Returns:
        tiles_full_info: list containing dictionaries
    """
    # ensure that the coordinates of the ROI are multiples of the zoom factor,
    # to avoid bad registration of tiles due to rounding problems.
    x = cfg['roi']['x']
    y = cfg['roi']['y']
    w = cfg['roi']['w']
    h = cfg['roi']['h']
    z = cfg['subsampling_factor']
    x, y, w, h = common.round_roi_to_nearest_multiple(z, x, y, w, h)
    cfg['roi']['x'] = x
    cfg['roi']['y'] = y
    cfg['roi']['w'] = w
    cfg['roi']['h'] = h

    # Automatically compute optimal size for tiles
    # tw, th : dimensions of the tiles
    # ov : width of overlapping bands between tiles
    ov = z * 100
    if w <= z * cfg['tile_size']:
        tw = w
    else:
        tw = z * cfg['tile_size']
    if h <= z * cfg['tile_size']:
        th = h
    else:
        th = z * cfg['tile_size']

    ntx = np.ceil(float(w - ov) / (tw - ov))
    nty = np.ceil(float(h - ov) / (th - ov))
    nt = ntx * nty

    print 'tiles size: (%d, %d)' % (tw, th)
    print 'total number of tiles: %d (%d x %d)' % (nt, ntx, nty)
    nb_pairs = len(cfg['images']) - 1
    print 'total number of pairs: %d' % nb_pairs

    # build tile_info dictionaries and store them in a list
    tiles_full_info = list()
    range_y = np.arange(y, y + h - ov, th - ov)
    range_x = np.arange(x, x + w - ov, tw - ov)
    rowmin, rowmax = range_y[0], range_y[-1]
    colmin, colmax = range_x[0], range_x[-1]

    for i, row in enumerate(range_y):
        for j, col in enumerate(range_x):
            # ensure that tile coordinates are multiples of the zoom factor
            col, row, tw, th = common.round_roi_to_nearest_multiple(z, col, row,
                                                                    tw, th)
            tile_dir = os.path.join(cfg['out_dir'], 'tile_%d_%d_row_%d' % (tw,
                                                                           th,
                                                                           row),
                                    'col_%d' % col)

            if row == rowmin and col == colmin:
                pos = 'UL'
            elif row == rowmin and col == colmax:
                pos = 'UR'
            elif row == rowmax and col == colmax:
                pos = 'BR'
            elif row == rowmax and col == colmin:
                pos = 'BL'
            elif row == rowmin and col > colmin:
                pos = 'U'
            elif col == colmin and row > rowmin:
                pos = 'L'
            elif row == rowmax and col > colmin:
                pos = 'B'
            elif col == colmax and row > rowmin:
                pos = 'R'
            else:
                pos = 'M'

            tile_info = {}
            tile_info['directory'] = tile_dir
            tile_info['coordinates'] = (col, row, tw, th)
            tile_info['index_in_roi'] = (i, j)
            tile_info['position_type'] = pos
            tile_info['roi_coordinates'] = (x, y, w, h)
            tile_info['overlap'] = ov
            tile_info['number_of_pairs'] = nb_pairs
            tile_info['images'] = cfg['images']
            tiles_full_info.append(tile_info)

    if len(tiles_full_info) == 1:
        tiles_full_info[0]['position_type'] = 'Single'

    return tiles_full_info