Я пытаюсь выровнять массивы (изображения). Массивы не разделяют однородные координаты из-за нелинейных искажений, и поэтому такого аффинного преобразования недостаточно.
К счастью, тривиально найти координаты совпадающих признаков между массивами, и они были использованы для вычисления начального аффинного преобразования. Я использовал остаточные различия между координатными парами для соответствия Smooth Bivariate Splines, которые затем моделируют зависящее от позиции смещение по x
и y
которое необходимо применить, чтобы преобразовать одно изображение в другое.
Проблема возникает при использовании этих сплайнов с геометрическим_транспортом() - хотя результирующие выравнивания превосходны, это очень медленно (массивы размером ~ 50 М).
Я создаю сплайн, представляющий сдвиги, требуемые в координатах x
и y
(здесь img1_coo является массивом Nx2 координат x
и y
в первом изображении, img2_coo одинаково для второго изображения (после аффинного преобразования):
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)
Это занимает ~ 10 минут на массиве 50M. Я вижу, что оценка SmoothBivariateSpline.ev(x,y)
с массивом размера 50M выполняется очень быстро:
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()
)
Итак, есть два решения для вашей проблемы, хотя потенциально только одно выполнимое:
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
занимает некоторое время, заключается в том, что вызывающие функции в Python медленны. Паули Виртанен создал интерфейс LowLevelCallable в SciPy, чтобы гарантировать, что функции C/Cython/Numba можно вызвать без накладных расходов Python. Если вы можете выразить свою функцию сопоставления в коде C или Numba, вы можете получить большие ускорения. Документы geometric_transform
сообщают вам требуемую подпись функции. Вот простой пример (кредит: Кира Эванс) для простого двумерного сдвига:
В Китоне:
#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
Затем, предположив, что вы импортировали модуль Cython в качестве mapping
в Python:
# 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)
с достойной производительностью.
Вы также можете использовать Numba для этого, что может быть более или менее привлекательным в зависимости от вашего варианта использования. См. Здесь, здесь и здесь для получения дополнительной информации.
map_coordinates
в функцииapply_spline
но, конечно, стоимость вызова функции 50M раз была все еще там. Ваш первый ответ решил мою проблему (сейчас занимает ~ 10-15 секунд!) Мне нравится идея ответа 2, я могу рассмотреть в будущем.