示例#1
0
def rectify_pair(im1,
                 im2,
                 rpc1,
                 rpc2,
                 x,
                 y,
                 w,
                 h,
                 out1,
                 out2,
                 A=None,
                 m=None,
                 flag='rpc'):
    """
    Rectify a ROI in a pair of Pleiades images.

    Args:
        im1, im2: paths to the two Pleiades images (usually jp2 or tif)
        rpc1, rpc2: paths to the two xml files containing RPC data
        x, y, w, h: four integers defining the rectangular ROI in the first
            image.  (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle.
        out1, out2: paths to the output crops
        A (optional): 3x3 numpy array containing the pointing error correction
            for im2. This matrix is usually estimated with the pointing_accuracy
            module.
        m (optional): Nx4 numpy array containing a list of sift matches, in the
            full image coordinates frame
        flag (default: 'rpc'): option to decide wether to use rpc of sift
            matches for the fundamental matrix estimation.

        This function uses the parameter subsampling_factor from the
        config module.  If the factor z > 1 then the output images will
        be subsampled by a factor z.  The output matrices H1, H2, and the
        ranges are also updated accordingly:
        Hi = Z*Hi   with Z = diag(1/z,1/z,1)   and
        disp_min = disp_min/z  (resp _max)

    Returns:
        H1, H2: Two 3x3 matrices representing the rectifying homographies that
            have been applied to the two (big) images.
        disp_min, disp_max: horizontal disparity range
    """
    # read RPC data
    rpc1 = rpc_model.RPCModel(rpc1)
    rpc2 = rpc_model.RPCModel(rpc2)

    # compute rectifying homographies
    if flag == 'rpc':
        H1, H2, disp_min, disp_max = compute_rectification_homographies(
            im1, im2, rpc1, rpc2, x, y, w, h, A, m)
    else:
        H1, H2, disp_min, disp_max = compute_rectification_homographies_sift(
            im1, im2, rpc1, rpc2, x, y, w, h)

    # compute output images size
    roi = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]
    pts1 = common.points_apply_homography(H1, roi)
    x0, y0, w0, h0 = common.bounding_box2D(pts1)
    # check that the first homography maps the ROI in the positive quadrant
    np.testing.assert_allclose(np.round([x0, y0]), 0, atol=.01)

    # apply homographies and do the crops TODO XXX FIXME cleanup here
    #homography_cropper.crop_and_apply_homography(out1, im1, H1, w0, h0, cfg['subsampling_factor'], True)
    #homography_cropper.crop_and_apply_homography(out2, im2, H2, w0, h0, cfg['subsampling_factor'], True)
    common.image_apply_homography(out1, im1, H1, w0, h0)
    common.image_apply_homography(out2, im2, H2, w0, h0)

    #  If subsampling_factor'] the homographies are altered to reflect the zoom
    if cfg['subsampling_factor'] != 1:
        from math import floor, ceil
        # update the H1 and H2 to reflect the zoom
        Z = np.eye(3)
        Z[0, 0] = Z[1, 1] = 1.0 / cfg['subsampling_factor']

        H1 = np.dot(Z, H1)
        H2 = np.dot(Z, H2)
        disp_min = floor(disp_min / cfg['subsampling_factor'])
        disp_max = ceil(disp_max / cfg['subsampling_factor'])

    return H1, H2, disp_min, disp_max
