爬虫网站自重。
如图1所示, x i ′ o y i ′ x_{i}'oy_{i}' xi′oyi′代表原来的旧坐标系, x i o y i x_{i}oy_{i} xioyi代表将要转换的新坐标系, X X X轴平移参数为 x 0 x_0 x0 , Y Y Y轴平移参数为 y 0 y_0 y0 ,旋转角为 α \alpha α ,比例(缩放)系数为 λ \lambda λ (图上未表示出来)。
由转换示意图,可以得出关系式:
[ x i y i ] = [ x 0 y 0 ] + λ [ c o s α − s i n α s i n α c o s α ] [ x i ′ y i ′ ] (1) \left[ \begin{matrix} x_{i} \\ y_{i} \\ \end{matrix} \right]=\left[ \begin{matrix} x_0 \\ y_0 \\ \end{matrix} \right]+\lambda\left[ \begin{matrix} cos\alpha&-sin\alpha \\ sin\alpha& cos\alpha \\ \end{matrix} \right] \left[ \begin{matrix} x_{i}' \\ y_{i}' \\ \end{matrix} \right] \tag{1} [xiyi]=[x0y0]+λ[cosαsinα−sinαcosα][xi′yi′](1)
未知参数有四个 ( x 0 , y 0 , λ , α ) (x_0,y_0,\lambda,\alpha) (x0,y0,λ,α) ,根据以上方程组得出:必须具有2个公共点(具有两个坐标系下坐标值的点)列出四个方程式,解算四个参数。当公共点有3个或3个以上时,相当于具备多余观测值,需列立误差方程式用最小二乘原理进行平差求解参数的最或然值。将式(1)变换为纯量的形式如下:
{ x i = x 0 + λ c o s α ⋅ x i ′ − λ s i n α ⋅ y i ′ y i = y 0 + λ s i n α ⋅ x i ′ + λ c o s α ⋅ y i ′ (2) \begin{cases} x_i=x_0+\lambda cos\alpha \cdot x_{i}'-\lambda sin\alpha \cdot y_{i}'\\ y_i=y_0+\lambda sin\alpha \cdot x_{i}'+\lambda cos\alpha \cdot y_{i}'\\ \end{cases} \tag{2} {xi=x0+λcosα⋅xi′−λsinα⋅yi′yi=y0+λsinα⋅xi′+λcosα⋅yi′(2)
设两坐标系中有 n n n个公共点 ( x i , y i ) (x_i,y_i) (xi,yi)和 ( x i ′ , y i ′ ) , i = 1 , 2 , . . . , n (x_{i}',y_{i}'),i=1,2,...,n (xi′,yi′),i=1,2,...,n ,令新坐标系为观测值,旧坐标系中坐标设为无误差,当 n ≥ 3 n\geq3 n≥3时,则可列误差方程为:
[ v x 1 v y 1 ⋮ v x n v y n ] = [ 1 0 x 1 ′ − y 1 ′ 0 1 y 1 ′ x 1 ′ ⋮ ⋮ ⋮ ⋮ 1 0 x n ′ − y n ′ 0 1 y n ′ x n ′ ] [ x 0 ^ y 0 ^ λ c o s α ^ ^ λ s i n α ^ ^ ] − [ x 1 y 1 ⋮ x n y n ] (3) \left[ \begin{matrix} v_{x_1} \\ v_{y_1} \\ \vdots\\ v_{x_n} \\ v_{y_n} \\ \end{matrix} \right]=\left[ \begin{matrix} 1&0&x_{1}'&-y_{1}' \\ 0&1&y_{1}'&x_{1}' \\ \vdots&\vdots&\vdots&\vdots\\ 1&0&x_{n}'&-y_{n}' \\ 0&1&y_{n}'&x_{n}' \\ \end{matrix} \right] \left[ \begin{matrix} \hat{x_0} \\ \hat{y_0} \\ \hat{\lambda cos \hat{\alpha}} \\ \hat{\lambda sin \hat{\alpha}} \\ \end{matrix} \right]- \left[ \begin{matrix} x_1 \\ y_1 \\ \vdots \\ x_n \\ y_n \\ \end{matrix} \right] \tag{3} vx1vy1⋮vxnvyn = 10⋮1001⋮01x1′y1′⋮xn′yn′−y1′x1′⋮−yn′xn′ x0^y0^λcosα^^λsinα^^ − x1y1⋮xnyn (3)
或
V = B x ^ − l (4) V=B\hat{x}-l\tag{4} V=Bx^−l(4)
{ l x = x i − ( x 0 + λ c o s α ⋅ x i ′ − λ s i n α ⋅ y i ′ ) l y = y i − ( y 0 + λ s i n α ⋅ x i ′ + λ c o s α ⋅ y i ′ ) (5) \begin{cases} l_x=x_i-(x_0+\lambda cos\alpha \cdot x_{i}'-\lambda sin\alpha \cdot y_{i}')\\ l_y=y_i-(y_0+\lambda sin\alpha \cdot x_{i}'+\lambda cos\alpha \cdot y_{i}')\\ \end{cases} \tag{5} {lx=xi−(x0+λcosα⋅xi′−λsinα⋅yi′)ly=yi−(y0+λsinα⋅xi′+λcosα⋅yi′)(5)
根据最小二乘原理 V T P V = m i n V^TPV=min VTPV=min 可得法方程:
B T B x ^ − B T l = 0 (6) B^TB\hat{x}-B^Tl=0\tag{6} BTBx^−BTl=0(6)
解法方程:
x ^ = ( B T B ) − 1 B T l = N B B − 1 W (7) \hat{x}=(B^TB)^{-1}B^Tl=N_{BB}^{-1}W\tag{7} x^=(BTB)−1BTl=NBB−1W(7)
X ^ = X 0 + x ^ (8) \hat{X}=X^0+\hat{x}\tag{8} X^=X0+x^(8)
式中: X 0 = [ x 0 , y 0 , λ c o s α , λ s i n α ] T = [ 0 , 0 , 1 , 0 ] T X^0=[x_0,y_0,\lambda cos \alpha,\lambda sin \alpha]^T=[0,0,1,0]^T X0=[x0,y0,λcosα,λsinα]T=[0,0,1,0]T。
可求得参数 x 0 = X ^ [ 1 ] , y 0 = X ^ [ 2 ] , α = a r c t a n X ^ [ 4 ] X ^ [ 3 ] , λ = X ^ [ 3 ] 2 + X ^ [ 4 ] 2 x_0=\hat{X}[1],y_0=\hat{X}[2],\alpha=arctan\frac{\hat{X}[4]}{\hat{X}[3]},\lambda=\sqrt{\hat{X}[3]^2+\hat{X}[4]^2} x0=X^[1],y0=X^[2],α=arctanX^[3]X^[4],λ=X^[3]2+X^[4]2 。
import numpy as np
def four_parameter_coordinate_conversion(old_pts, new_pts):
"""
四参数坐标转换
:param old_pts: 旧坐标系
:param new_pts: 新坐标系
:return: 四参数计算结果
"""
if old_pts.shape[0] != new_pts.shape[0]:
return False
if old_pts.shape[0] < 3:
return False
n = old_pts.shape[0] # 点的个数
B = np.zeros((n * 2, 4)) # 矩阵B
L = np.zeros((n * 2, 1)) # 矩阵L
X = np.ones((4, 1)) # 解X
deltX = np.ones((4, 1)) # 迭代增量deltX
# 四参数的初始值
x0 = 0.0
y0 = 0.0
lamda = 1.0
alpha = 0.0
X[0] = x0
X[1] = y0
X[2] = lamda * np.cos(alpha)
X[3] = lamda * np.sin(alpha)
# 构建计算所需矩阵
for i in range(0, n):
B[2 * i, 0] = 1
B[2 * i, 1] = 0
B[2 * i, 0] = old_pts[i].x
B[2 * i, 1] = -old_pts[i].y
B[2 * i + 1, 0] = 0
B[2 * i + 1, 1] = 1
B[2 * i + 1, 0] = old_pts[i].y
B[2 * i + 1, 1] = old_pts[i].x
L[2 * i] = new_pts[i].x
L[2 * i + 1] = new_pts[i].y
# 迭代计算
count = 0
while np.linalg.norm(deltX) > 0.0001:
l = L - B.dot(X)
NBB = B.T.dot(B)
W = B.T.dot(l)
deltX = np.linalg.inv(NBB).dot(W)
X += deltX
count += 1
if count >= 10:
break
print("迭代次数:", count)
return X