web-dev-qa-db-ja.com

グリッドデータの高速補間

通常のグリッド方式でボリューム上でサンプリングされた物理変数を表すデータの大きな3dnp.ndarrayがあります(array [0,0,0]の値は、物理座標(0,0,0)での値を表します)。 ))。

大まかなグリッドのデータを補間して、グリッド間隔を細かくしたいと思います。現時点では、scipy griddata線形補間を使用していますが、かなり遅いです(20x20x20配列の場合は約90秒)。それは私の目的のために少し過剰に設計されており、ボリュームデータのランダムサンプリングを可能にします。一定間隔のデータと、補間したい特定のポイントのセットが限られているという事実を利用できるものはありますか?

19
Rowan

承知しました!異なることを行う2つのオプションがありますが、どちらも元のデータの定期的にグリッド化された性質を利用します。

1つ目は scipy.ndimage.zoom です。元のデータの補間に基づいて、より密度の高い通常のグリッドを作成したい場合は、これが最適な方法です。

2番目は scipy.ndimage.map_coordinates です。データ内のいくつか(または多く)の任意のポイントを補間したいが、元のデータの定期的にグリッド化された性質を利用したい場合(たとえば、四分木は不要)、それが最適な方法です。


配列を「ズーム」する( scipy.ndimage.zoom

簡単な例として(これは3次補間を使用します。双一次の場合はorder=1を、最も近い場合はorder=0を使用します):

import numpy as np
import scipy.ndimage as ndimage

data = np.arange(9).reshape(3,3)

print 'Original:\n', data
print 'Zoomed by 2x:\n', ndimage.zoom(data, 2)

これにより、次のようになります。

Original:
[[0 1 2]
 [3 4 5]
 [6 7 8]]
Zoomed by 2x:
[[0 0 1 1 2 2]
 [1 1 1 2 2 3]
 [2 2 3 3 4 4]
 [4 4 5 5 6 6]
 [5 6 6 7 7 7]
 [6 6 7 7 8 8]]

これは、3D(およびnD)アレイでも機能します。ただし、たとえば2倍にズームすると、すべての軸に沿ってズームすることに注意してください。

data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape

これにより、次のようになります。

Original:
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]]

 [[ 9 10 11]
  [12 13 14]
  [15 16 17]]

 [[18 19 20]
  [21 22 23]
  [24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)

ズームしたい3バンドのRGB画像のようなものがある場合は、ズーム係数としてタプルのシーケンスを指定することでこれを行うことができます。

print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))

これにより、次のようになります。

Zoomed by 2x along the last two axes:
[[[ 0  0  1  1  2  2]
  [ 1  1  1  2  2  3]
  [ 2  2  3  3  4  4]
  [ 4  4  5  5  6  6]
  [ 5  6  6  7  7  7]
  [ 6  6  7  7  8  8]]

 [[ 9  9 10 10 11 11]
  [10 10 10 11 11 12]
  [11 11 12 12 13 13]
  [13 13 14 14 15 15]
  [14 15 15 16 16 16]
  [15 15 16 16 17 17]]

 [[18 18 19 19 20 20]
  [19 19 19 20 20 21]
  [20 20 21 21 22 22]
  [22 22 23 23 24 24]
  [23 24 24 25 25 25]
  [24 24 25 25 26 26]]]

map_coordinates を使用した定期的にグリッド化されたデータの任意の補間

map_coordinatesについて最初に理解することは、pixel座標で動作することです(たとえば、配列にインデックスを付けるのと同じですが、値フロートにすることができます)。あなたの説明から、これはまさにあなたが望むものですが、しばしば人々を混乱させます。たとえば、x、y、zの「実世界」座標がある場合、それらをインデックスベースの「ピクセル」座標に変換する必要があります。

とにかく、1.2、0.3、1.4の位置にある元の配列の値を補間したいとしましょう。

以前のRGB画像の場合の観点からこれを考えると、最初の座標は「バンド」に対応し、2番目は「行」に対応し、最後は「列」に対応します。どの順序がデータの構造化に完全に依存するかに対応しますが、印刷された配列との比較を視覚化しやすくするため、これらを「z、y、x」座標として使用します。

