Артефакт в преобразовании изображения с помощью фазового сдвига Фурье в NumPy - PullRequest
0 голосов
/ 25 декабря 2018

Следующий код создает артефакт при сдвиге изображений по сдвигу фазы Фурье:

Код самого сдвига фазы:

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() с теми же сменами.

enter image description here enter image description here

Что я делаю не так в приведенном выше коде, который создает этот артефакт?

[ОБНОВЛЕНИЕ] Один из комментариев предложил использовать 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() выше?

enter image description here

[ОБНОВЛЕНИЕ] Теперь, когда мы исключили мой 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()

И вывод выглядит так: enter image description here

[ОБНОВЛЕНИЕ] После ответа Криса я играл с другими методами интерполяции из opencv с логарифмическим масштабированием интенсивности, и я пришел к аналогичным выводам: артефакт действительно присутствует и с флагом Ланцоша в cv2.warpAffine() - хотя и оченьслабый - и кубический явно работает лучше для этого случая объектов с низкой выборкой (здесь, звезды):

enter image description here

Код, чтобы добраться до этого:

# 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 мм), и я наивно применил тот же алгоритм, который я использую для профессиональных, более крупных инструментов, где эта проблема не проявлялась.

Ответы [ 2 ]

0 голосов
/ 25 декабря 2018

Проблема здесь связана с тем, как отображается изображение, и с недостаточной выборкой изображения.Код правильный, но не подходит для изображения.

1.Undersampling

Изображение имеет очень резкие переходы.Некоторые звезды отображаются только в одном пикселе.Это отличительная черта недостаточной выборки.В правильно подобранном изображении единственная точка света (независимо от того, насколько она мала) выглядит как диск Эйри (в случае идеальной линзы) на изображении и должна занимать несколько пикселей, чтобы предотвратить наложение.

Я предполагаю, что изображение не может быть изменено, и оптимизировано для приложения.

Однако важно отметить, как изображение выбирается, чтобы иметь возможность выбрать соответствующие инструменты обработки изображения.

В этом случае переходы с пониженной дискретизацией означают, что интерполяция на основе Фурье не идеальна.

2.Интерполяция на основе Фурье

При сдвиге или масштабировании изображения через область Фурье используется синхрополярный интерполятор.Это идеальный интерполятор, соответствующий прямоугольному окну в области Фурье.Интерполятор sinc распространяется бесконечно (или, по крайней мере, до краев изображения) и затухает с 1 / x, что довольно медленно.Поэтому он не идеален в случае изображений с низкой частотой дискретизации.

Поскольку изображение с низкой частотой дискретизации имеет резкие переходы, sinc-интерполятор вызывает звон (как и многие другие интерполяторы).И из-за медленного затухания функции sinc это кольцо очень далеко.

Например, искусственный резкий переход на этом рисунке (синий) при интерполяции через область Фурье (красный) показывает сильный звонокэто очень далеко.Эта цифра контрастирует с другими интерполяторами, которые переносят звон на разные расстояния.

Plot showing the interpolation of a sharp transition, using various interpolators

3.Отображение изображения

Изображение отображается в вопросе, очень сильно растягивая контраст.Это предназначено для наблюдения тусклых звезд, но также сильно усиливает звон, вызванный резкими переходами у этих звезд.На графике выше представьте, что растягиваете и обрезаете ось Y, чтобы вы видели только область y=[0,0.01]Звон будет выглядеть как черно-белый узор.

4.Альтернативные интерполяторы

На графике выше показано влияние различных интерполяторов на резкий переход.Применительно к смещению изображения в вопросе, это результат:

Region from OP's image, shifted using different interpolators

Для трех методов в нижнем ряду звонок не наблюдаетсяпотому что это происходит в области, которая полностью насыщена на дисплее изображения.Использование другого диапазона значений серого на дисплее может также показать некоторые звуки здесь.

Все эти интерполяторы предназначены для аппроксимации идеального синкполяционного интерполятора, но с более коротким пространственным пространством, так что их дешевле вычислять.Следовательно, все они показывают некоторый сигнал при переходах с низкой частотой дискретизации.

Единственные интерполяторы, которые не вызывают вызов на острых кромках, - это линейная интерполяция и интерполяция ближайшего соседа.Подходят ли они для вашего приложения, зависит от приложения, я не могу сказать.


Это код, который я использовал для построения графика выше:

a = double((0:99)<50);
b = resample(a,20,0,'ft');
c = resample(a,20,0,'3-cubic');
d = resample(a,20,0,'lanczos8');
a = resample(a,20,0,'nn');
plot(a)
hold on
plot(b)
plot(c)
plot(d)
legend({'input','sinc','cubic','Lanczos (8)'})
set(gca,'xlim',[600,1400],'ylim',[-0.2,1.2])
set(gca,'fontsize',16)
set(gca,'linewidth',1)
set(get(gca,'children'),'linewidth',2)
set(gca,'Position',[0.07,0.11,0.9,0.815])

Функция resample находится в DIPimage , вы можете использовать imresize вместо этого, за исключением метода 'ft', который просто дополняет частотную область нулями, что приводит к интерполяции sinc.

0 голосов
/ 25 декабря 2018

Посмотрите на ndimage.fourier_shift, насколько я знаю, что не создает никаких артефактов.

...