def get_homography_estimate(filename): # # Conventions: # a_i, b_i # are variables in image space, units are pixels # a_w, b_w # are variables in world space, units are meters # print '\n========================================' print ' File: ' + filename print '========================================\n' from skimage.io import imread from skimage.color import rgb2gray im = imread(filename) im = rgb2gray(im) detections = get_tag_detections(im, cache_tag=filename) print ' %d tags detected.' % len(detections) # # Sort detections by distance to center # c_i = np.array([im.shape[1], im.shape[0]]) / 2. dist = lambda p_i: np.linalg.norm(p_i - c_i) closer_to_center = lambda d1, d2: int(dist(d1.c) - dist(d2.c)) detections.sort(cmp=closer_to_center) from tag36h11_mosaic import TagMosaic tag_mosaic = TagMosaic(0.0254) mosaic_pos = lambda det: tag_mosaic.get_position_meters(det.id) det_i = np.array([ d.c for d in detections ]) det_w = np.array([ mosaic_pos(d) for d in detections ]) # # Improvisation on the classic calibration procedure. We # obtain our initial homography estimate from 9 detections # at the center. This is better because the distortion is # minimal at the center. # det_i9 = det_i[:9] det_w9 = det_w[:9] from projective_math import WeightedLocalHomography, UnitWeightingFunction H_estimator = WeightedLocalHomography(UnitWeightingFunction()) for s, t in zip(det_w9, det_i9): H_estimator.add_correspondence(s, t) from tupletypes import Correspondence H = H_estimator.get_homography_at(det_w9[0]) corrs = [ Correspondence(w, i) for w, i in zip(det_w, det_i) ] return HomographyInfo(corrs, H, im.shape)
def process(filename): # # Conventions: # a_i, b_i # are variables in image space, units are pixels # a_w, b_w # are variables in world space, units are meters # print '\n========================================' print ' File: ' + filename print '========================================\n' im = imread(filename) im = rgb2gray(im) im = (im * 255.).astype(np.uint8) tag_mosaic = TagMosaic(0.0254) detections = AprilTagDetector().detect(im) print ' %d tags detected.' % len(detections) # # Sort detections by distance to center # c_i = np.array([im.shape[1], im.shape[0]]) / 2. dist = lambda p_i: np.linalg.norm(p_i - c_i) closer_to_center = lambda d1, d2: int(dist(d1.c) - dist(d2.c)) detections.sort(cmp=closer_to_center) mosaic_pos = lambda det: tag_mosaic.get_position_meters(det.id) det_i = np.array([ d.c for d in detections ]) det_w = np.array([ mosaic_pos(d) for d in detections ]) # # To learn a weighted local homography, we find the weighting # function parameters that minimize reprojection error across # leave-one-out validation folds of the data. Since the # homography is local at the center, we only use 9 nearest # detections to the center # det_i9 = det_i[:9] det_w9 = det_w[:9] def local_homography_loocv_error(theta, args): src, tgt = args errs = [ local_homography_error(theta, src[t_ix], tgt[t_ix], src[v_ix], tgt[v_ix]) for t_ix, v_ix in LeaveOneOut(len(src)) ] return np.mean(errs) def learn_homography_i2w(): result = minimize( local_homography_loocv_error, x0=[ 50, 1, 1e-3 ], args=[ det_i9, det_w9 ], method='Powell', options={'ftol': 1e-3} ) print '\nHomography: i->w' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for i, w in zip(det_i9, det_w9): H.add_correspondence(i, w) return H def learn_homography_w2i(): result = minimize( local_homography_loocv_error, x0=[ 0.0254, 1, 1e-3 ], method='Powell', args=[ det_w9, det_i9 ], options={'ftol': 1e-3} ) print '\nHomography: w->i' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for w, i in zip(det_w9, det_i9): H.add_correspondence(w, i) return H # # We assume that the distortion is zero at the center of # the image and we are interesting in the word to image # homography at the center of the image. However, we don't # know the center of the image in world coordinates. # So we follow a procedure as explained below: # # First, we learn a homography from image to world # Next, we find where the image center `c_i` maps to in # world coordinates (`c_w`). Finally, we find the local # homography `LH0` from world to image at `c_w` # H_iw = learn_homography_i2w() c_i = np.array([im.shape[1], im.shape[0]]) / 2. c_w = H_iw.map(c_i)[:2] H_wi = learn_homography_w2i() LH0 = H_wi.get_homography_at(c_w) print '\nHomography at center' print '----------------------' print ' c_w =', c_w print ' c_i =', c_i print 'LH0 * c_w =', H_wi.map(c_w) # # Obtain distortion estimate # detected + undistortion = mapped # (or) undistortion = mapped - detected # def homogeneous_coords(arr): return np.hstack([ arr, np.ones((len(arr), 1)) ]) mapped_i = LH0.dot(homogeneous_coords(det_w).T).T mapped_i = np.array([ p / p[2] for p in mapped_i ]) mapped_i = mapped_i[:,:2] undistortion = mapped_i - det_i # image + undistortion = mapped max_distortion = np.max([np.linalg.norm(u) for u in undistortion]) print '\nMaximum distortion is %.2f pixels' % max_distortion # # Fit non-parametric model to the observations # model = GPModel(det_i, undistortion) print '\nGP Hyper-parameters' print '---------------------' print ' x: ', model._gp_x._covf.theta print ' log-likelihood: %.4f' % model._gp_x.model_evidence() print ' y: ', model._gp_y._covf.theta print ' log-likelihood: %.4f' % model._gp_y.model_evidence() print '' print ' Optimization detail:' print ' [ x ]' print ' ' + str(model._gp_x.fit_result).replace('\n', '\n ') print ' [ y ]' print ' ' + str(model._gp_y.fit_result).replace('\n', '\n ') # # Visualization # from skimage.filters import scharr from matplotlib import pyplot as plt plt.style.use('ggplot') plt.figure(figsize=(16,10)) #__1__ plt.subplot(221) plt.title(filename.split('/')[-1]) plt.imshow(im, cmap='gray') plt.plot(det_i[:,0], det_i[:,1], 'o') for i, d in enumerate(detections): plt.text(d.c[0], d.c[1], str(i), fontsize=8, color='white', bbox=dict(facecolor='maroon', alpha=0.75)) plt.grid() plt.axis('equal') #__2__ plt.subplot(222) plt.title('Non-linear Homography') if False: plt.imshow(1.-scharr(im), cmap='bone', interpolation='gaussian') from collections import defaultdict row_groups = defaultdict(list) col_groups = defaultdict(list) for d in detections: row, col = tag_mosaic.get_row(d.id), tag_mosaic.get_col(d.id) row_groups[row] += [ d.id ] col_groups[col] += [ d.id ] for k, v in row_groups.iteritems(): a = tag_mosaic.get_position_meters(np.min(v)) b = tag_mosaic.get_position_meters(np.max(v)) x_coords = np.linspace(a[0], b[0], 100) points = np.array([ H_wi.map([x, a[1]]) for x in x_coords ]) plt.plot(points[:,0], points[:,1], '-',color='#CF4457', linewidth=2) for k, v in col_groups.iteritems(): a = tag_mosaic.get_position_meters(np.min(v)) b = tag_mosaic.get_position_meters(np.max(v)) y_coords = np.linspace(a[1], b[1], 100) points = np.array([ H_wi.map([a[0], y]) for y in y_coords ]) plt.plot(points[:,0], points[:,1], '-',color='#CF4457', linewidth=2) plt.plot(det_i[:,0], det_i[:,1], 'kx') plt.grid() plt.axis('equal') #__3__ plt.subplot(223) plt.title('Qualitative Estimates') for i, d in enumerate(detections): plt.text(d.c[0], d.c[1], str(i), fontsize=8, color='#999999') X, Y = det_i[:,0], det_i[:,1] U, V = undistortion[:,0], undistortion[:,1] plt.quiver(X, Y, U, -V, units='dots') # plt.quiver(X, Y, U, -V, angles='xy', scale_units='xy', scale=1) # plot exact plt.text( 0.5, 0, 'max observed distortion: %.2f px' % max_distortion, color='#CF4457', fontsize=10, horizontalalignment='center', verticalalignment='bottom', transform=plt.gca().transAxes) plt.gca().invert_yaxis() plt.axis('equal') #__4__ plt.subplot(224) plt.title('Qualitative Undistortion') H, W = im.shape grid = np.array([[x, y] for y in xrange(0, H, 80) for x in xrange(0, W, 80)]) predicted = model.predict(grid) X, Y = grid[:,0], grid[:,1] U, V = predicted[:,0], predicted[:,1] plt.quiver(X, Y, U, -V, units='dots') #plt.quiver(X, Y, U, -V, angles='xy', scale_units='xy', scale=1)) # plot exact plt.gca().invert_yaxis() plt.axis('equal') plt.tight_layout() plt.gcf().patch.set_facecolor('#dddddd') #plt.show() plt.savefig(filename + '.svg', bbox_inches='tight')
def get_homography_model(filename): # # Conventions: # a_i, b_i # are variables in image space, units are pixels # a_w, b_w # are variables in world space, units are meters # print '\n========================================' print ' File: ' + filename print '========================================\n' im = imread(filename) im = rgb2gray(im) detections = get_tag_detections(im) print ' %d tags detected.' % len(detections) # # Sort detections by distance to center # c_i = np.array([im.shape[1], im.shape[0]]) / 2. dist = lambda p_i: np.linalg.norm(p_i - c_i) closer_to_center = lambda d1, d2: int(dist(d1.c) - dist(d2.c)) detections.sort(cmp=closer_to_center) tag_mosaic = TagMosaic(0.0254) mosaic_pos = lambda det: tag_mosaic.get_position_meters(det.id) det_i = np.array([ d.c for d in detections ]) det_w = np.array([ mosaic_pos(d) for d in detections ]) # # To learn a weighted local homography, we find the weighting # function parameters that minimize reprojection error across # leave-one-out validation folds of the data. Since the # homography is local at the center, we only use 9 detections # nearest to the center # det_i9 = det_i[:9] det_w9 = det_w[:9] def local_homography_loocv_error(theta, args): src, tgt = args errs = [ local_homography_error(theta, src[t_ix], tgt[t_ix], src[v_ix], tgt[v_ix]) for t_ix, v_ix in LeaveOneOut(len(src)) ] return np.mean(errs) def learn_homography_i2w(): result = minimize( local_homography_loocv_error, x0=[ 50, 1, 1e-3 ], args=[ det_i9, det_w9 ], method='Powell', options={'ftol': 1e-3} ) print '\nHomography: i->w' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for i, w in zip(det_i9, det_w9): H.add_correspondence(i, w) return H def learn_homography_w2i(): result = minimize( local_homography_loocv_error, x0=[ 0.0254, 1, 1e-3 ], method='Powell', args=[ det_w9, det_i9 ], options={'ftol': 1e-3} ) print '\nHomography: w->i' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for w, i in zip(det_w9, det_i9): H.add_correspondence(w, i) return H # # We assume that the distortion is zero at the center of # the image and we are interesting in the word to image # homography at the center of the image. However, we don't # know the center of the image in world coordinates. # So we follow a procedure as explained below: # # First, we learn a homography from image to world # Next, we find where the image center `c_i` maps to in # world coordinates (`c_w`). Finally, we find the local # homography `LH0` from world to image at `c_w` # H_iw = learn_homography_i2w() c_i = np.array([im.shape[1], im.shape[0]]) / 2. c_w = H_iw.map(c_i)[:2] H_wi = learn_homography_w2i() LH0 = H_wi.get_homography_at(c_w) print '\nHomography at center' print '----------------------' print ' c_w =', c_w print ' c_i =', c_i print 'LH0 * c_w =', H_wi.map(c_w) print '' corrs = [ Correspondence(w, i) for w, i in zip(det_w, det_i) ] return corrs, WorldImageHomographyInfo(H_wi, c_w, c_i)
def get_homography_model(filename): # # Conventions: # a_i, b_i # are variables in image space, units are pixels # a_w, b_w # are variables in world space, units are meters # print '\n========================================' print ' File: ' + filename print '========================================\n' im = imread(filename) im = rgb2gray(im) detections = get_tag_detections(im) print ' %d tags detected.' % len(detections) # # Sort detections by distance to center # c_i = np.array([im.shape[1], im.shape[0]]) / 2. dist = lambda p_i: np.linalg.norm(p_i - c_i) closer_to_center = lambda d1, d2: int(dist(d1.c) - dist(d2.c)) detections.sort(cmp=closer_to_center) tag_mosaic = TagMosaic(0.0254) mosaic_pos = lambda det: tag_mosaic.get_position_meters(det.id) det_i = np.array([d.c for d in detections]) det_w = np.array([mosaic_pos(d) for d in detections]) # # To learn a weighted local homography, we find the weighting # function parameters that minimize reprojection error across # leave-one-out validation folds of the data. Since the # homography is local at the center, we only use 9 detections # nearest to the center # det_i9 = det_i[:9] det_w9 = det_w[:9] def local_homography_loocv_error(theta, args): src, tgt = args errs = [ local_homography_error(theta, src[t_ix], tgt[t_ix], src[v_ix], tgt[v_ix]) for t_ix, v_ix in LeaveOneOut(len(src)) ] return np.mean(errs) def learn_homography_i2w(): result = minimize(local_homography_loocv_error, x0=[50, 1, 1e-3], args=[det_i9, det_w9], method='Powell', options={'ftol': 1e-3}) print '\nHomography: i->w' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for i, w in zip(det_i9, det_w9): H.add_correspondence(i, w) return H def learn_homography_w2i(): result = minimize(local_homography_loocv_error, x0=[0.0254, 1, 1e-3], method='Powell', args=[det_w9, det_i9], options={'ftol': 1e-3}) print '\nHomography: w->i' print '------------------' print ' params:', result.x print ' rmse: %.6f' % sqrt(result.fun) print '\n Optimization detail:' print ' ' + str(result).replace('\n', '\n ') H = create_local_homography_object(*result.x) for w, i in zip(det_w9, det_i9): H.add_correspondence(w, i) return H # # We assume that the distortion is zero at the center of # the image and we are interesting in the word to image # homography at the center of the image. However, we don't # know the center of the image in world coordinates. # So we follow a procedure as explained below: # # First, we learn a homography from image to world # Next, we find where the image center `c_i` maps to in # world coordinates (`c_w`). Finally, we find the local # homography `LH0` from world to image at `c_w` # H_iw = learn_homography_i2w() c_i = np.array([im.shape[1], im.shape[0]]) / 2. c_w = H_iw.map(c_i)[:2] H_wi = learn_homography_w2i() LH0 = H_wi.get_homography_at(c_w) print '\nHomography at center' print '----------------------' print ' c_w =', c_w print ' c_i =', c_i print 'LH0 * c_w =', H_wi.map(c_w) print '' corrs = [Correspondence(w, i) for w, i in zip(det_w, det_i)] return corrs, WorldImageHomographyInfo(H_wi, c_w, c_i)