Следующий код создает артефакт при сдвиге изображений по сдвигу фазы Фурье:
Код самого сдвига фазы:
def phase_shift(fimage, dx, dy):
# Shift the phase of the fourier transform of an image
dims = fimage.shape
x, y = np.meshgrid(np.arange(-dims[1] / 2, dims[1] / 2), np.arange(-dims[0] / 2, dims[0] / 2))
kx = -1j * 2 * np.pi * x / dims[1]
ky = -1j * 2 * np.pi * y / dims[0]
shifted_fimage = fimage * np.exp(-(kx * dx + ky * dy))
return shifted_fimage
Использование для фактического сдвига изображения и получениясмещенное изображение:
def translate_by_phase_shift(image, dx, dy):
# Get the fourier transform
fimage = np.fft.fftshift(np.fft.fftn(image))
# Phase shift
shifted_fimage = phase_shift(fimage, dx, dy)
# Inverse transform -> translated image
shifted_image = np.real(np.fft.ifftn(np.fft.ifftshift(shifted_fimage)))
return shifted_image
Артефакт показан на изображениях ниже (изображение имеет четные размеры).Верхний ряд - это контекст (все изображение), нижний - крупный план в красном прямоугольнике.Слева: контрольное изображение.Середина: сдвинуто с указанным кодом и подвержено артефакту.Справа: как это выглядит при использовании cv2.warpAffine()
с теми же сменами.
Что я делаю не так в приведенном выше коде, который создает этот артефакт?
[ОБНОВЛЕНИЕ] Один из комментариев предложил использовать scipy.ndimage.fourier.fourier_shift()
.Поэтому я сделал именно это:
fourier_shifted_image = fourier_shift(np.fft.fftn(image), shift)
shifted_image = np.fft.ifftn(fourier_shifted_image)
и нанес на график действительную часть (shifted_image.real
)
Фактически, он также производит точно такой же артефакт (см. Изображение ниже, с правой стороны).), что, я полагаю, исключает ошибку в моем пользовательском коде phase_shift()
выше?
[ОБНОВЛЕНИЕ] Теперь, когда мы исключили мой phase_shift (), вот воспроизводимый код, при условии, что вы загружаете массив изображений отсюда: https://www.dropbox.com/s/dmbv56xfqkv8qqz/image.npy?dl=0
import os
import numpy as np
import matplotlib
matplotlib.use('TKAgg')
import matplotlib.pyplot as plt
from scipy.ndimage.fourier import fourier_shift
# Load the image (update path according to your case)
image = np.load(os.path.expanduser('~/DDS/46P_Wirtanen/image.npy'))
# Shift vector
shift = np.array([-3.75, -7.5 ])
# Phase-shift
fourier_shifted_image = fourier_shift(np.fft.fftn(image), shift)
shifted_image = np.fft.ifftn(fourier_shifted_image)
interp_method = 'hanning'
zoomfov = [1525, 1750, 1010, 1225]
vmin = np.percentile(image, 0.1)
vmax = np.percentile(image, 99.8)
fig, ax = plt.subplots(1,2, figsize=(14, 6), sharex=True,sharey=True)
ax[0].imshow(image, origin='lower', cmap='gray', vmin=vmin, vmax=vmax, interpolation=interp_method)
ax[0].set_title('Original image')
ax[1].imshow(shifted_image.real, origin='lower', cmap='gray', vmin=vmin, vmax=vmax, interpolation=interp_method)
ax[1].set_title('with scipy.ndimage.fourier.fourier_shift()')
plt.axis(zoomfov)
plt.tight_layout()
plt.show()
И вывод выглядит так:
[ОБНОВЛЕНИЕ] После ответа Криса я играл с другими методами интерполяции из opencv с логарифмическим масштабированием интенсивности, и я пришел к аналогичным выводам: артефакт действительно присутствует и с флагом Ланцоша в cv2.warpAffine()
- хотя и оченьслабый - и кубический явно работает лучше для этого случая объектов с низкой выборкой (здесь, звезды):
Код, чтобы добраться до этого:
# Compare interpolation methods
import cv2
# Fourier phase shift.
fourier_shifted = fourier_shift(np.fft.fftn(image), shift)
fourier_shifted_image = np.fft.ifftn(fourier_shifted).real
# Use opencv
Mtrans = np.float32([[1,0,shift[1]],[0,1, shift[0]]])
shifted_image_cubic = cv2.warpAffine(image, Mtrans, image.shape[::-1], flags=cv2.INTER_CUBIC)
shifted_image_lanczos = cv2.warpAffine(image, Mtrans, image.shape[::-1], flags=cv2.INTER_LANCZOS4)
zoomfov = [1525, 1750, 1010, 1225]
pmin = 2
pmax = 99.999
fig, ax = plt.subplots(1,3, figsize=(19, 7), sharex=True,sharey=True)
ax[0].imshow(fourier_shifted_image, origin='lower', cmap='gray',
vmin=np.percentile(fourier_shifted_image, pmin), vmax=np.percentile(fourier_shifted_image, pmax),
interpolation=interp_method, norm=LogNorm())
add_rectangle(zoomfov, ax[0])
ax[0].set_title('shifted with Fourier phase shift')
ax[1].imshow(shifted_image_cubic, origin='lower', cmap='gray',
vmin=np.percentile(shifted_image_cubic, pmin), vmax=np.percentile(shifted_image_cubic, pmax),
interpolation=interp_method, norm=LogNorm())
add_rectangle(zoomfov, ax[1])
ax[1].set_title('with cv2.warpAffine(...,flags=cv2.INTER_CUBIC)')
ax[2].imshow(shifted_image_lanczos, origin='lower', cmap='gray',
vmin=np.percentile(shifted_image_lanczos, pmin), vmax=np.percentile(shifted_image_lanczos, pmax),
interpolation=interp_method, norm=LogNorm())
#ax[2].imshow(shifted_image.real, origin='lower', cmap='gray', vmin=np.percentile(Llights_prep[frame], pmin), vmax=np.percentile(Llights_prep[frame], pmax), interpolation=interp_method)
add_rectangle(zoomfov, ax[2])
ax[2].set_title('with cv2.warpAffine(...,flags=cv2.INTER_LANCZOS4) ')
plt.axis(zoomfov)
plt.tight_layout()
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.1, hspace=None)
plt.show()
И ответить на вопросы Криса, действительно, недопустимоЗахваченные звезды, конечно же, неизбежны с нашими скромными любительскими системами обработки изображений (плохой диаметр 130 мм), и я наивно применил тот же алгоритм, который я использую для профессиональных, более крупных инструментов, где эта проблема не проявлялась.