Как сдвинуть массив изображений с префиксом supixel в python?

Я пытаюсь сместить 2D-массив, представляющий изображение с субпиксельной точностью, используя 2D-БПФ и теорему о преобразовании Фурье. Это хорошо работает, когда значение сдвига в целых числах (точность пикселей), однако я получаю много артефактов, когда значение сдвига не является целым числом, то есть долей пикселя. Код ниже:

import numpy as np
from scipy.fftpack import fftfreq
def shift_fft(input_array,shift):
    shift_rows,shift_cols = shift
    nr,nc = input_array.shape
    Nr, Nc = fftfreq(nr), fftfreq(nc)
    Nc,Nr = np.meshgrid(Nc,Nr)
    fft_inputarray = np.fft.fft2(input_array)
    fourier_shift = np.exp(1j*2*np.pi*((shift_rows*Nr)+(shift_cols*Nc)))
    output_array = np.fft.ifft2(fft_inputarray*fourier_shift)
    return np.real(output_array)

Таким образом, shift_fft(input_array,[2,0]) будет работать, но shift_fft(input_array,[2.4,0]) не будет работать без артефактов. Что я делаю не так? Например, рассматривая изображение Лены с разрешением 128х128 пикселей. Если я хочу сдвинуться на 10,4 пикселя в каждом направлении, я получу некоторую колебательную модуляцию изображения. Изображения следующие:

До:

Лена, до смены

После:

Лена, после смены

2 ответа

Вы можете попробовать использовать scipy.ndimage.shift. Сдвигает пиксели аналогичноnumpy.roll, но также допускает дробное смещение значений с интерполяцией.

Для цветного изображения убедитесь, что для третьей оси (каналов) задан сдвиг 0.

import scipy.ndimage

scipy.ndimage.shift(input_array, (2.4, 0))

По умолчанию он устанавливает черный фон, но вы можете настроить режим, чтобы он был обтекаемым или имел собственный цвет.

По какой-то причине я обнаружил, что сдвиг scipy ndimage очень медленный, особенно для n-мерных изображений. Для смещения по определенной оси, для целочисленных и нецелочисленных сдвигов я создал простенькую функцию:

      def shift_img_along_axis( img, axis=0, shift = 1 , constant_values=0):
    """ shift array along a specific axis. New value is taken as weighted by the two distances to the assocaited original pixels.
    NOTE: at the border of image, when not enough original pixel is accessible, data will be meaned with regard to additional constant_values. 
    constant_values: value to set to pixels with no association in original image img 
    RETURNS : shifted image. 
    A.Mau. """
    intshift = int(shift)
    remain0 = abs( shift - int(shift) )
    remain1 = 1-remain0 #if shift is uint : remain1=1 and remain0 =0
    npad = int( np.ceil( abs( shift ) ) )  #ceil relative to 0. ( 0.5=> 1 and -0.5=> -1 )

    pad_arg = [(0,0)]*img.ndim
    pad_arg[axis] = (npad,npad)
    bigger_image = np.pad( img, pad_arg, 'constant', constant_values=constant_values) 
    
    part1 = remain1*bigger_image.take( np.arange(npad+intshift, npad+intshift+img.shape[axis]) ,axis)
    if remain0==0:
        shifted = part1
    else:
        if shift>0:
            part0 = remain0*bigger_image.take( np.arange(npad+intshift+1, npad+intshift+1+img.shape[axis]) ,axis) #
        else:
            part0 = remain0*bigger_image.take( np.arange(npad+intshift-1, npad+intshift-1+img.shape[axis]) ,axis) #

        shifted = part0 + part1
    return shifted

Быстрый пример:

      np.random.seed(1)
img = np.random.uniform(0,10,(3,4)).astype('int')
print( img ) 
shift = 1.5
shifted = shift_img_along_axis( img, axis=1, shift=shift )
print( shifted )

Печать изображения:

      [[4 7 0 3]
 [1 0 1 3]
 [3 5 4 6]]

Сдвинутое изображение:

      [[3.5 1.5 1.5 0. ]
 [0.5 2.  1.5 0. ]
 [4.5 5.  3.  0. ]]

С нашим смещением 1,5 первое значение в смещенном изображении является средним значением 7 и 0 и т. д. Если значение отсутствует в исходном изображении, будет взято дополнительное значение 0. Если вы хотите получить результат, аналогичный np.roll (изображение возвращается на другую сторону...), вам придется немного изменить его!

Другие вопросы по тегам