import numpy as np
import scipy.ndimage as ndimage

data = np.arange(27).reshape(3,3,3)

print 'Original:\n', data
print 'Sampled at 1.2, 0.3, 1.4:'
print ndimage.map_coordinates(data, [[1.2], [0.3], [1.4]])

これにより、次のようになります。

Original:
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]]

 [[ 9 10 11]
  [12 13 14]
  [15 16 17]]

 [[18 19 20]
  [21 22 23]
  [24 25 26]]]
Sampled at 1.2, 0.3, 1.4:
[14]

繰り返しますが、これはデフォルトで3次補間です。 order kwargを使用して、補間のタイプを制御します。

ここで、scipy.ndimageのすべての操作が元の配列のdtypeを保持することに注意してください。浮動小数点の結果が必要な場合は、元の配列を浮動小数点としてキャストする必要があります。

In [74]: ndimage.map_coordinates(data.astype(float), [[1.2], [0.3], [1.4]])
Out[74]: array([ 13.5965])

あなたが気付くかもしれないもう一つのことは、補間された座標フォーマットが単一のポイントに対してかなり厄介であるということです(例えば、それはNx3配列の代わりに3xN配列を期待します)。ただし、座標のシーケンスがある場合は、間違いなく優れています。たとえば、データの「キューブ」を通過する線に沿ってサンプリングする場合を考えてみます。

xi = np.linspace(0, 2, 10)
yi = 0.8 * xi
zi = 1.2 * xi
print ndimage.map_coordinates(data, [zi, yi, xi])

これにより、次のようになります。

[ 0  1  4  8 12 17 21 24  0  0]

これは、境界条件がどのように処理されるかについて言及するのにも適した場所です。デフォルトでは、配列の外側はすべて0に設定されています。したがって、シーケンスの最後の2つの値は0です。 (つまり、最後の2つの要素でziは> 2です)。

配列の外側の点を-999にしたい場合(これは整数配列であるため、nanは使用できません。nanが必要な場合は、次のものが必要です。フロートにキャストします。):

In [75]: ndimage.map_coordinates(data, [zi, yi, xi], cval=-999)
Out[75]: array([   0,    1,    4,    8,   12,   17,   21,   24, -999, -999])

配列外のポイントに最も近い値を返すようにしたい場合は、次のようにします。

In [76]: ndimage.map_coordinates(data, [zi, yi, xi], mode='nearest')
Out[76]: array([ 0,  1,  4,  8, 12, 17, 21, 24, 25, 25])

"reflect"とデフォルトの"wrap"に加えて、"nearest""constant"を境界モードとして使用することもできます。これらはかなり自明ですが、混乱している場合は少し実験してみてください。

たとえば、配列の最初のバンドの最初の行に沿って、配列の2倍の距離に伸びる線を補間してみましょう。

xi = np.linspace(0, 5, 10)
yi, zi = np.zeros_like(xi), np.zeros_like(xi)

デフォルトのギブ:

In [77]: ndimage.map_coordinates(data, [zi, yi, xi])
Out[77]: array([0, 0, 1, 2, 0, 0, 0, 0, 0, 0])

これを以下と比較してください:

In [78]: ndimage.map_coordinates(data, [zi, yi, xi], mode='reflect')
Out[78]: array([0, 0, 1, 2, 2, 1, 2, 1, 0, 0])

In [78]: ndimage.map_coordinates(data, [zi, yi, xi], mode='wrap')
Out[78]: array([0, 0, 1, 2, 0, 1, 1, 2, 0, 1])

うまくいけば、それは物事を少し明確にします!

34
Joe Kington

ジョーによる素晴らしい答え。彼の提案に基づいて、regulargridパッケージ( https://pypi.python.org/pypi/regulargrid/ )を作成しました。 、ソース https://github.com/JohannesBuchner/regulargrid

任意の座標スケールの非常に高速なscipy.ndimage.map_coordinatesを介して、n次元デカルトグリッド(ここで必要に応じて)をサポートします。

7
j13r