假设我有一张图像,我想将其作为系统正向模型的一部分进行变形。在反向模型中,我需要能够撤消扭曲。考虑以下几点:
import numpy as np
from scipy.ndimage import map_coordinates
from matplotlib import pyplot as plt
def make_rotation_matrix(abg, radians=False):
ABG = np.zeros(3)
ABG[:len(abg)] = abg
abg = ABG
if not radians:
abg = np.radians(abg)
alpha, beta, gamma = abg
cos1 = np.cos(alpha)
cos2 = np.cos(beta)
cos3 = np.cos(gamma)
sin1 = np.sin(alpha)
sin2 = np.sin(beta)
sin3 = np.sin(gamma)
Rx = np.asarray([
[1, 0, 0 ], # NOQA
[0, cos1, -sin1],
[0, sin1, cos1]
])
Ry = np.asarray([
[cos2, 0, sin2],
[ 0, 1, 0], # NOQA
[-sin2, 0, cos2],
])
Rz = np.asarray([
[cos3, -sin3, 0],
[sin3, cos3, 0],
[0, 0, 1],
])
m = Rz@Ry@Rx
return m
# draw a square in an image
sfe = np.zeros((128,128), dtype=float)
c=64
w=32
sfe[c-w:c+w,c-w:c+w] = 1
# compute the coordinates, translate to the origin, rotate, translate back
xin = np.arange(128)
yin = np.arange(128)
xin, yin = np.meshgrid(xin,yin)
rot = make_rotation_matrix((0,45,0))
ox, oy = 127/2, 127/2
tin = np.eye(4)
tin[0,-1] = -ox
tin[1,-1] = -oy
tout = np.eye(4)
tout[0,-1] = ox
tout[1,-1] = oy
rot2 = np.zeros((4,4), dtype=float)
rot2[:3,:3] = rot
rot2[-1,-1] = 1
M = tout@(rot2@tin)
Mi = np.linalg.inv(M)
points = np.zeros((xin.size, 4), dtype=float)
points[:,0] = xin.ravel()
points[:,1] = yin.ravel()
points[:,2] = 0 # z=0
points[:,3] = 1 # lambda coordinate for homography
out = np.dot(Mi, points.T)
xout = out[0].reshape(xin.shape)
yout = out[1].reshape(yin.shape)
zout = out[2].reshape(xin.shape)
hout = out[3].reshape(xin.shape)
# do I need to do something differently here?
points2 = points.copy()
out2 = np.dot(M, points2.T)
xout2 = out2[0].reshape(xin.shape)
yout2 = out2[1].reshape(yin.shape)
zout2 = out2[2].reshape(xin.shape)
hout2 = out2[3].reshape(xin.shape)
mapped = map_coordinates(sfe, (yout,xout))
unmapped = map_coordinates(mapped, (yout2,xout2))
neighbors = np.hstack((sfe, mapped, unmapped))
plt.imshow(neighbors)
如果我执行时钟旋转而不是平面外旋转,我会得到我期望的行为:
我明白,根据构造,我假设图像是鸟瞰图或平面单应性图像,这没问题。我错过了什么?一些与图像扭曲相关的谷歌发现cryptic matlab answers,但我不明白“空间参考”在做什么。
编辑:一个示例单应性,其逆实际上不会撤消使用 map_coordinates 的转换:
H = np.array([[ 0.063, -0.011, 0.761],
[ 0.011, 0.063, -0.639],
[-0. , -0. , 0.063]])
简单地用 plot.scatter 绘制一个正方形,它确实反转了。
我重构了你的代码,看看发生了什么。
首先,
map_coordinates()
执行“拉”操作,即将源像素拉入结果网格,使用您提供的索引。这些索引需要使用规则网格(对于结果)和变换的inverse(到源帧)来生成。
然后...删除 Z 确实很重要,尤其是在反转转换时。
给定一个平面外旋转,比如围绕 Y,你会得到这样的东西:
[[ 0.70711 0. 0.70711 0. ]
[ 0. 1. 0. 0. ]
[-0.70711 0. 0.70711 0. ]
[ 0. 0. 0. 1. ]]
它的倒数是:
[[ 0.70711 0. -0.70711 0. ]
[ 0. 1. 0. 0. ]
[ 0.70711 0. 0.70711 0. ]
[ 0. 0. 0. 1. ]]
如果你把 Z 放在两者中(并将其应用于你拥有的 2D 数据),你现在得到一对变换,它们都收缩图像:
[[0.70711 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]]
[[0.70711 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]]
收缩是旋转(平面内点)的出现,但不是旋转。删除 Z 不保持
inv(M) @ M == I
.
旋转的点,已经旋转出它们的平面,具有非零 Z,当你想进一步旋转它们时这很重要(例如旋转它们back)。删除 Z 意味着您不再拥有该信息。你必须假设它们在空间中的位置,而二维变换必须收缩或拉伸,这取决于你假设它们位于哪个平面。
您必须先将 Z 放入 M (4x4) 中,然后反转生成的 3x3 矩阵。现在你有了正确的逆,它扩展了图像,导致恒等变换。
[[0.70711 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]]
[[1.41421 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]]
现在这里有一些代码:
def translate4(tx=0, ty=0, tz=0):
T = np.eye(4)
T[0:3, 3] = (tx, ty, tz)
return T
def rotate4(rx=0, ry=0, rz=0):
R = np.eye(4)
R[0:3, 0:3] = make_rotation_matrix((rx, ry, rz))
return R
def dropZ(T4):
"assumes that XYZW inputs have Z=0 and that the result's Z will be ignored"
tmp = T4[[0,1,3], :]
tmp = tmp[:, [0,1,3]]
return tmp
# input data
im_source = cv.imread(cv.samples.findFile("lena.jpg"), cv.IMREAD_GRAYSCALE)
height, width = im_source.shape[:2]
# transformation: rotate around center
cx, cy = (width-1)/2, (height-1)/2
Tin = translate4(-cx, -cy)
Tout = translate4(+cx, +cy)
R = rotate4(ry=45, rz=30) # with a little in-plane rotation
M = Tout @ R @ Tin
M3 = dropZ(M)
Mi3 = inv(M3)
#print(M3)
#print(Mi3)
# coordinates grid
xin = np.arange(width)
yin = np.arange(height)
xin, yin = np.meshgrid(xin, yin)
zin = np.zeros_like(xin)
win = np.ones_like(xin)
points4 = np.vstack((xin.flatten(), yin.flatten(), zin.flatten(), win.flatten()))
print(points4)
points3 = np.vstack((xin.flatten(), yin.flatten(), win.flatten()))
print(points3)
# always: transform inverted because map_coords is backwards/pull
# can't invert right at the map_coords() call because we've already warped the grid by then
points_warped = inv(M3) @ points3 # apply M3 to identity grid, for input image
print("warped:")
print(points_warped)
points_identity = M3 @ points_warped # apply inv(M3) to warped grid, giving identity grid
print("unwarped: (identity grid)")
print(points_identity)
points_unwarping = M3 @ points3 # apply inv(M3) to identity grid, suitable for unwarping *warped* image
# map_coordinates() wants indices, so Y,X or I,J
coords_warped = points_warped.reshape((3, height, width))[[1,0]]
coords_identity = points_identity.reshape((3, height, width))[[1,0]]
coords_unwarping = points_unwarping.reshape((3, height, width))[[1,0]]
im_warped = map_coordinates(im_source, coords_warped)
im_identity = map_coordinates(im_source, coords_identity)
im_unwarped = map_coordinates(im_warped, coords_unwarping)
neighbors = np.hstack((im_source, im_warped, im_identity, im_unwarped))
#neighbors = np.hstack((im1, im2, im3))
plt.figure(figsize=(20,20))
plt.imshow(neighbors, cmap='gray')
plt.show()