简体   繁体   English

在Python中以给定坐标将2D数据重新放置到较大的2D网格上

[英]Regrid 2D data onto larger 2D grid at given coordinates in Python

I have a square 2D array data that I would like to add to a larger 2D array frame at some given set of non-integer coordinates coords . 我有一个方形2D数组data ,我想在给定的一组非整数坐标coords处将其添加到更大的2D数组frame中。 The idea is that data will be interpolated onto frame with it's center at the new coordinates. 这个想法是将data以新坐标的中心插入到frame

Some toy data: 一些玩具数据:

# A gaussian to add to the frame
x, y = np.meshgrid(np.linspace(-1,1,10), np.linspace(-1,1,10))
data = 50*np.exp(-np.sqrt(x**2+y**2)**2)

# The frame to add the gaussian to
frame = np.random.normal(size=(100,50))

# The desired (x,y) location of the gaussian center on the new frame
coords = 23.4, 22.6

Here's the idea. 这是主意。 I want to add this: 我要添加:

高斯

to this: 对此:

在此处输入图片说明

to get this: 得到这个:

在此处输入图片说明

If the coordinates were integers (indexes), of course I could simply add them like this: 如果坐标是整数(索引),那么我当然可以像这样简单地添加它们:

frame[23:33,22:32] += data

But I want to be able to specify non-integer coordinates so that data is regridded and added to frame . 但我希望能够指定非整数坐标,以便将data重新注册并添加到frame

I've looked into PIL.Image methods but my use case is just for 2D data, not images. 我已经研究了PIL.Image方法,但是我的用例仅用于2D数据,而不是图像。 Is there a way to do this with just scipy ? 有没有办法做到这scipy Can this be done with interp2d or a similar function? 可以使用interp2d或类似的功能吗? Any guidance would be greatly appreciated! 任何指导将不胜感激!

Scipy's shift function from scipy.ndimage.interpolation is what you are looking for, as long as the grid spacings between data and frame overlap. 您正在寻找scipy.ndimage.interpolation Scipy shift功能,只要dataframe之间的网格间距重叠即可。 If not, look to the other answer. 如果不是,请寻找其他答案。 The shift function can take floating point numbers as input and will do a spline interpolation. shift功能可以将浮点数作为输入,并进行样条插值。 First, I put the data into an array as large as frame, then shift it, and then add it. 首先,我将数据放入与框架一样大的数组中,然后将其移位,然后添加。 Make sure to reverse the coordinate list, as x is the rightmost dimension in numpy arrays. 确保反转坐标列表,因为xnumpy数组中最右边的维。 One of the nice features of shift is that it sets to zero those values that go out of bounds. shift的一个不错的功能之一就是将超出范围的值设置为零。

import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage.interpolation import shift

# A gaussian to add to the frame.
x, y = np.meshgrid(np.linspace(-1,1,10), np.linspace(-1,1,10))
data = 50*np.exp(-np.sqrt(x**2+y**2)**2)

# The frame to add the gaussian to
frame = np.random.normal(size=(100,50))
x_frame = np.arange(50)
y_frame = np.arange(100)

# The desired (x,y) location of the gaussian center on the new frame.
coords = np.array([23.4, 22.6])

# First, create a frame as large as the frame.
data_large = np.zeros(frame.shape)
data_large[:data.shape[0], :data.shape[1]] = data[:,:]

# Subtract half the distance as the bottom left is at 0,0 instead of the center.
# The shift of 4.5 is because data is 10 points wide.
# Reverse the coords array as x is the last coordinate.
coords_shift = -4.5
data_large = shift(data_large, coords[::-1] + coords_shift)

frame += data_large

# Plot the result and add lines to indicate to coordinates
plt.figure()
plt.pcolormesh(x_frame, y_frame, frame, cmap=plt.cm.jet)
plt.axhline(coords[1], color='w')
plt.axvline(coords[0], color='w')
plt.colorbar()
plt.gca().invert_yaxis()
plt.show()

The script gives you the following figure, which has the desired coordinates indicated with white dotted lines. 该脚本为您提供下图,该图具有用白色虚线表示的所需坐标。

插值结果

One possible solution is to use scipy.interpolate.RectBivariateSpline . 一种可能的解决方案是使用scipy.interpolate.RectBivariateSpline In the code below, x_0 and y_0 are the coordinates of a feature from data (ie, the position of the center of the Gaussian in your example) that need to be mapped to the coordinates given by coords . 在下面的代码中, x_0y_0是数据中要素的坐标(例如,您的示例中高斯中心的位置),需要将其映射到coords给出的coords There are a couple of advantages to this approach: 这种方法有两个优点:

  1. If you need to "place" the same object into multiple locations in the output frame , the spline needs to be computed only once (but evaluated multiple times). 如果需要将同一对象“放置”到输出frame中的多个位置,则样条线只需要计算一次(但是需​​要多次评估)。

  2. In case you actually need to compute integrated flux of the model over a pixel, you can use the integral method of scipy.interpolate.RectBivariateSpline . 如果您实际上需要计算模型在一个像素上的积分通量,则可以使用scipy.interpolate.RectBivariateSplineintegral方法

