【问题标题】:Reprojecting polar to cartesian grid将极坐标重新投影到笛卡尔网格
【发布时间】:2011-01-10 23:55:15
【问题描述】:

我有一个极坐标 (r,theta) 网格(这意味着每个单元都是一个环形部分),其中包含一些物理量(例如温度)的值,我想重新网格(或重新投影,或重新采样)这些值到笛卡尔网格上。是否有任何 Python 包可以做到这一点?

我对将单元格中心的坐标从极坐标转换为笛卡尔坐标不感兴趣 - 这很容易。相反,我正在寻找一个可以真正正确地重新网格化数据的包。

感谢您的任何建议!

【问题讨论】:

  • 这不是一个简单的问题,写起来既有趣又大熊。我想我需要 2 到 3 天的时间才能想出一些效率极低的东西。

标签: python image


【解决方案1】:

感谢您的回答 - 经过更多思考后,我想出了以下代码:

import numpy as np

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as mpl

from scipy.interpolate import interp1d
from scipy.ndimage import map_coordinates


def polar2cartesian(r, t, grid, x, y, order=3):

    X, Y = np.meshgrid(x, y)

    new_r = np.sqrt(X*X+Y*Y)
    new_t = np.arctan2(X, Y)

    ir = interp1d(r, np.arange(len(r)), bounds_error=False)
    it = interp1d(t, np.arange(len(t)))

    new_ir = ir(new_r.ravel())
    new_it = it(new_t.ravel())

    new_ir[new_r.ravel() > r.max()] = len(r)-1
    new_ir[new_r.ravel() < r.min()] = 0

    return map_coordinates(grid, np.array([new_ir, new_it]),
                            order=order).reshape(new_r.shape)

# Define original polar grid

nr = 10
nt = 10

r = np.linspace(1, 100, nr)
t = np.linspace(0., np.pi, nt)
z = np.random.random((nr, nt))

# Define new cartesian grid

nx = 100
ny = 200

x = np.linspace(0., 100., nx)
y = np.linspace(-100., 100., ny)

# Interpolate polar grid to cartesian grid (nearest neighbor)

fig = mpl.figure()
ax = fig.add_subplot(111)
ax.imshow(polar2cartesian(r, t, z, x, y, order=0), interpolation='nearest')
fig.savefig('test1.png')

# Interpolate polar grid to cartesian grid (cubic spline)

fig = mpl.figure()
ax = fig.add_subplot(111)
ax.imshow(polar2cartesian(r, t, z, x, y, order=3), interpolation='nearest')
fig.savefig('test2.png')

这不是严格的重新网格化,但可以满足我的需要。只是发布代码以防它对其他人有用。欢迎提出改进建议!

【讨论】:

  • 只是一个小小的修正。我想,你的代码中应该是 arctan2(Y, X)。
【解决方案2】:

您可以使用scipy.ndimage.geometric_transform 更简洁地执行此操作。下面是一些示例代码:

import numpy as N
import scipy as S
import scipy.ndimage

temperature = <whatever> 
# This is the data in your polar grid.
# The 0th and 1st axes correspond to r and θ, respectively.
# For the sake of simplicity, θ goes from 0 to 2π, 
# and r's units are just its indices.

def polar2cartesian(outcoords, inputshape, origin):
    """Coordinate transform for converting a polar array to Cartesian coordinates. 
    inputshape is a tuple containing the shape of the polar array. origin is a
    tuple containing the x and y indices of where the origin should be in the
    output array."""

    xindex, yindex = outcoords
    x0, y0 = origin
    x = xindex - x0
    y = yindex - y0

    r = N.sqrt(x**2 + y**2)
    theta = N.arctan2(y, x)
    theta_index = N.round((theta + N.pi) * inputshape[1] / (2 * N.pi))

    return (r,theta_index)

temperature_cartesian = S.ndimage.geometric_transform(temperature, polar2cartesian, 
    order=0,
    output_shape = (temperature.shape[0] * 2, temperature.shape[0] * 2),
    extra_keywords = {'inputshape':temperature.shape,
        'center':(temperature.shape[0], temperature.shape[0])})

您可以根据需要更改order=0 以获得更好的插值。输出数组temperature_cartesian 在这里是 2r x 2r,但你可以指定任何你喜欢的大小和原点。

