我正在尝试以一种非常具体的方式水平拉伸图像。每个 x 素数坐标应遵循相对于原始 x 坐标的切线路径。我相信有两种方法可以做到这一点:
使用this答案进行地图反演,我试图找出为什么两个图像不相同。我知道第一种方法可以为我提供所需的正确图像,那么为什么第二种方法不起作用呢?是因为@ChristophRackwitz 对答案的评论“精度有限”吗?
import cv2
import glob
import numpy as np
import math
A = -1010
B = -3.931
C = 5.258
D = 978.3
M = -193.8
N = 1740
def get_tan_func_value(x):
return A * math.tan((((x-N)/M)+B)/C) + D
def get_inverse_tan_func_value(x):
return M * (C*math.atan((x-D)/A) - B) + N
# answer from linked post
def invert_map(F, shape):
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices(shape)
P = np.copy(I)
for i in range(10):
P += I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
return P
# import image
images = glob.glob('*.jpg')
img = cv2.imread(images[0])
h, w = img.shape[:2]
map_x_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_x_inverse_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_y = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
# x tan function map
for i in range(map_x_tan.shape[0]):
map_x_tan[i,:] = [get_tan_func_value(x) for x in range(map_x_tan.shape[1])]
# x inverse tan function map
for i in range(map_x_inverse_tan.shape[0]):
map_x_inverse_tan[i,:] = [get_inverse_tan_func_value(x) for x in range(map_x_inverse_tan.shape[1])]
# default y map
for j in range(map_y.shape[1]):
map_y[:,j] = [y for y in range(map_y.shape[0])]
# convert x tan map to 2 channel (x,y) map
(xymap_tan, _) = cv2.convertMaps(map1=map_x_tan, map2=map_y, dstmap1type=cv2.CV_32FC2)
# invert the 2 channel x tan map
xymap_inverted = invert_map(xymap_tan, (h,w))
# remap and write the target image (inverse tan function with normal map)
target = cv2.remap(img, map_x_inverse_tan, map_y, cv2.INTER_LINEAR)
cv2.imwrite("target.jpg", target)
# remap and write the attempted image (normal tan function with inverted map)
attempt = cv2.remap(img, xymap_inverted, None, cv2.INTER_LINEAR)
cv2.imwrite("attempt.jpg", attempt)
结果表明,尝试(带有倒置贴图的正常 tan 函数)在图像边缘附近的拉伸比预期要小。除了边缘之外,图像上几乎所有其他地方都是相同的。为了节省空间我没有贴原图
我已经尝试过那个
invert_map
程序。似乎有点容易振荡。
用这个代替:
def invert_map(F):
(h, w) = F.shape[:2] # (h, w, 2), "xymap"
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices((h,w)) # identity map
P = np.copy(I)
for i in range(10):
correction = I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
P += correction * 0.5
return P
我只是将修正值阻尼 0.5,这使得定点迭代更加温和,收敛速度也更快。
在我对你的 tan 贴图的实验中,我发现 5-10 次迭代已经足够好了,并且在进一步的迭代中没有进一步的进展。
我的探索的整个笔记本:https://gist.github.com/crackwitz/67f76f8a9eff21476b080c06d20660d0
干得好!我添加了一些额外的改进:
代码:
# invert remapping for cv2.remap (hwc order c0 == u, c1 == v)
def invert_remap(m0, steps_refine=8, dampening=[1.0,0.49], thr_inv=0.5):
ident0 = np.float32(np.dstack(np.indices((m0.shape[0], m0.shape[1]))[:,:,::-1])
m1 = np.copy(ident0)
for i in range(steps_refine):
diff0 = ident0 - cv2.remap(m0[:,:,:2], m1, None, interpolation=cv2.INTER_LINEAR)
if i>0 and i == steps_refine-1 and thr_inv >= 0: #deactivate invalids
m1[np.abs(diff0)>thr_inv] = -1.0
else:
m1 += diff0 * dampening[i%len(dampening)]
return m1
这应该与原始解决方案相同:
def invert_map(F):
return invert_remap(F, steps_refine=10, dampening=[0.5], thr_inv=-1)