示例#2
0
def cloud_water_image_domain(out,
                             w,
                             h,
                             H,
                             rpc,
                             roi_gml=None,
                             cld_gml=None,
                             wat_msk=None):
    """
    Computes a mask for pixels masked by clouds, water, or out of image domain.

    Args:
        out: path to the output image file.
        w, h: (w, h) are the dimensions of the output image mask.
        H: 3x3 numpy array representing the homography that transforms the
            original full image into the rectified tile.
        rpc: paths to the xml file containing the rpc coefficients of the image.
            RPC model is used with SRTM data to derive the water mask.
        roi_gml (optional, default None): path to a gml file containing a mask
            defining the area contained in the full image
        cld_gml (optional, default None): path to a gml file containing a mask
            defining the areas covered by clouds
        wat_msk (optional): path to a tiff file containing a water mask.

    Returns:
        True if the tile is completely masked, False otherwise.
    """
    if (os.path.isfile(out) and cfg['skip_existing']):
        print 'Mask %s already exists, skip.' % out

    # put the coefficients of the homography in a string
    hij = ' '.join(['%f' % x for x in H.flatten()])

    # image domain mask
    if roi_gml is None:  # initialize to 255
        common.run('plambda zero:%dx%d "x 255 +" -o %s' % (w, h, out))
    else:
        common.run('cldmask %d %d -h "%s" %s %s' % (w, h, hij, roi_gml, out))
        if common.is_image_black(out):  # if we are already out, return
            return True

    # cloud mask
    if cld_gml is not None:
        cld_msk = common.tmpfile('.png')
        common.run('cldmask %d %d -h "%s" %s %s' %
                   (w, h, hij, cld_gml, cld_msk))
        # cld msk has to be inverted.
        # TODO: add flag to the cldmask binary, to avoid using read/write the
        # msk one more time for this
        common.run('plambda %s "255 x -" -o %s' % (cld_msk, cld_msk))

        intersection(out, out, cld_msk)

    if wat_msk is not None:
        # water mask (tiff)
        wat_msk_crop = common.tmpfile('.png')
        common.image_apply_homography(wat_msk_crop, wat_msk, H, w, h)
        intersection(out, out, wat_msk_crop)
    else:
        # water mask (srtm)
        water_msk = common.tmpfile('.png')
        env = os.environ.copy()
        env['SRTM4_CACHE'] = cfg['srtm_dir']
        common.run(
            'watermask %d %d -h "%s" %s %s' % (w, h, hij, rpc, water_msk), env)
        intersection(out, out, water_msk)

    return common.is_image_black(out)
示例#3
0
文件: masking.py 项目: cpalmann/s2p
def cloud_water_image_domain(out, w, h, H, rpc, roi_gml=None, cld_gml=None, wat_msk=None):
    """
    Computes a mask for pixels masked by clouds, water, or out of image domain.

    Args:
        out: path to the output image file.
        w, h: (w, h) are the dimensions of the output image mask.
        H: 3x3 numpy array representing the homography that transforms the
            original full image into the rectified tile.
        rpc: paths to the xml file containing the rpc coefficients of the image.
            RPC model is used with SRTM data to derive the water mask.
        roi_gml (optional, default None): path to a gml file containing a mask
            defining the area contained in the full image
        cld_gml (optional, default None): path to a gml file containing a mask
            defining the areas covered by clouds
        wat_msk (optional): path to a tiff file containing a water mask.

    Returns:
        True if the tile is completely masked, False otherwise.
    """
    if (os.path.isfile(out) and cfg['skip_existing']):
        print 'Mask %s already exists, skip.' % out

    # put the coefficients of the homography in a string
    hij = ' '.join(['%f' % x for x in H.flatten()])

    # image domain mask
    if roi_gml is None:  # initialize to 255
        common.run('plambda zero:%dx%d "x 255 +" -o %s' % (w, h, out))
    else:
        common.run('cldmask %d %d -h "%s" %s %s' % (w, h, hij, roi_gml, out))
        if common.is_image_black(out):  # if we are already out, return
            return True

    # cloud mask
    if cld_gml is not None:
        cld_msk = common.tmpfile('.png')
        common.run('cldmask %d %d -h "%s" %s %s' % (w, h, hij, cld_gml,
                                                    cld_msk))
        # cld msk has to be inverted.
        # TODO: add flag to the cldmask binary, to avoid using read/write the
        # msk one more time for this
        common.run('plambda %s "255 x -" -o %s' % (cld_msk, cld_msk))

        intersection(out, out, cld_msk)

    if wat_msk is not None:
        # water mask (tiff)
        wat_msk_crop = common.tmpfile('.png')
        common.image_apply_homography(wat_msk_crop, wat_msk, H, w, h)
        intersection(out, out, wat_msk_crop)
    else:
        # water mask (srtm)
        water_msk = common.tmpfile('.png')
        env = os.environ.copy()
        env['SRTM4_CACHE'] = cfg['srtm_dir']
        common.run('watermask %d %d -h "%s" %s %s' % (w, h, hij, rpc, water_msk),
                   env)
        intersection(out, out, water_msk)

    return common.is_image_black(out)
