def test_subpixel(self): anarr = np.zeros((4, 5)) anarr[2, 3] = 1 # The correspondence principle should hold first_guess = (2, 3) second_guess = utils._interpolate(anarr, first_guess, rad=1) np.testing.assert_equal(second_guess, (2, 3)) # Now something more meaningful anarr[2, 4] = 1 second_guess = utils._interpolate(anarr, first_guess, rad=1) np.testing.assert_almost_equal(second_guess, (2, 3.5))
def test_subpixel_edge(self): anarr = np.zeros((4, 5)) anarr[3, 0] = 1 anarr[3, 4] = 1 first_guess = (4, 0) second_guess = utils._interpolate(anarr, first_guess, rad=2) np.testing.assert_almost_equal(second_guess, (3, -0.5)) anarr[3, 0] += 1 anarr[0, 4] = 1 second_guess = utils._interpolate(anarr, first_guess, rad=2) np.testing.assert_almost_equal(second_guess, (3.25, -0.5))
def compute_motion_shifts(scan, template, in_place=True, num_threads=8): """ Compute shifts in y and x for rigid subpixel motion correction. Returns the number of pixels that each image in the scan was to the right (x_shift) or below (y_shift) the template. Negative shifts mean the image was to the left or above the template. :param np.array scan: 2 or 3-dimensional scan (image_height, image_width[, num_frames]). :param np.array template: 2-d template image. Each frame in scan is aligned to this. :param bool in_place: Whether the scan can be overwritten. :param int num_threads: Number of threads used for the ffts. :returns: (y_shifts, x_shifts) Two arrays (num_frames) with the y, x motion shifts. ..note:: Based in imreg_dft.translation(). """ import pyfftw from imreg_dft import utils # Add third dimension if scan is a single image if scan.ndim == 2: scan = np.expand_dims(scan, -1) # Get some params image_height, image_width, num_frames = scan.shape taper = np.outer(signal.tukey(image_height, 0.2), signal.tukey(image_width, 0.2)) # Prepare fftw frame = pyfftw.empty_aligned((image_height, image_width), dtype='complex64') fft = pyfftw.builders.fft2(frame, threads=num_threads, overwrite_input=in_place, avoid_copy=True) ifft = pyfftw.builders.ifft2(frame, threads=num_threads, overwrite_input=in_place, avoid_copy=True) # Get fourier transform of template template_freq = fft(template * taper).conj() # we only need the conjugate abs_template_freq = abs(template_freq) eps = abs_template_freq.max() * 1e-15 # Compute subpixel shifts per image y_shifts = np.empty(num_frames) x_shifts = np.empty(num_frames) for i in range(num_frames): # Compute correlation via cross power spectrum image_freq = fft(scan[:, :, i] * taper) cross_power = (image_freq * template_freq) / (abs(image_freq) * abs_template_freq + eps) shifted_cross_power = np.fft.fftshift(abs(ifft(cross_power))) # Get best shift shifts = np.unravel_index(np.argmax(shifted_cross_power), shifted_cross_power.shape) shifts = utils._interpolate(shifted_cross_power, shifts, rad=3) # Map back to deviations from center y_shifts[i] = shifts[0] - image_height // 2 x_shifts[i] = shifts[1] - image_width // 2 return y_shifts, x_shifts
def test_subpixel_crazy(self): anarr = np.zeros((4, 5)) first_guess = (0, 0) second_guess = utils._interpolate(anarr, first_guess, rad=2) np.testing.assert_array_less(second_guess, anarr.shape)