我正在尝试对齐数组(图像)。由于非线性失真,这些数组无法共享同质坐标,因此affine transformation不够。
幸运的是,在数组之间找到匹配特征的坐标很简单,这些坐标用于计算初始仿射变换。我使用坐标对之间的残差来拟合Smooth Bivariate Splines,然后对x
和y
中依赖于位置的偏移量进行建模,以便将一个图像转换为另一个图像
将这些样条线与geometric_transform()配合使用时会出现问题-尽管对齐效果非常好,但速度非常慢(数组大小约为50M)。
我创建了一个样条,表示在x
和y
坐标中所需的移动(此处img1_coo是第一张图像img2_coo中由x
和y
坐标组成的Nx2数组对于第二张图片(仿射变换之后)是相同的:
from scipy import interpolate
sbs_x = interpolate.SmoothBivariateSpline(img1_coo[:,0], img1_coo[:,1], img1_coo[:,0]-img2_coo[:,0])
sbs_y = interpolate.SmoothBivariateSpline(img1_coo[:,0], img1_coo[:,1], img1_coo[:,1]-img2_coo[:,1])
geometric_transform()的可调用项为:
def apply_spline(xy):
return xy[0] - sbs_x.ev(xy[0], xy[1]), xy[1] - sbs_y.ev(xy[0], xy[1])
执行以下转换:
from scipy import ndimage
img2_data_splined = ndimage.geometric_transform(img2_data, apply_spline)
在50M阵列上大约需要10分钟。我看到使用50M大小的数组评估SmoothBivariateSpline.ev(x,y)
的速度非常快:
xx, yy = np.meshgrid(np.arange(8000), np.arange(6000))
%timeit sbs_x.ev(xx,yy)
6.78 s ± 43.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
因此,我猜想geometric_transform()
在缓慢地调用每个坐标方面很慢。为了可视化,这里是我正在校正的样条图(颜色从〜-1像素转变为+5像素偏移)
我尝试降低插值的顺序等,但是没有发现速度增加。欢迎加快geometric_transform()
的使用,或者是否还有其他实现来执行图像配准和处理复杂几何形状/变形的帮助?
(我曾经尝试过使用skimage.warp
来PolynomialTransform
,但是对齐效果不佳,并且对齐速度也很慢,但不如geometric_transform()
慢)
答案 0 :(得分:2)
因此,针对您的问题有两种解决方案,尽管可能只有一种可行的解决方案:
ndimage.map_coordinates
由于interpolate.SmoothBivariateSpline.ev
是矢量化的,因此在最终表达式中,您几乎已经完成了操作:
from scipy import ndimage as ndi
xx, yy = np.meshgrid(np.arange(8000), np.arange(6000))
coords_in_input = apply_spline((xx, yy))
img2_data_splined = ndi.map_coordinates(img2_data, coords_in_input)
(注意:您可能需要根据坐标约定进行一些移调。)
geometric_transform
需要一段时间的原因是calling functions in Python is slow。 Pauli Virtanen在SciPy中创建了LowLevelCallable接口,以确保可以在没有Python开销的情况下调用C / Cython / Numba函数。如果可以用C或Numba代码表示映射功能,则可以大大提高速度。 geometric_transform
docs告诉您所需的函数签名。这是一个简单的2D转换示例(示例:Kira Evans):
在Cython中:
#cython: boundscheck=False
#cython: wraparound=False
#cython: cdivision=True
#cython: nonecheck=False
#cython: initializedcheck=False
#cython: binding=False
#cython: infer_types=False
import numpy as np
cimport numpy as cnp
from libc.stdint cimport intptr_t
cdef api int shift(intptr_t* output_coords, double* input_coords,
int output_rank, int input_rank,
void* user_data):
cdef:
Py_ssize_t i
for i in range(output_rank):
input_coords[i] = <double> output_coords[i] - (<double*> user_data)[0]
return 1
然后,假设您已在Python中将Cython模块导入为mapping
,
# Don't declare the user_data array inline because
# .ctypes.get_as_parameter
# does not keep reference to the array
shift_amount = np.array([42], dtype=np.double)
shift_cy = LowLevelCallable.from_cython(mapping, name='shift',
user_data=shift_amount.ctypes.get_as_parameter())
现在您可以这样做:
shifted = ndi.geometric_transform(image, shift_cy)
表现不错。