示例#4
0
def rectify_pair(im1, im2, rpc1, rpc2, x, y, w, h, out1, out2, A=None, sift_matches=None, method="rpc"):
    """
    Rectify a ROI in a pair of images.

    Args:
        im1, im2: paths to two image files
        rpc1, rpc2: paths to the two xml files containing RPC data
        x, y, w, h: four integers defining the rectangular ROI in the first
            image.  (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle.
        out1, out2: paths to the output rectified crops
        A (optional): 3x3 numpy array containing the pointing error correction
            for im2. This matrix is usually estimated with the pointing_accuracy
            module.
        sift_matches (optional): Nx4 numpy array containing a list of sift
            matches, in the full image coordinates frame
        method (default: 'rpc'): option to decide wether to use rpc of sift
            matches for the fundamental matrix estimation.

        This function uses the parameter subsampling_factor from the
        config module. If the factor z > 1 then the output images will
        be subsampled by a factor z. The output matrices H1, H2, and the
        ranges are also updated accordingly:
        Hi = Z * Hi with Z = diag(1/z, 1/z, 1) and
        disp_min = disp_min / z  (resp _max)

    Returns:
        H1, H2: Two 3x3 matrices representing the rectifying homographies that
        have been applied to the two original (large) images.
        disp_min, disp_max: horizontal disparity range
    """
    # read RPC data
    rpc1 = rpc_model.RPCModel(rpc1)
    rpc2 = rpc_model.RPCModel(rpc2)

    # compute real or virtual matches
    if method == "rpc":
        # find virtual matches from RPC camera models
        matches = rpc_utils.matches_from_rpc(rpc1, rpc2, x, y, w, h, cfg["n_gcp_per_axis"])

        # correct second image coordinates with the pointing correction matrix
        if A is not None:
            matches[:, 2:] = common.points_apply_homography(np.linalg.inv(A), matches[:, 2:])
    else:
        matches = sift_matches

    # compute rectifying homographies
    H1, H2, F = rectification_homographies(matches, x, y, w, h)

    if cfg["register_with_shear"]:
        # compose H2 with a horizontal shear to reduce the disparity range
        a = np.mean(rpc_utils.altitude_range(rpc1, x, y, w, h))
        lon, lat, alt = rpc_utils.ground_control_points(rpc1, x, y, w, h, a, a, 4)
        x1, y1 = rpc1.inverse_estimate(lon, lat, alt)[:2]
        x2, y2 = rpc2.inverse_estimate(lon, lat, alt)[:2]
        m = np.vstack([x1, y1, x2, y2]).T
        m = np.vstack({tuple(row) for row in m})  # remove duplicates due to no alt range
        H2 = register_horizontally_shear(m, H1, H2)

    # compose H2 with a horizontal translation to center disp range around 0
    if sift_matches is not None:
        sift_matches = filter_matches_epipolar_constraint(F, sift_matches, cfg["epipolar_thresh"])
        if len(sift_matches) < 10:
            print "WARNING: no registration with less than 10 matches"
        else:
            H2 = register_horizontally_translation(sift_matches, H1, H2)

    # compute disparity range
    disp_m, disp_M = disparity_range(rpc1, rpc2, x, y, w, h, H1, H2, sift_matches, A)

    # compute output images size
    roi = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]
    pts1 = common.points_apply_homography(H1, roi)
    x0, y0, w0, h0 = common.bounding_box2D(pts1)
    # check that the first homography maps the ROI in the positive quadrant
    np.testing.assert_allclose(np.round([x0, y0]), 0, atol=0.01)

    # apply homographies and do the crops TODO XXX FIXME cleanup here
    # homography_cropper.crop_and_apply_homography(out1, im1, H1, w0, h0, cfg['subsampling_factor'], True)
    # homography_cropper.crop_and_apply_homography(out2, im2, H2, w0, h0, cfg['subsampling_factor'], True)
    common.image_apply_homography(out1, im1, H1, w0, h0)
    common.image_apply_homography(out2, im2, H2, w0, h0)

    #  if subsampling_factor'] the homographies are altered to reflect the zoom
    if cfg["subsampling_factor"] != 1:
        Z = np.eye(3)
        Z[0, 0] = Z[1, 1] = 1.0 / cfg["subsampling_factor"]

        H1 = np.dot(Z, H1)
        H2 = np.dot(Z, H2)
        disp_m = np.floor(disp_m / cfg["subsampling_factor"])
        disp_M = np.ceil(disp_M / cfg["subsampling_factor"])

    return H1, H2, disp_m, disp_M
示例#5
0
def crop_and_apply_homography(im_out, im_in, H, w, h, subsampling_factor=1,
        convert_to_gray=False):
    """
    Warps a piece of a Pleiades (panchro or ms) image with a homography.

    Args:
        im_out: path to the output image
        im_in: path to the input (tif) full Pleiades image
        H: numpy array containing the 3x3 homography matrix
        w, h: size of the output image
        subsampling_factor (optional, default=1): when set to z>1,
            will result in the application of the homography Z*H where Z =
            diag(1/z, 1/z, 1), so the output will be zoomed out by a factor z.
            The output image will be (w/z, h/z)
        convert_to_gray (optional, default False): it set to True, and if the
            input image has 4 channels, it is converted to gray before applying
            zoom and homographies.

    Returns:
        nothing

    The homography has to be used as: coord_out = H coord_in. The produced
    output image corresponds to coord_out in [0, w] x [0, h]. The warp is made
    by Pascal Monasse's binary named 'homography'.
    """

    # crop a piece of the big input image, to which the homography will be
    # applied
    # warning: as the crop uses integer coordinates, be careful to round off
    # (x0, y0) before modifying the homograpy. You want the crop and the
    # translation representing it do exactly the same thing.
    pts = [[0, 0], [w, 0], [w, h], [0, h]]
    inv_H_pts = common.points_apply_homography(np.linalg.inv(H), pts)
    x0, y0, w0, h0 = common.bounding_box2D(inv_H_pts)
    x0, y0 = np.floor([x0, y0])
    w0, h0 = np.ceil([w0, h0])
    crop_fullres = common.image_crop_LARGE(im_in, x0, y0, w0, h0)

    # This filter is needed (for panchro images) because the original PLEAIDES
    # SENSOR PERFECT images are aliased
    if (common.image_pix_dim(crop_fullres) == 1 and subsampling_factor == 1 and
            cfg['use_pleiades_unsharpening']):
        tmp = image_apply_pleiades_unsharpening_filter(crop_fullres)
        common.run('rm -f %s' % crop_fullres)
        crop_fullres = tmp

    # convert to gray
    if common.image_pix_dim(crop_fullres) == 4:
        if convert_to_gray:
            crop_fullres = common.pansharpened_to_panchro(crop_fullres)

    # compensate the homography with the translation induced by the preliminary
    # crop, then apply the homography and crop.
    H = np.dot(H, common.matrix_translation(x0, y0))

    # Since the objective is to compute a zoomed out homographic transformation
    # of the input image, to save computations we zoom out the image before
    # applying the homography. If Z is the matrix representing the zoom out and
    # H the homography matrix, this trick consists in applying Z*H*Z^{-1} to
    # the zoomed image Z*Im instead of applying Z*H to the original image Im.
    if subsampling_factor == 1:
        common.image_apply_homography(im_out, crop_fullres, H, w, h)
        return

    else:
        assert(subsampling_factor >= 1)

        # H becomes Z*H*Z^{-1}
        Z = np.eye(3);
        Z[0,0] = Z[1,1] = 1 / float(subsampling_factor)
        H = np.dot(Z, H)
        H = np.dot(H, np.linalg.inv(Z))

        # w, and h are updated accordingly
        w = int(w / subsampling_factor)
        h = int(h / subsampling_factor)

        # the DCT zoom is NOT SAFE when the input image size is not a multiple
        # of the zoom factor
        tmpw, tmph = common.image_size(crop_fullres)
        tmpw, tmph = int(tmpw / subsampling_factor), int(tmph / subsampling_factor)
        crop_fullres_safe = common.image_crop_tif(crop_fullres, 0, 0, tmpw *
                subsampling_factor, tmph * subsampling_factor)
        common.run('rm -f %s' % crop_fullres)

        # zoom out the input image (crop_fullres)
        crop_zoom_out = common.image_safe_zoom_fft(crop_fullres_safe,
                subsampling_factor)
        common.run('rm -f %s' % crop_fullres_safe)

        # apply the homography to the zoomed out crop
        common.image_apply_homography(im_out, crop_zoom_out, H, w, h)
        return
示例#6
0
def crop_and_apply_homography(im_out,
                              im_in,
                              H,
                              w,
                              h,
                              subsampling_factor=1,
                              convert_to_gray=False):
    """
    Warps a piece of a Pleiades (panchro or ms) image with a homography.

    Args:
        im_out: path to the output image
        im_in: path to the input (tif) full Pleiades image
        H: numpy array containing the 3x3 homography matrix
        w, h: size of the output image
        subsampling_factor (optional, default=1): when set to z>1,
            will result in the application of the homography Z*H where Z =
            diag(1/z, 1/z, 1), so the output will be zoomed out by a factor z.
            The output image will be (w/z, h/z)
        convert_to_gray (optional, default False): it set to True, and if the
            input image has 4 channels, it is converted to gray before applying
            zoom and homographies.

    Returns:
        nothing

    The homography has to be used as: coord_out = H coord_in. The produced
    output image corresponds to coord_out in [0, w] x [0, h]. The warp is made
    by Pascal Monasse's binary named 'homography'.
    """

    # crop a piece of the big input image, to which the homography will be
    # applied
    # warning: as the crop uses integer coordinates, be careful to round off
    # (x0, y0) before modifying the homograpy. You want the crop and the
    # translation representing it do exactly the same thing.
    pts = [[0, 0], [w, 0], [w, h], [0, h]]
    inv_H_pts = common.points_apply_homography(np.linalg.inv(H), pts)
    x0, y0, w0, h0 = common.bounding_box2D(inv_H_pts)
    x0, y0 = np.floor([x0, y0])
    w0, h0 = np.ceil([w0, h0])
    crop_fullres = common.image_crop_LARGE(im_in, x0, y0, w0, h0)

    # This filter is needed (for panchro images) because the original PLEAIDES
    # SENSOR PERFECT images are aliased
    if (common.image_pix_dim(crop_fullres) == 1 and subsampling_factor == 1
            and cfg['use_pleiades_unsharpening']):
        tmp = image_apply_pleiades_unsharpening_filter(crop_fullres)
        common.run('rm -f %s' % crop_fullres)
        crop_fullres = tmp

    # convert to gray
    if common.image_pix_dim(crop_fullres) == 4:
        if convert_to_gray:
            crop_fullres = common.pansharpened_to_panchro(crop_fullres)

    # compensate the homography with the translation induced by the preliminary
    # crop, then apply the homography and crop.
    H = np.dot(H, common.matrix_translation(x0, y0))

    # Since the objective is to compute a zoomed out homographic transformation
    # of the input image, to save computations we zoom out the image before
    # applying the homography. If Z is the matrix representing the zoom out and
    # H the homography matrix, this trick consists in applying Z*H*Z^{-1} to
    # the zoomed image Z*Im instead of applying Z*H to the original image Im.
    if subsampling_factor == 1:
        common.image_apply_homography(im_out, crop_fullres, H, w, h)
        return

    else:
        assert (subsampling_factor >= 1)

        # H becomes Z*H*Z^{-1}
        Z = np.eye(3)
        Z[0, 0] = Z[1, 1] = 1 / float(subsampling_factor)
        H = np.dot(Z, H)
        H = np.dot(H, np.linalg.inv(Z))

        # w, and h are updated accordingly
        w = int(w / subsampling_factor)
        h = int(h / subsampling_factor)

        # the DCT zoom is NOT SAFE when the input image size is not a multiple
        # of the zoom factor
        tmpw, tmph = common.image_size(crop_fullres)
        tmpw, tmph = int(tmpw / subsampling_factor), int(tmph /
                                                         subsampling_factor)
        crop_fullres_safe = common.image_crop_tif(crop_fullres, 0, 0,
                                                  tmpw * subsampling_factor,
                                                  tmph * subsampling_factor)
        common.run('rm -f %s' % crop_fullres)

        # zoom out the input image (crop_fullres)
        crop_zoom_out = common.image_safe_zoom_fft(crop_fullres_safe,
                                                   subsampling_factor)
        common.run('rm -f %s' % crop_fullres_safe)

        # apply the homography to the zoomed out crop
        common.image_apply_homography(im_out, crop_zoom_out, H, w, h)
        return
