def pointing_correction(tile, i): """ Compute the translation that corrects the pointing error on a pair of tiles. Args: tile: dictionary containing the information needed to process the tile i: index of the processed pair """ x, y, w, h = tile['coordinates'] out_dir = os.path.join(tile['dir'], 'pair_{}'.format(i)) img1 = cfg['images'][0]['img'] rpc1 = cfg['images'][0]['rpcm'] img2 = cfg['images'][i]['img'] rpc2 = cfg['images'][i]['rpcm'] # correct pointing error print('correcting pointing on tile {} {} pair {}...'.format(x, y, i)) method = 'relative' if cfg[ 'relative_sift_match_thresh'] is True else 'absolute' A, m = pointing_accuracy.compute_correction(img1, img2, rpc1, rpc2, x, y, w, h, method, cfg['sift_match_thresh'], cfg['max_pointing_error']) if A is not None: # A is the correction matrix np.savetxt(os.path.join(out_dir, 'pointing.txt'), A, fmt='%6.3f') if m is not None: # m is the list of sift matches np.savetxt(os.path.join(out_dir, 'sift_matches.txt'), m, fmt='%9.3f') np.savetxt(os.path.join(out_dir, 'center_keypts_sec.txt'), np.mean(m[:, 2:], 0), fmt='%9.3f') if cfg['debug']: visualisation.plot_matches( img1, img2, rpc1, rpc2, m, os.path.join(out_dir, 'sift_matches_pointing.png'), x, y, w, h)
def pointing_correction(tile, i): """ Compute the translation that corrects the pointing error on a pair of tiles. Args: tile: dictionary containing the information needed to process the tile i: index of the processed pair """ x, y, w, h = tile['coordinates'] out_dir = os.path.join(tile['dir'], 'pair_{}'.format(i)) img1 = cfg['images'][0]['img'] rpc1 = cfg['images'][0]['rpc'] img2 = cfg['images'][i]['img'] rpc2 = cfg['images'][i]['rpc'] # correct pointing error print('correcting pointing on tile {} {} pair {}...'.format(x, y, i)) try: A, m = pointing_accuracy.compute_correction(img1, rpc1, img2, rpc2, x, y, w, h) except common.RunFailure as e: stderr = os.path.join(out_dir, 'stderr.log') with open(stderr, 'w') as f: f.write('ERROR during pointing correction with cmd: %s\n' % e[0]['command']) f.write('Stop processing this pair\n') return if A is not None: # A is the correction matrix np.savetxt(os.path.join(out_dir, 'pointing.txt'), A, fmt='%6.3f') if m is not None: # m is the list of sift matches np.savetxt(os.path.join(out_dir, 'sift_matches.txt'), m, fmt='%9.3f') np.savetxt(os.path.join(out_dir, 'center_keypts_sec.txt'), np.mean(m[:, 2:], 0), fmt='%9.3f') if cfg['debug']: visualisation.plot_matches( img1, img2, rpc1, rpc2, m, x, y, w, h, os.path.join(out_dir, 'sift_matches_pointing.png'))
def rectify_pair(im1, im2, rpc1, rpc2, x, y, w, h, out1, out2, A=None, sift_matches=None, method='rpc', hmargin=0, vmargin=0): """ Rectify a ROI in a pair of images. Args: im1, im2: paths to two GeoTIFF image files rpc1, rpc2: two instances of the rpcm.RPCModel class 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. {h,v}margin (optional): horizontal and vertical margins added on the sides of the rectified images 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 """ # 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:]) elif method == 'sift': matches = sift_matches else: raise Exception( "Unknown value {} for argument 'method'".format(method)) if matches is None or len(matches) < 4: raise NoRectificationMatchesError( "No or not enough matches found to rectify image pair") # 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.projection(lon, lat, alt)[:2] x2, y2 = rpc2.projection(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) < 1: warnings.warn( "Need at least one sift match for the horizontal registration", category=NoHorizontalRegistrationWarning, ) else: H2 = register_horizontally_translation(sift_matches, H1, H2) # compute disparity range if cfg['debug']: out_dir = os.path.dirname(out1) np.savetxt(os.path.join(out_dir, 'sift_matches_disp.txt'), sift_matches, fmt='%9.3f') visualisation.plot_matches( im1, im2, rpc1, rpc2, sift_matches, x, y, w, h, os.path.join(out_dir, 'sift_matches_disp.png')) disp_m, disp_M = disparity_range(rpc1, rpc2, x, y, w, h, H1, H2, sift_matches, A) # recompute hmargin and homographies hmargin = int(np.ceil(max([hmargin, np.fabs(disp_m), np.fabs(disp_M)]))) T = common.matrix_translation(hmargin, vmargin) H1, H2 = np.dot(T, H1), np.dot(T, H2) # 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]), [hmargin, vmargin], atol=.01) # apply homographies and do the crops common.image_apply_homography(out1, im1, H1, w0 + 2 * hmargin, h0 + 2 * vmargin) common.image_apply_homography(out2, im2, H2, w0 + 2 * hmargin, h0 + 2 * vmargin) return H1, H2, disp_m, disp_M
def rectify_pair(im1, im2, rpc1, rpc2, x, y, w, h, out1, out2, A=None, sift_matches=None, method='rpc', hmargin=0, vmargin=0): """ 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. {h,v}margin (optional): horizontal and vertical margins added on the sides of the rectified images 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 if cfg['debug']: out_dir = os.path.dirname(out1) np.savetxt(os.path.join(out_dir, 'sift_matches_disp.txt'), sift_matches, fmt='%9.3f') visualisation.plot_matches( im1, im2, rpc1, rpc2, sift_matches, x, y, w, h, os.path.join(out_dir, 'sift_matches_disp.png')) disp_m, disp_M = disparity_range(rpc1, rpc2, x, y, w, h, H1, H2, sift_matches, A) # recompute hmargin and homographies hmargin = int(np.ceil(max([hmargin, np.fabs(disp_m), np.fabs(disp_M)]))) T = common.matrix_translation(hmargin, vmargin) H1, H2 = np.dot(T, H1), np.dot(T, H2) # compute rectifying homographies for non-epipolar mode (rectify the secondary tile only) if block_matching.rectify_secondary_tile_only(cfg['matching_algorithm']): H1_inv = np.linalg.inv(H1) H1 = np.eye( 3 ) # H1 is replaced by 2-D array with ones on the diagonal and zeros elsewhere H2 = np.dot(H1_inv, H2) T = common.matrix_translation(-x + hmargin, -y + vmargin) H1 = np.dot(T, H1) H2 = np.dot(T, H2) # 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]), [hmargin, vmargin], atol=.01) # apply homographies and do the crops common.image_apply_homography(out1, im1, H1, w0 + 2 * hmargin, h0 + 2 * vmargin) common.image_apply_homography(out2, im2, H2, w0 + 2 * hmargin, h0 + 2 * vmargin) if block_matching.rectify_secondary_tile_only(cfg['matching_algorithm']): pts_in = [[0, 0], [disp_m, 0], [disp_M, 0]] pts_out = common.points_apply_homography(H1_inv, pts_in) disp_m = pts_out[1, :] - pts_out[0, :] disp_M = pts_out[2, :] - pts_out[0, :] return H1, H2, disp_m, disp_M