|
@@ -2,6 +2,7 @@ import sys
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
+
|
|
|
def ransac_iters(p, k, z):
|
|
|
""" Computes the required number of iterations for RANSAC.
|
|
|
|
|
@@ -17,9 +18,9 @@ def ransac_iters(p, k, z):
|
|
|
def log(base, argument):
|
|
|
""" Computes the logarithm to a given base for a given argument
|
|
|
|
|
|
- :param base: The base of the logarithm.
|
|
|
- :param argument: The argument of the logarithm.
|
|
|
- :return: The logarithm.
|
|
|
+ :param base: The base of the logarithm
|
|
|
+ :param argument: The argument of the logarithm
|
|
|
+ :return: The logarithm
|
|
|
"""
|
|
|
|
|
|
return np.log(argument) / np.log(base)
|
|
@@ -48,9 +49,13 @@ def ransac(pairs, n_iters, k, threshold):
|
|
|
H: (3, 3) numpy array, best homography observed during RANSAC
|
|
|
max_inliers: number of inliers N
|
|
|
inliers: (N, 4) numpy array containing the coordinates of the inliers
|
|
|
+ :param pairs:
|
|
|
+ :param n_iters:
|
|
|
+ :param threshold:
|
|
|
+ :param k:
|
|
|
"""
|
|
|
|
|
|
- H = {}
|
|
|
+ h = {}
|
|
|
max_inliers = 0
|
|
|
inliers = {}
|
|
|
|
|
@@ -73,19 +78,19 @@ def ransac(pairs, n_iters, k, threshold):
|
|
|
The points in the samples then have to be conditioned, so their values are between -1 and 1.
|
|
|
We also retrieve the condition matrices from this function.
|
|
|
"""
|
|
|
- sample1_conditioned, T_1 = condition_points(sample1)
|
|
|
- sample2_conditioned, T_2 = condition_points(sample2)
|
|
|
+ sample1_conditioned, t_1 = condition_points(sample1)
|
|
|
+ sample2_conditioned, t_2 = condition_points(sample2)
|
|
|
|
|
|
"""
|
|
|
Using the conditioned coordinates, we calculate the homographies both with respect to the conditioned points
|
|
|
and the unconditioned points.
|
|
|
"""
|
|
|
- H_current, HC = compute_homography(sample1_conditioned, sample2_conditioned, T_1, T_2)
|
|
|
+ h_current, hc = compute_homography(sample1_conditioned, sample2_conditioned, t_1, t_2)
|
|
|
|
|
|
"""
|
|
|
We then use the unconditioned points (as clarified in the office hours) to compute the homography distances.
|
|
|
"""
|
|
|
- homography_distances = compute_homography_distance(H_current, p1, p2)
|
|
|
+ homography_distances = compute_homography_distance(h_current, p1, p2)
|
|
|
|
|
|
"""
|
|
|
Using the find_inliers function, we retrieve the inliers for our given homography as well as the number
|
|
@@ -98,7 +103,7 @@ def ransac(pairs, n_iters, k, threshold):
|
|
|
better), we save our current homography as well as the number of inliers and the inliers themselves.
|
|
|
"""
|
|
|
if number_inliers_current > max_inliers:
|
|
|
- H = H_current
|
|
|
+ h = h_current
|
|
|
max_inliers = number_inliers_current
|
|
|
inliers = inliers_current
|
|
|
|
|
@@ -106,7 +111,7 @@ def ransac(pairs, n_iters, k, threshold):
|
|
|
We then return the best homography we found as well as the number of inliers found with it and the inliers
|
|
|
themselves.
|
|
|
"""
|
|
|
- return H, max_inliers, inliers
|
|
|
+ return h, max_inliers, inliers
|
|
|
|
|
|
|
|
|
def recompute_homography(inliers):
|
|
@@ -128,17 +133,17 @@ def recompute_homography(inliers):
|
|
|
"""
|
|
|
We then condition these points.
|
|
|
"""
|
|
|
- p1_conditioned, T_1 = condition_points(p1)
|
|
|
- p2_conditioned, T_2 = condition_points(p2)
|
|
|
+ p1_conditioned, t_1 = condition_points(p1)
|
|
|
+ p2_conditioned, t_2 = condition_points(p2)
|
|
|
|
|
|
"""
|
|
|
Using the conditioned points, we recompute the homography. The new homography should give better results than
|
|
|
the previously computed one, since we only use inliers (correct correspondences) here instead of all
|
|
|
correspondences including the wrong ones.
|
|
|
"""
|
|
|
- H, HC = compute_homography(p1_conditioned, p2_conditioned, T_1, T_2)
|
|
|
+ h, hc = compute_homography(p1_conditioned, p2_conditioned, t_1, t_2)
|
|
|
|
|
|
- return H
|
|
|
+ return h
|
|
|
|
|
|
|
|
|
def pick_samples(p1, p2, k):
|
|
@@ -175,11 +180,11 @@ def pick_samples(p1, p2, k):
|
|
|
|
|
|
def condition_points(points):
|
|
|
""" Conditioning: Normalization of coordinates for numeric stability
|
|
|
- by substracting the mean and dividing by half of the component-wise
|
|
|
+ by subtracting the mean and dividing by half of the component-wise
|
|
|
maximum absolute value.
|
|
|
Further, turns coordinates into homogeneous coordinates.
|
|
|
Args:
|
|
|
- points: (l, 2) numpy array containing unnormailzed cartesian coordinates.
|
|
|
+ points: (l, 2) numpy array containing not normalized cartesian coordinates.
|
|
|
|
|
|
Returns:
|
|
|
ps: (l, 3) numpy array containing normalized points in homogeneous coordinates.
|
|
@@ -205,7 +210,7 @@ def condition_points(points):
|
|
|
We then calculate the transformation matrix T like on slide 18 in Lecture 8.
|
|
|
We use the x components of s and t in the first row and the y components of s and t in the second row.
|
|
|
"""
|
|
|
- T = np.array(([1 / sx, 0, - tx / sx],
|
|
|
+ t = np.array(([1 / sx, 0, - tx / sx],
|
|
|
[0, 1 / sy, - ty / sy],
|
|
|
[0, 0, 1]))
|
|
|
|
|
@@ -217,22 +222,22 @@ def condition_points(points):
|
|
|
ps = []
|
|
|
for point in points:
|
|
|
homogeneous_point = np.append(point, 1)
|
|
|
- homogeneous_transformed_point = np.matmul(T, homogeneous_point)
|
|
|
+ homogeneous_transformed_point = np.matmul(t, homogeneous_point)
|
|
|
ps.append(homogeneous_transformed_point)
|
|
|
|
|
|
- return np.array(ps), T
|
|
|
+ return np.array(ps), t
|
|
|
|
|
|
|
|
|
-def compute_homography(p1, p2, T1, T2):
|
|
|
+def compute_homography(p1, p2, t1, t2):
|
|
|
""" Estimate homography matrix from point correspondences of conditioned coordinates.
|
|
|
- Both returned matrices shoul be normalized so that the bottom right value equals 1.
|
|
|
+ Both returned matrices should be normalized so that the bottom right value equals 1.
|
|
|
You may use np.linalg.svd for this function.
|
|
|
|
|
|
Args:
|
|
|
p1: (l, 3) numpy array, the conditioned homogeneous coordinates of interest points in img1
|
|
|
p2: (l, 3) numpy array, the conditioned homogeneous coordinates of interest points in img2
|
|
|
- T1: (3,3) numpy array, conditioning matrix for p1
|
|
|
- T2: (3,3) numpy array, conditioning matrix for p2
|
|
|
+ t1: (3,3) numpy array, conditioning matrix for p1
|
|
|
+ t2: (3,3) numpy array, conditioning matrix for p2
|
|
|
|
|
|
Returns:
|
|
|
H: (3, 3) numpy array, homography matrix with respect to unconditioned coordinates
|
|
@@ -247,12 +252,12 @@ def compute_homography(p1, p2, T1, T2):
|
|
|
"""
|
|
|
We calculate the number of rows in p1 (which must be the same as number of rows in p2).
|
|
|
"""
|
|
|
- N = p1.shape[0]
|
|
|
+ n = p1.shape[0]
|
|
|
# Create a (2*N, 9) matrix filled with zeros
|
|
|
- A = np.zeros((2 * N, 9))
|
|
|
+ a = np.zeros((2 * n, 9))
|
|
|
|
|
|
# We iterate from 0 to N (N is the number of correspondences)
|
|
|
- for i in range(0, N):
|
|
|
+ for i in range(0, n):
|
|
|
# The point from the first image (divided by the last component so that x and y have the non-homogeneous
|
|
|
# value. We could cut off the last component (which is now 1), but that step is not necessary here.)
|
|
|
point1 = p1[i] / p1[i][2]
|
|
@@ -278,45 +283,45 @@ def compute_homography(p1, p2, T1, T2):
|
|
|
"""
|
|
|
Finally, we save the values in the corresponding rows in matrix A
|
|
|
"""
|
|
|
- A[first_row_index] = first_row
|
|
|
- A[second_row_index] = second_row
|
|
|
+ a[first_row_index] = first_row
|
|
|
+ a[second_row_index] = second_row
|
|
|
|
|
|
"""
|
|
|
Now we perform a single value decomposition on A and retrieve the last right singular vector
|
|
|
"""
|
|
|
- u, s, vt = np.linalg.svd(A)
|
|
|
+ u, s, vt = np.linalg.svd(a)
|
|
|
h = vt[-1]
|
|
|
|
|
|
"""
|
|
|
Since we used conditioned points, we retrieve the homography matrix HC regarding the conditioned coordinates
|
|
|
by reshaping the last right singular vector .
|
|
|
"""
|
|
|
- HC = np.reshape(h, (3, 3))
|
|
|
+ hc = np.reshape(h, (3, 3))
|
|
|
|
|
|
"""
|
|
|
The matrix is then normalized so that the bottom right value equals 1.
|
|
|
"""
|
|
|
- HC = HC / HC[2, 2]
|
|
|
+ hc = hc / hc[2, 2]
|
|
|
|
|
|
"""
|
|
|
We calculate the homography matrix with respect to the unconditioned coordinates by using the formula from
|
|
|
lecture 8 on slide 18.
|
|
|
"""
|
|
|
- H = np.matmul(np.linalg.inv(T2), np.matmul(HC, T1))
|
|
|
+ h = np.matmul(np.linalg.inv(t2), np.matmul(hc, t1))
|
|
|
|
|
|
"""
|
|
|
The homography matrix is also normalized.
|
|
|
"""
|
|
|
- H = H / H[2, 2]
|
|
|
+ h = h / h[2, 2]
|
|
|
|
|
|
- return H, HC
|
|
|
+ return h, hc
|
|
|
|
|
|
|
|
|
-def compute_homography_distance(H, p1, p2):
|
|
|
+def compute_homography_distance(h, p1, p2):
|
|
|
""" Computes the pairwise symmetric homography distance.
|
|
|
|
|
|
Args:
|
|
|
- H: (3, 3) numpy array, homography matrix
|
|
|
+ h: (3, 3) numpy array, homography matrix
|
|
|
p1: (l, 2) numpy array, interest points in img1
|
|
|
p2: (l, 2) numpy array, interest points in img2
|
|
|
|
|
@@ -327,28 +332,28 @@ def compute_homography_distance(H, p1, p2):
|
|
|
"""
|
|
|
Determine the number of points in p1 (should be the same as in p2).
|
|
|
"""
|
|
|
- l = p1.shape[0]
|
|
|
+ number_of_points = p1.shape[0]
|
|
|
|
|
|
"""
|
|
|
Calculate the inverse of the homography H.
|
|
|
"""
|
|
|
- H_inverse = np.linalg.inv(H)
|
|
|
+ h_inverse = np.linalg.inv(h)
|
|
|
|
|
|
"""
|
|
|
Create a zero-filled array which we later fill with the distances.
|
|
|
"""
|
|
|
- distances = np.zeros(l)
|
|
|
+ distances = np.zeros(number_of_points)
|
|
|
|
|
|
"""
|
|
|
We calculate the transformed points for the given homography.
|
|
|
"""
|
|
|
- p1_transformed = transform_pts(np.array(p1), H)
|
|
|
- p2_transformed = transform_pts(np.array(p2), H_inverse)
|
|
|
+ p1_transformed = transform_pts(np.array(p1), h)
|
|
|
+ p2_transformed = transform_pts(np.array(p2), h_inverse)
|
|
|
|
|
|
"""
|
|
|
We calculate the symmetric squared distances using the formula in the assignment sheet for every point pair.
|
|
|
"""
|
|
|
- for index in range(0, l):
|
|
|
+ for index in range(0, number_of_points):
|
|
|
x1 = p1[index]
|
|
|
x2 = p2[index]
|
|
|
x1_transformed = p1_transformed[index]
|
|
@@ -368,7 +373,7 @@ def find_inliers(pairs, dist, threshold):
|
|
|
|
|
|
Returns:
|
|
|
N: number of inliers
|
|
|
- inliers: (N, 4)
|
|
|
+ inliers: (n, 4)
|
|
|
"""
|
|
|
|
|
|
inliers_list = []
|
|
@@ -387,18 +392,18 @@ def find_inliers(pairs, dist, threshold):
|
|
|
Furthermore, we convert the inliers_list to a numpy array.
|
|
|
Both values are then returned.
|
|
|
"""
|
|
|
- N = len(inliers_list)
|
|
|
+ n = len(inliers_list)
|
|
|
inliers = np.array(inliers_list)
|
|
|
|
|
|
- return N, inliers
|
|
|
+ return n, inliers
|
|
|
|
|
|
|
|
|
-def transform_pts(p, H):
|
|
|
+def transform_pts(p, h):
|
|
|
""" Transform p through the homography matrix H.
|
|
|
|
|
|
Args:
|
|
|
p: (l, 2) numpy array, interest points
|
|
|
- H: (3, 3) numpy array, homography matrix
|
|
|
+ h: (3, 3) numpy array, homography matrix
|
|
|
|
|
|
Returns:
|
|
|
points: (l, 2) numpy array, transformed points
|
|
@@ -420,7 +425,7 @@ def transform_pts(p, H):
|
|
|
"""
|
|
|
for point_index, point_value in enumerate(p):
|
|
|
homogeneous_p = np.append(point_value, 1)
|
|
|
- homogeneous_transformed_p = np.matmul(H, homogeneous_p)
|
|
|
+ homogeneous_transformed_p = np.matmul(h, homogeneous_p)
|
|
|
normalized_homogeneous_transformed_p = homogeneous_transformed_p / homogeneous_transformed_p[2]
|
|
|
transformed_p = normalized_homogeneous_transformed_p[0:2]
|
|
|
points_list.append(transformed_p)
|
|
@@ -429,11 +434,11 @@ def transform_pts(p, H):
|
|
|
|
|
|
|
|
|
def main():
|
|
|
- if len(sys.argv) != 3:
|
|
|
- print('Usage: hgcalc <pairs_file_path> <homography_file_path>')
|
|
|
+ if len(sys.argv) != 2:
|
|
|
+ print('Usage: hgcalc <pairs_file_path>')
|
|
|
else:
|
|
|
pairs_file_path = sys.argv[1]
|
|
|
- homography_file_path = sys.argv[2]
|
|
|
+ homography_file_path = '.\\homography.csv'
|
|
|
# RANSAC Parameters
|
|
|
ransac_threshold = 0.02 # inlier threshold
|
|
|
p = 0.35 # probability that any given correspondence is valid
|
|
@@ -443,14 +448,15 @@ def main():
|
|
|
pairs = np.loadtxt(pairs_file_path)
|
|
|
|
|
|
n_iters = ransac_iters(p, k, z)
|
|
|
- print('Interations to be done: ', n_iters)
|
|
|
- H, num_inliers, inliers = ransac(pairs, n_iters, k, ransac_threshold)
|
|
|
+ print('Iterations to be done: ', n_iters)
|
|
|
+ h, num_inliers, inliers = ransac(pairs, n_iters, k, ransac_threshold)
|
|
|
print('Number of inliers:', num_inliers)
|
|
|
|
|
|
# recompute homography matrix based on inliers
|
|
|
- H = recompute_homography(inliers)
|
|
|
- print(H)
|
|
|
- np.savetxt(homography_file_path, H)
|
|
|
+ h = recompute_homography(inliers)
|
|
|
+ print(h)
|
|
|
+ np.savetxt(homography_file_path, h)
|
|
|
+
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
main()
|