示例#7
0
def rectify_pair(im1, im2, rpc1, rpc2, x, y, w, h, out1, out2, A=None, m=None,
                 flag='rpc'):
    """
    Rectify a ROI in a pair of Pleiades images.

    Args:
        im1, im2: paths to the two Pleiades images (usually jp2 or tif)
        rpc1, rpc2: paths to the two xml files containing RPC data
        x, y, w, h: four integers defining the rectangular ROI in the first
            image.  (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle.
        out1, out2: paths to the output crops
        A (optional): 3x3 numpy array containing the pointing error correction
            for im2. This matrix is usually estimated with the pointing_accuracy
            module.
        m (optional): Nx4 numpy array containing a list of sift matches, in the
            full image coordinates frame
        flag (default: 'rpc'): option to decide wether to use rpc of sift
            matches for the fundamental matrix estimation.

        This function uses the parameter subsampling_factor from the
        config module.  If the factor z > 1 then the output images will
        be subsampled by a factor z.  The output matrices H1, H2, and the
        ranges are also updated accordingly:
        Hi = Z*Hi   with Z = diag(1/z,1/z,1)   and
        disp_min = disp_min/z  (resp _max)

    Returns:
        H1, H2: Two 3x3 matrices representing the rectifying homographies that
            have been applied to the two (big) images.
        disp_min, disp_max: horizontal disparity range
    """
    # read RPC data
    rpc1 = rpc_model.RPCModel(rpc1)
    rpc2 = rpc_model.RPCModel(rpc2)

    # compute rectifying homographies
    if flag == 'rpc':
        H1, H2, disp_min, disp_max = compute_rectification_homographies(
            im1, im2, rpc1, rpc2, x, y, w, h, A, m)
    else:
        H1, H2, disp_min, disp_max = compute_rectification_homographies_sift(
            im1, im2, rpc1, rpc2, x, y, w, h)

    # compute output images size
    roi = [[x, y], [x+w, y], [x+w, y+h], [x, y+h]]
    pts1 = common.points_apply_homography(H1, roi)
    x0, y0, w0, h0 = common.bounding_box2D(pts1)
    # check that the first homography maps the ROI in the positive quadrant
    np.testing.assert_allclose(np.round([x0, y0]), 0, atol=.01)

    # apply homographies and do the crops TODO XXX FIXME cleanup here
    #homography_cropper.crop_and_apply_homography(out1, im1, H1, w0, h0, cfg['subsampling_factor'], True)
    #homography_cropper.crop_and_apply_homography(out2, im2, H2, w0, h0, cfg['subsampling_factor'], True)
    common.image_apply_homography(out1, im1, H1, w0, h0)
    common.image_apply_homography(out2, im2, H2, w0, h0)

    #  If subsampling_factor'] the homographies are altered to reflect the zoom
    if cfg['subsampling_factor'] != 1:
        from math import floor, ceil
        # update the H1 and H2 to reflect the zoom
        Z = np.eye(3)
        Z[0, 0] = Z[1, 1] = 1.0 / cfg['subsampling_factor']

        H1 = np.dot(Z, H1)
        H2 = np.dot(Z, H2)
        disp_min = floor(disp_min / cfg['subsampling_factor'])
        disp_max = ceil(disp_max / cfg['subsampling_factor'])

    return H1, H2, disp_min, disp_max