Resample using spline interpolation: 使用样条插值重新采样:

from scipy.interpolate import RectBivariateSpline
x = np.arange(data.shape[1], dtype=np.float)
y = np.arange(data.shape[0], dtype=np.float)
kx = 3; ky = 3; # spline degree
spline = RectBivariateSpline(
    x, y, data.T, kx=kx, ky=ky, s=0
)

# Define coordinates of a feature in the data array.
# This can be the center of the Gaussian:
x_0 = (data.shape[1] - 1.0) / 2.0
y_0 = (data.shape[0] - 1.0) / 2.0

# create output grid, shifted as necessary:
yg, xg = np.indices(frame.shape, dtype=np.float64)
xg += x_0 - coords[0] # see below how to account for pixel scale change
yg += y_0 - coords[1] # see below how to account for pixel scale change

# resample and fill extrapolated points with 0:
resampled_data = spline.ev(xg, yg)
extrapol = (((xg < -0.5) | (xg >= data.shape[1] - 0.5)) |
            ((yg < -0.5) | (yg >= data.shape[0] - 0.5)))
resampled_data[extrapol] = 0

Now plot the frame and resampled data : 现在绘制frame并重新采样data

plt.figure(figsize=(14, 14));
plt.imshow(frame+resampled_data, cmap=plt.cm.jet,
          origin='upper', interpolation='none', aspect='equal')
plt.show()

在此处输入图片说明

If you also want to allow for scale changes, then replace code for computing xg and yg above with: 如果您还希望允许比例更改,则将上面用于计算xgyg代码替换为:

coords = 20, 80 # change coords to easily identifiable (in plot) values
zoom_x = 2 # example scale change along X axis
zoom_y = 3 # example scale change along Y axis
yg, xg = np.indices(frame.shape, dtype=np.float64)
xg = (xg - coords[0]) / zoom_x + x_0
yg = (yg - coords[1]) / zoom_y + y_0

在此处输入图片说明

Most likely this is what you actually want based on your example. 根据示例,这很可能是您真正想要的。 Specifically, the coordinates of pixels in data are "spaced" by 0.222(2) distance units. 具体而言, data中像素的坐标以0.222(2)个距离单位“间隔开”。 Therefore it actually seems that for your particular example (whether accidental or intentional), you have a zoom factor of 0.222(2). 因此,实际上,对于您的特定示例(无论是偶然的还是有意的),您的缩放系数为0.222(2)。 In that case your data image would shrink to almost 2 pixels in the output frame. 在这种情况下,您的data图像在输出帧中将缩小到几乎2个像素。


Comparison to @Chiel answer @Chiel答案的比较

In the image below, I compare the results from my method (left), @Chiel 's method (center) and difference (right panel): 在下图中,我比较了我的方法(左), @Chiel的方法(中)和差异(右图)的结果:

在此处输入图片说明

Fundamentally, the two methods are quite similar and possibly even use the same algorithm (I did not look at the code for shift but based on the description - it also uses splines). 从根本上讲,这两种方法非常相似,甚至可能使用相同的算法(我没有查看shift代码,而是基于描述-它也使用样条线)。 From comparison image it is visible that the biggest differences are at the edges and, for unknown to me reasons, shift seems to truncate the shifted image slightly too soon. 从比较图像中可以看出,最大的差异在边缘,并且由于我所不知道的原因, shift似乎太快地截断了移位的图像。

I think the biggest difference is that my method allows for pixel scale changes and it also allows re-use of the same interpolator to place the original image at different locations in the output frame. 我认为最大的不同是,我的方法可以改变像素比例,并且还可以重复使用相同的插值器将原始图像放置在输出帧的不同位置。 @Chiel 's method is somewhat simpler but (what I did not like about it is that) it requires creation of a larger array ( data_large ) into which the original image is placed in the corner. @Chiel的方法稍微简单一些,但是(我不喜欢的是)它需要创建一个更大的数组( data_large ),将原始图像放置在其中。

While the other answers have gone into detail, but here's my lazy solution: 虽然其他答案已经详细介绍了,但这是我的懒惰解决方案:

xc,yc = 23.4, 22.6
x, y = np.meshgrid(np.linspace(-1,1,10)-xc%1, np.linspace(-1,1,10)-yc%1)
data = 50*np.exp(-np.sqrt(x**2+y**2)**2)
frame = np.random.normal(size=(100,50))
frame[23:33,22:32] += data

And it's the way you liked it. 这就是您喜欢它的方式。 As you mentioned, the coordinates of both are the same, so the origin of data is somewhere between the indices. 正如您提到的,两者的坐标是相同的,因此data的原点在索引之间。 Now just simply shift it by the amount you want it to be off a grid point (remainder to one) in the second line and you're good to go (you might need to flip the sign, but I think this is correct). 现在只需简单地将其偏移第二行中您希望其偏离网格点的数量(保留为一个),就可以了(您可能需要翻转符号,但我认为这是正确的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM