def rectify_aoi(file1, file2, aoi, z=None, correct_pointing=True, register_ground=True, debug=False): """ Args: file1, file2 (strings): file paths or urls of two satellite images aoi (geojson.Polygon): area of interest z (float, optional): base altitude with respect to WGS84 ellipsoid. If None, z is retrieved from srtm. Returns: rect1, rect2: numpy arrays with the images S1, S2: transformation matrices from the coordinate system of the original images disp_min, disp_max: horizontal disparity range P1, P2: affine rpc approximations of the two images computed during the rectification """ # read the RPC coefficients rpc1 = utils.rpc_from_geotiff(file1) rpc2 = utils.rpc_from_geotiff(file2) # get the altitude of the center of the AOI if z is None: lon, lat = np.mean(aoi['coordinates'][0][:4], axis=0) z = srtm4.srtm4(lon, lat) # compute rectifying affine transforms S1, S2, w, h, P1, P2 = rectifying_affine_transforms(rpc1, rpc2, aoi, z, register_ground) # compute SIFT keypoint matches (needed to estimate the disparity range) q1, q2 = sift_roi(file1, file2, aoi, z) # correct pointing error with the SIFT keypoint matches (optional) if correct_pointing: S1, S2 = pointing_error_correction(S1, S2, q1, q2) # rectify the crops rect1 = affine_crop(file1, S1, w, h) rect2 = affine_crop(file2, S2, w, h) #print (file1,S1,w,h) # transform the matches to the domain of the rectified images q1 = utils.points_apply_homography(S1, q1) q2 = utils.points_apply_homography(S2, q2) # disparity range bounds kpts_disps = (q2 - q1)[:, 0] disp_min = np.percentile(kpts_disps, 5) disp_max = np.percentile(kpts_disps, 100 - 5) if debug: # matches visualisation import cv2 kp1 = [cv2.KeyPoint(x, y, 1, 0) for x, y in q1] kp2 = [cv2.KeyPoint(x, y, 1, 0) for x, y in q2] matches = [[cv2.DMatch(i, i, 0, 0)] for i in range(len(q1))] plt.figure() plt.imshow(cv2.drawMatchesKnn(utils.simple_equalization_8bit(rect1), kp1, utils.simple_equalization_8bit(rect2), kp2, matches, None, flags=2)) plt.show() return rect1, rect2, S1, S2, disp_min, disp_max, P1, P2
def affine_crop(input_path, A, w, h): """ Apply an affine transform to an image. Args: input_path (string): path or url to the input image A (numpy array): 3x3 array representing an affine transform in homogeneous coordinates w, h (ints): width and height of the output image Return: numpy array of shape (h, w) containing a subset of the transformed image. The subset is the rectangle between points 0, 0 and w, h. """ # determine the rectangle that we need to read in the input image output_rectangle = [[0, 0], [w, 0], [w, h], [0, h]] x, y, w0, h0 = utils.bounding_box2D(utils.points_apply_homography(np.linalg.inv(A), output_rectangle)) x, y = np.floor((x, y)).astype(int) w0, h0 = np.ceil((w0, h0)).astype(int) # crop the needed rectangle in the input image with rasterio.open(input_path, 'r') as src: aoi = src.read(indexes=1, window=((y, y + h0), (x, x + w0)), boundless=True) # compensate the affine transform for the crop B = A @ utils.matrix_translation(x, y) # apply the affine transform out = ndimage.affine_transform(aoi.T, np.linalg.inv(B), output_shape=(w, h)).T return out
def rectify_aoi(file1, file2, aoi, z=None): """ Args: file1, file2: filename of two satellite images aoi: area of interest z (float, optional): base altitude with respect to WGS84 ellipsoid. If None, z is retrieved from srtm. Returns: rect1, rect2: numpy arrays with the images S1, S2: transformation matrices from the coordinate system of the original images disp_min, disp_max: horizontal disparity range P1, P2: affine rpc approximations of the two images computed during the rectification """ # read the RPC coefficients rpc1 = utils.rpc_from_geotiff(file1) rpc2 = utils.rpc_from_geotiff(file2) # get the altitude of the center of the AOI if z is None: lon, lat = np.mean(aoi['coordinates'][0][:4], axis=0) z = srtm4.srtm4(lon, lat) # compute rectifying affine transforms S1, S2, w, h, P1, P2 = rectifying_affine_transforms(rpc1, rpc2, aoi, z=z) # compute sift keypoint matches q1, q2 = sift_roi(file1, file2, aoi, z) # transform the matches to the domain of the rectified images q1 = utils.points_apply_homography(S1, q1) q2 = utils.points_apply_homography(S2, q2) # pointing correction (y_shift) y_shift = np.median(q2 - q1, axis=0)[1] S2 = matrix_translation(0, -y_shift) @ S2 # rectify the crops rect1 = affine_crop(file1, S1, w, h) rect2 = affine_crop(file2, S2, w, h) # disparity range bounds kpts_disps = (q2 - q1)[:, 0] disp_min = np.percentile(kpts_disps, 2) disp_max = np.percentile(kpts_disps, 100 - 2) return rect1, rect2, S1, S2, disp_min, disp_max, P1, P2
def sift_roi(file1, file2, aoi, z): """ Args: file1, file2 (str): paths or urls to two GeoTIFF images aoi (geojson.Polygon): area of interest z (float): base altitude for the aoi Returns: two numpy arrays with the coordinates of the matching points in the original (full-size) image domains """ # image crops crop1, x1, y1 = utils.crop_aoi(file1, aoi, z=z) crop2, x2, y2 = utils.crop_aoi(file2, aoi, z=z) # sift keypoint matches p1, p2 = match_pair(crop1, crop2) q1 = utils.points_apply_homography(utils.matrix_translation(x1, y1), p1) q2 = utils.points_apply_homography(utils.matrix_translation(x2, y2), p2) return q1, q2
def pointing_error_correction(S1, S2, q1, q2): """ Correct rectifying similarities for the pointing error. Args: S1, S2 (np.array): two 3x3 matrices representing the rectifying similarities q1, q2 (lists): two lists of matching keypoints Returns: two 3x3 matrices representing the corrected rectifying similarities """ # transform the matches to the domain of the rectified images q1 = utils.points_apply_homography(S1, q1) q2 = utils.points_apply_homography(S2, q2) # CODE HERE: insert a few lines to correct the vertical shift y_shift = np.median(q2 - q1, axis=0)[1] S1 = utils.matrix_translation(-0, +y_shift / 2) @ S1 S2 = utils.matrix_translation(-0, -y_shift / 2) @ S2 return S1, S2
def sift_roi(file1, file2, aoi, z): """ Args: file1, file2: filename of two satellite images aoi: area of interest z: base height for the aoi Returns: q1, q2: numpy arrays with the coordinates of the matching points in the original (full-size) image domains """ # image crops crop1, x1, y1 = utils.crop_aoi(file1, aoi, z=z) crop2, x2, y2 = utils.crop_aoi(file2, aoi, z=z) # sift keypoint matches p1, p2 = match_pair(crop1, crop2) q1 = utils.points_apply_homography(matrix_translation(x1, y1), p1) q2 = utils.points_apply_homography(matrix_translation(x2, y2), p2) return q1, q2