【讨论】:

    【解决方案3】:

    前段时间,当我尝试做类似的事情时,我来到这篇文章,即将极地数据重新投影到笛卡尔网格中,反之亦然。这里提出的解决方案效果很好。但是,执行坐标变换需要一些时间。我只是想分享另一种方法,它可以将处理时间减少多达 50 倍或更多。

    算法使用scipy.ndimage.interpolation.map_coordinates函数。

    让我们看一个小例子:

    import numpy as np
    
    # Auxiliary function to map polar data to a cartesian plane
    def polar_to_cart(polar_data, theta_step, range_step, x, y, order=3):
    
        from scipy.ndimage.interpolation import map_coordinates as mp
    
        # "x" and "y" are numpy arrays with the desired cartesian coordinates
        # we make a meshgrid with them
        X, Y = np.meshgrid(x, y)
    
        # Now that we have the X and Y coordinates of each point in the output plane
        # we can calculate their corresponding theta and range
        Tc = np.degrees(np.arctan2(Y, X)).ravel()
        Rc = (np.sqrt(X**2 + Y**2)).ravel()
    
        # Negative angles are corrected
        Tc[Tc < 0] = 360 + Tc[Tc < 0]
    
        # Using the known theta and range steps, the coordinates are mapped to
        # those of the data grid
        Tc = Tc / theta_step
        Rc = Rc / range_step
    
        # An array of polar coordinates is created stacking the previous arrays
        coords = np.vstack((Ac, Sc))
    
        # To avoid holes in the 360º - 0º boundary, the last column of the data
        # copied in the begining
        polar_data = np.vstack((polar_data, polar_data[-1,:]))
    
        # The data is mapped to the new coordinates
        # Values outside range are substituted with nans
        cart_data = mp(polar_data, coords, order=order, mode='constant', cval=np.nan)
    
        # The data is reshaped and returned
        return(cart_data.reshape(len(y), len(x)).T)
    
    polar_data = ... # Here a 2D array of data is assumed, with shape thetas x ranges
    
    # We create the x and y axes of the output cartesian data
    x = y = np.arange(-100000, 100000, 1000)
    
    # We call the mapping function assuming 1 degree of theta step and 500 meters of
    # range step. The default order of 3 is used.
    cart_data = polar_to_cart(polar_data, 1, 500, x, y)
    

    我希望这对和我有同样情况的人有所帮助。

    【讨论】:

    • 太棒了! ...较小的修正是坐标应该由 Tc 和 Rc 构建,而不是 Ac 和 Sc
    【解决方案4】:

    是否有任何 Python 包可以做到这一点?

    是的!现在至少有一个 Python 包具有将矩阵从笛卡尔坐标重新映射到极坐标的功能:abel.tools.polar.reproject_image_into_polar(),它是PyAbel package 的一部分。

    (Iñigo Hernáez Corres 是正确的,scipy.ndimage.interpolation.map_coordinates 是迄今为止我们发现的从笛卡尔坐标重新投影到极坐标的最快方法。)

    可以从PyPi 安装 PyAbel,方法是在命令行输入以下内容:

    pip install pyabel
    

    然后,在python中,您可以使用以下代码将图像重新投影到极坐标中:

    import abel
    abel.tools.polar.reproject_image_into_polar(MyImage)
    

    [根据应用程序,您可能会考虑传递 jacobian=True 参数,该参数会重新缩放矩阵的强度,以考虑在您从笛卡尔坐标转换为极坐标。]

    这是一个完整的例子:

    import numpy as np
    import matplotlib.pyplot as plt
    import abel
    
    CartImage = abel.tools.analytical.sample_image(501)[201:-200, 201:-200]
    
    PolarImage, r_grid, theta_grid = abel.tools.polar.reproject_image_into_polar(CartImage)
    
    fig, axs = plt.subplots(1,2, figsize=(7,3.5))
    axs[0].imshow(CartImage , aspect='auto', origin='lower')
    axs[1].imshow(PolarImage, aspect='auto', origin='lower', 
                  extent=(np.min(theta_grid), np.max(theta_grid), np.min(r_grid), np.max(r_grid)))
    
    axs[0].set_title('Cartesian')
    axs[0].set_xlabel('x')
    axs[0].set_ylabel('y')
    
    axs[1].set_title('Polar')
    axs[1].set_xlabel('Theta')
    axs[1].set_ylabel('r')
    
    plt.tight_layout()
    plt.show()
    

    注意:SO 上有另一个很好的讨论(关于将彩色图像重新映射到极坐标):image information along a polar coordinate system

    【讨论】:

    • 这是一个很好的例子。但是它在 python3.4 上给了我TypeError: 'numpy.float64' object cannot be interpreted as an integer 。如果你是代码的维护者,你应该检查一下。
    • 您的代码似乎在索引网格上转换图像,其中原点始终位于像素上。有时,原点需要位于相邻像素之间。您示例中的圆圈在转换后变为非直线,部分原因是左侧的圆圈不在图像中居中,部分原因是原点移动了半个像素。您可以尝试使用相同的数据,将圆置于中心 abel.tools.analytical.SampleImage(501).image[200:-200, 200:-200],然后将其插值到 100x100 网格(原为 101x101),然后进行转换。
    【解决方案5】:

    OpenCV 3.4 现在可以通过 warpPolar() 轻松做到这一点

    调用非常简单:

    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    
    # Read in our image from disk
    image = cv2.imread('washington_quarter.png',0)
    plt.imshow(image),plt.show()
    

    margin = 0.9 # Cut off the outer 10% of the image
    # Do the polar rotation along 1024 angular steps with a radius of 256 pixels.
    polar_img = cv2.warpPolar(image, (256, 1024), (image.shape[0]/2,image.shape[1]/2), image.shape[1]*margin*0.5, cv2.WARP_POLAR_LINEAR)
    # Rotate it sideways to be more visually pleasing
    polar_img = cv2.rotate(polar_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
    plt.imshow(polar_img),plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多