以下代码通过傅立叶相移来移动图像时会产生伪像:
相移本身的代码是:
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()
[更新]
收到Cris的答复后,我使用了opencv的其他插值方法,并进行了强度的对数缩放,得出了类似的结论:cv2.warpAffine()
中的Lanczos标志确实也出现了伪像-尽管非常微弱-并且对于这种欠采样对象(此处为星星)的情况,三次方显然更有效:
要获得的代码:
# 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毫米)的确无法避免采样不足的恒星,而且我天真地采用了与用于大型专业仪器的算法相同的算法问题没有显示。
答案 0 :(得分:2)
此处的问题与图像的显示方式以及图像的欠采样有关。该代码正确,但是不适合该图像。
图像有一些非常清晰的过渡。有些星星仅在一个像素中显示。这是欠采样的标志。在正确采样的图像中,单个光点(无论多小)都会在图像中显示为艾里斑(在理想透镜的情况下),并且应占据几个像素以防止混叠。
我假设成像无法更改,并且已针对应用进行了优化。
但是,重要的是要注意如何对图像进行采样,以便能够选择合适的图像处理工具。
在这种情况下,采样不足的过渡意味着基于傅立叶的插值并不理想。
在通过傅立叶域移动或缩放图像时,将使用正弦内插器。这是理想的插值器,对应于傅立叶域中的矩形窗口。 Sinc内插器无限扩展(或至少延伸到图像的边缘),并以1 / x衰减,这非常慢。因此,在欠采样图像的情况下,这是不理想的。
由于欠采样图像具有清晰的过渡,因此正弦内插器会引起振铃(许多其他内插器也一样)。而且由于Sinc函数的衰减缓慢,因此这种振铃进行得很远。
例如,当通过傅立叶域(红色)进行插值时,该图中的人工尖锐跃迁(蓝色)显示出很强的振铃效果,并且非常远。该图与将振铃带到不同距离的其他内插器形成对比。
通过非常强烈地拉伸对比度,图像可以显示在问题中。这意味着可以观察昏暗的恒星,但也可以大大增强引起这些恒星急剧跃迁的振铃。在上图中,想象一下拉伸和裁剪y轴,以便仅看到区域y=[0,0.01]
。铃声看起来像黑白图案。
上图显示了不同插值器对急剧过渡的影响。当用于平移问题中的图像时,结果如下:
对于底行的三种方法,由于在图像显示中完全饱和的区域中发生振铃,因此无法观察到振铃。在显示屏中使用不同范围的灰度值可能还会在此处显示一些振铃。
所有这些内插器的设计均近似于理想的Sinc内插器,但空间占用空间较短,因此计算成本较低。因此,它们都在欠采样过渡处显示出一些振铃。
不引起尖锐边缘振铃的唯一内插器是线性内插和最近邻插值。我不能说这些是否适合您的应用程序。
这是我用来制作上图的代码:
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插值。
答案 1 :(得分:1)
看看ndimage.fourier_shift,据我所知这不会产生任何伪像。