示例#8
0
def rectify_pair(im1, im2, rpc1, rpc2, x, y, w, h, out1, out2, A=None,
                 sift_matches=None, method='rpc'):
    """
    Rectify a ROI in a pair of images.

    Args:
        im1, im2: paths to two image files
        rpc1, rpc2: paths to the two xml files containing RPC data
        x, y, w, h: four integers defining the rectangular ROI in the first
            image.  (x, y) is the top-left corner, and (w, h) are the dimensions
            of the rectangle.
        out1, out2: paths to the output rectified crops
        A (optional): 3x3 numpy array containing the pointing error correction
            for im2. This matrix is usually estimated with the pointing_accuracy
            module.
        sift_matches (optional): Nx4 numpy array containing a list of sift
            matches, in the full image coordinates frame
        method (default: 'rpc'): option to decide wether to use rpc of sift
            matches for the fundamental matrix estimation.

        This function uses the parameter subsampling_factor from the
        config module. If the factor z > 1 then the output images will
        be subsampled by a factor z. The output matrices H1, H2, and the
        ranges are also updated accordingly:
        Hi = Z * Hi with Z = diag(1/z, 1/z, 1) and
        disp_min = disp_min / z  (resp _max)

    Returns:
        H1, H2: Two 3x3 matrices representing the rectifying homographies that
        have been applied to the two original (large) images.
        disp_min, disp_max: horizontal disparity range
    """
    # read RPC data
    rpc1 = rpc_model.RPCModel(rpc1)
    rpc2 = rpc_model.RPCModel(rpc2)

    # compute real or virtual matches
    if method == 'rpc':
        # find virtual matches from RPC camera models
        matches = rpc_utils.matches_from_rpc(rpc1, rpc2, x, y, w, h,
                                             cfg['n_gcp_per_axis'])

        # correct second image coordinates with the pointing correction matrix
        if A is not None:
            matches[:, 2:] = common.points_apply_homography(np.linalg.inv(A),
                                                            matches[:, 2:])
    else:
        matches = sift_matches

    # compute rectifying homographies
    H1, H2, F = rectification_homographies(matches, x, y, w, h)

    # compose H2 with a horizontal translation to center disp range around 0
    if sift_matches is not None:
        sift_matches = filter_matches_epipolar_constraint(F, sift_matches,
                                                          cfg['epipolar_thresh'])
        if len(sift_matches) < 10:
            print 'WARNING: no registration with less than 10 matches'
        else:
            H2 = register_horizontally(sift_matches, H1, H2)

    # compute disparity range
    disp_m, disp_M = disparity_range(rpc1, rpc2, x, y, w, h, H1, H2,
                                     sift_matches, A)

    # compute output images size
    roi = [[x, y], [x+w, y], [x+w, y+h], [x, y+h]]
    pts1 = common.points_apply_homography(H1, roi)
    x0, y0, w0, h0 = common.bounding_box2D(pts1)
    # check that the first homography maps the ROI in the positive quadrant
    np.testing.assert_allclose(np.round([x0, y0]), 0, atol=.01)

    # apply homographies and do the crops TODO XXX FIXME cleanup here
    #homography_cropper.crop_and_apply_homography(out1, im1, H1, w0, h0, cfg['subsampling_factor'], True)
    #homography_cropper.crop_and_apply_homography(out2, im2, H2, w0, h0, cfg['subsampling_factor'], True)
    common.image_apply_homography(out1, im1, H1, w0, h0)
    common.image_apply_homography(out2, im2, H2, w0, h0)

    #  if subsampling_factor'] the homographies are altered to reflect the zoom
    if cfg['subsampling_factor'] != 1:
        Z = np.eye(3)
        Z[0, 0] = Z[1, 1] = 1.0 / cfg['subsampling_factor']

        H1 = np.dot(Z, H1)
        H2 = np.dot(Z, H2)
        disp_m = np.floor(disp_m / cfg['subsampling_factor'])
        disp_M = np.ceil(disp_M / cfg['subsampling_factor'])

    return H1, H2, disp_m, disp_M