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
def matches_from_projection_matrices_roi(im1, im2, rpc1, rpc2, x, y, w, h): """ Computes a list of sift matches between two Pleiades images. Args: im1, im2: paths to the two Pleiades images (usually jp2 or tif) rpc1, rpc2: two instances of the rpc_model.RPCModel class x, y, w, h: four integers definig the rectangular ROI in the first image. (x, y) is the top-left corner, and (w, h) are the dimensions of the rectangle. This function uses the parameter subsampling_factor_registration from the config module. If factor > 1 then the registration is performed over subsampled images, but the resulting keypoints are then scaled back to conceal the subsampling Returns: matches: 2D numpy array containing a list of matches. Each line contains one pair of points, ordered as x1 y1 x2 y2. The coordinate system is that of the big images. If no sift matches are found, then an exception is raised. """ #m, M = rpc_utils.altitude_range(rpc1, x, y, w, h) m=5 M=20 # build an array with vertices of the 3D ROI, obtained as {2D ROI} x [m, M] # also include the midpoints because the 8 corners of the frustum alone don't seem to work a = np.array([x, x, x, x, x+w, x+w, x+w, x+w,x+w/2,x+w/2,x+w/2,x+w/2,x+w/2,x+w/2,x ,x ,x+w ,x+w ]) b = np.array([y, y, y+h, y+h, y, y, y+h, y+h,y ,y ,y+h/2,y+h/2,y+h ,y+h ,y+h/2,y+h/2,y+h/2,y+h/2]) c = np.array([m, M, m, M, m, M, m, M,m ,M ,m ,M ,m ,M ,m ,M ,m ,M ]) xx = np.zeros(len(a)) yy = np.zeros(len(a)) # corresponding points in im2 P1 = np.loadtxt(rpc1) P2 = np.loadtxt(rpc2) M = P1[:,:3] p4 = P1[:,3] m3 = M[2,:] inv_M = np.linalg.inv(M) v = np.vstack((a,b,c*0+1)) for i in range(len(a)): v = np.array([a[i],b[i],1]) mu = c[i] / np.sign ( np.linalg.det(M) ) X3D = inv_M.dot (mu * v - p4 ) # backproject newpoints = P2.dot(np.hstack([X3D,1])) xx[i] = newpoints[0] / newpoints[2] yy[i] = newpoints[1] / newpoints[2] print xx print yy matches = np.vstack([a, b,xx,yy]).T return matches ##### xx, yy = rpc_utils.find_corresponding_point(rpc1, rpc2, a, b, c)[0:2] # bounding box in im2 x2, y2, w2, h2 = common.bounding_box2D(np.vstack([xx, yy]).T) ## GF NOT USED x1, y1, w1, h1 = x, y, w, h x2, y2, w2, h2 = x, y, w, h # do crops, to apply sift on reasonably sized images crop1 = common.image_crop_LARGE(im1, x1, y1, w1, h1) crop2 = common.image_crop_LARGE(im2, x2, y2, w2, h2) T1 = common.matrix_translation(x1, y1) T2 = common.matrix_translation(x2, y2) # call sift matches for the images matches = matches_from_sift(crop1, crop2) if matches.size: # compensate coordinates for the crop and the zoom pts1 = common.points_apply_homography(T1, matches[:, 0:2]) pts2 = common.points_apply_homography(T2, matches[:, 2:4]) return np.hstack([pts1, pts2]) else: raise Exception("no sift matches")
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