Shift interpolation does not give expected behaviour

2024/10/12 10:24:44

When using scipy.ndimage.interpolation.shift to shift a numpy data array along one axis with periodic boundary treatment (mode = 'wrap'), I get an unexpected behavior. The routine tries to force the first pixel (index 0) to be identical to the last one (index N-1) instead of the "last plus one (index N)".

Minimal example:

# module import
import numpy as np
from scipy.ndimage.interpolation import shift
import matplotlib.pyplot as plt# print scipy.__version__
# 0.18.1a = range(10)plt.figure(figsize=(16,12))for i, shift_pix in enumerate(range(10)):# shift the data via spline interpolationb = shift(a, shift=shift_pix, mode='wrap')# plotting the dataplt.subplot(5,2,i+1)plt.plot(a, marker='o', label='data')plt.plot(np.roll(a, shift_pix), marker='o', label='data, roll')plt.plot(b, marker='o',label='shifted data')if i == 0:plt.legend(loc=4,fontsize=12)plt.ylim(-1,10)ax = plt.gca()ax.text(0.10,0.80,'shift %d pix' % i, transform=ax.transAxes)

Blue line: data before the shift
Green line: expected shift behavior
Red line: actual shift output of scipy.ndimage.interpolation.shift

Is there some error in how I call the function or how I understand its behavior with mode = 'wrap'? The current results are in contrast to the mode parameter description from the related scipy tutorial page and from another StackOverflow post. Is there an off-by-one-error in the code?

Scipy version used is 0.18.1, distributed in anaconda-2.2.0

enter image description here

Answer

It seems that the behaviour you have observed is intentional.

The cause of the problem lies in the C function map_coordinate which translates the coordinates after shift to ones before shift:

map_coordinate(double in, npy_intp len, int mode)

The function is used as the subroutine in NI_ZoomShift that does the actual shift. Its interesting part looks like this:

enter image description here

Example. Lets see how the output for output = shift(np.arange(10), shift=4, mode='wrap') (from the question) is computed.

NI_ZoomShift computes edge values output[0] and output[9] in some special way, so lets take a look at computation of output[1] (a bit simplified):

# input  =         [0,1,2,3,4,5,6,7,8,9]
# output = [ ,?, , , , , , , , ]          '?' == computed position
# shift  = 4
output_index = 1in  = output_index - shift    # -3
sz  = 10 - 1                  # 9
in += sz * ((-5 / 9) + 1)
#  +=  9 * ((     0) + 1) == 9
# in == 6return input[in]  # 6 

It is clear that sz = len - 1 is responsible for the behaviour you have observed. It was changed from sz = len in a suggestively named commit dating back to 2007: Fix off-by-on errors in ndimage boundary routines. Update tests.

I don't know why such change was introduced. One of the possible explanations that come to my mind is as follows:

Function 'shift' uses splines for interpolation. A knot vector of an uniform spline on interval [0, k] is simply [0,1,2,...,k]. When we say that the spline should wrap, it is natural to require equality on values for knots 0 and k, so that many copies of the spline could be glued together, forming a periodic function:

0--1--2--3-...-k              0--1--2--3-...-k              0--1-- ...0--1--2--3-...-k              0--1--2--3-...-k      ...

Maybe shift just treats its input as a list of values for spline's knots?

https://en.xdnf.cn/q/69664.html

Related Q&A

HEX decoding in Python 3.2

In Python 2.x Im able to do this:>>> 4f6c6567.decode(hex_codec) OlegBut in Python 3.2 I encounter this error:>>> b4f6c6567.decode(hex_codec) Traceback (most recent call last):File &qu…

How do I access session data in Jinja2 templates (Bottle framework on app engine)?

Im running the micro framework Bottle on Google App Engine. Im using Jinja2 for my templates. And Im using Beaker to handle the sessions. Im still a pretty big Python newbie and am pretty stoked I g…

What is a dimensional range of [-1,0] in Pytorch?

So Im struggling to understand some terminology about collections in Pytorch. I keep running into the same kinds of errors about the range of my tensors being incorrect, and when I try to Google for a …

Pyinstaller executable keeps opening

Background I am working on face recognition following this link and I would like to build a standalone application using Python. My main.py script looks like the following. # main.py# Import packages a…

Occasional deadlock in multiprocessing.Pool

I have N independent tasks that are executed in a multiprocessing.Pool of size os.cpu_count() (8 in my case), with maxtasksperchild=1 (i.e. a fresh worker process is created for each new task). The mai…

How to kill a subprocess called using subprocess.call in python? [duplicate]

This question already has answers here:How to terminate a python subprocess launched with shell=True(11 answers)Closed 7 years ago.I am calling a command using subprocess.call(cmd, shell=True)The comma…

Printing one color using imshow [closed]

Closed. This question needs debugging details. It is not currently accepting answers.Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to repro…

Send headers along in python [duplicate]

This question already has answers here:Changing user agent on urllib2.urlopen(9 answers)Closed 9 years ago.I have the following python script and I would like to send "fake" header informatio…

How to download image to memory using python requests?

Like here How to download image using requests , but to memory, using http://docs.python-requests.org.

Accessing NumPy record array columns in Cython

Im a relatively experienced Python programmer, but havent written any C in a very long time and am attempting to understand Cython. Im trying to write a Cython function that will operate on a column o…