-
Notifications
You must be signed in to change notification settings - Fork 48
/
affine_transform.py
140 lines (99 loc) · 4.39 KB
/
affine_transform.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Created by Qixun Qu
# 2017/04/10
#
import numpy as np
class Affine():
def create_test_case(self, outlier_rate=0):
''' CREATE_TEST_CASE
Randomly generate a test case of affine transformation.
Input arguments:
- outlier_rate : the percentage of outliers in test case,
default is 0
Outputs:
- pts : warped points
- pts_tilde : source points that wll be transformed
- A, t : parameters of affine transformation, A is a 2x2
matrix, t is a 2x1 vector, both of them are created randomly
'''
# Randomly generate affine transformation
# A is a 2x2 matrix, the range of each value is from -2 to 2
A = 4 * np.random.rand(2, 2) - 2
# % t is a 2x1 VECTOR, the range of each value is from -10 to 10
t = 20 * np.random.rand(2, 1) - 10
# Set the number of points in test case
num = 1000
# Compute the number of outliers and inliers respectively
outliers = int(np.round(num * outlier_rate))
inliers = int(num - outliers)
# Gernerate source points whose scope from (0,0) to (100, 100)
pts_s = 100 * np.random.rand(2, num)
# Initialize warped points matrix
pts_t = np.zeros((2, num))
# Compute inliers in warped points matrix by applying A and t
pts_t[:, :inliers] = np.dot(A, pts_s[:, :inliers]) + t
# Generate outliers in warped points matrix
pts_t[:, inliers:] = 100 * np.random.rand(2, outliers)
# Reset the order of warped points matrix,
# outliers and inliers will scatter randomly in test case
rnd_idx = np.random.permutation(num)
pts_s = pts_s[:, rnd_idx]
pts_t = pts_t[:, rnd_idx]
return A, t, pts_s, pts_t
def estimate_affine(self, pts_s, pts_t):
''' ESTIMATE_AFFINE
Estimate affine transformation by the given points
correspondences.
Input arguments:
- pts : points in target image
- pts_tilde : points in source image
Outputs:
- A, t : the affine transformation, A is a 2x2 matrix
that indicates the rotation and scaling transformation,
t is a 2x1 vector determines the translation
Method:
To estimate an affine transformation between two images,
at least 3 corresponding points are needed.
In this case, 6-parameter affine transformation are taken into
consideration, which is shown as follows:
| x' | = | a b | * | x | + | tx |
| y' | | c d | | y | | ty |
For 3 corresponding points, 6 equations can be formed as below:
| x1 y1 0 0 1 0 | | a | | x1' |
| 0 0 x1 y1 0 1 | | b | | y1' |
| x2 y2 0 0 1 0 | * | c | = | x2' |
| 0 0 x2 y2 0 1 | | d | | y2' |
| x3 y3 0 0 1 0 | | tx | | x3' |
| 0 0 x3 y3 0 1 | | ty | | y3' |
|------> M <------| |-> theta <-| |-> b <-|
Solve the equation to compute theta by: theta = M \ b
Thus, affine transformation can be obtained as:
A = | a b | t = | tx |
| c d | | ty |
'''
# Get the number of corresponding points
pts_num = pts_s.shape[1]
# Initialize the matrix M,
# M has 6 columns, since the affine transformation
# has 6 parameters in this case
M = np.zeros((2 * pts_num, 6))
for i in range(pts_num):
# Form the matrix m
temp = [[pts_s[0, i], pts_s[1, i], 0, 0, 1, 0],
[0, 0, pts_s[0, i], pts_s[1, i], 0, 1]]
M[2 * i: 2 * i + 2, :] = np.array(temp, dtype=object)
# Form the matrix b,
# b contains all known target points
b = pts_t.T.reshape((2 * pts_num, 1))
try:
# Solve the linear equation
theta = np.linalg.lstsq(M, b, rcond=None)[0]
# Form the affine transformation
A = theta[:4].reshape((2, 2))
t = theta[4:]
except np.linalg.linalg.LinAlgError:
# If M is singular matrix, return None
# print("Singular matrix.")
A = None
t = None
return A, t