2017/09/30

3次元一様ランダム回転

3次元の学習データを増やすため、ランダムかつ一様に回転する関数を作りました。手法は色々あるようなのですが、今回は、Shoemake's methodと呼ばれている方法を利用しました。実装例が
http://www.mech.utah.edu/~brannon/public/rotation.pdf
のサブルーチンRNDC (ページA-9)にFortranのコードで示されていましたので、これをPythonで書き直しました。

コードは、ここか下を参照。3次元のベクトルを入力すると、ランダムに回転させたベクトルが返ります。回転行列がrなので、これを返すようにすれば、色々なベクトルを同じように回転できます。

 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
27
28
29
import numpy as np
import math

def uniform_random_rotate(v):
    x0 = np.random.random()
    y1 = 2*math.pi*np.random.random()
    y2 = 2*math.pi*np.random.random()
    r1 = math.sqrt(1.0-x0)
    r2 = math.sqrt(x0)
    u0 = math.cos(y2)*r2
    u1 = math.sin(y1)*r1
    u2 = math.cos(y1)*r1
    u3 = math.sin(y2)*r2
    coefi = 2.0*u0*u0-1.0
    coefuu = 2.0
    coefe = 2.0*u0
    r = np.zeros(shape=(3, 3))
    r[0, 0] = coefi+coefuu*u1*u1
    r[1, 1] = coefi+coefuu*u2*u2
    r[2, 2] = coefi+coefuu*u3*u3

    r[1, 2] = coefuu*u2*u3-coefe*u1
    r[2, 0] = coefuu*u3*u1-coefe*u2
    r[0, 1] = coefuu*u1*u2-coefe*u3

    r[2, 1] = coefuu*u3*u2+coefe*u1
    r[0, 2] = coefuu*u1*u3+coefe*u2
    r[1, 0] = coefuu*u2*u1+coefe*u3
    return np.dot(v, r)

利用例は以下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%matplotlib inline
import numpy as np
import math
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from random_rotate import uniform_random_rotate

uvec = np.array([1,0,0])
data = []
for v in range(10000):
    data.append(uniform_random_rotate(uvec))
data = np.array(data)

fig = plt.figure(figsize=(15, 10))
ax = fig.add_subplot(111, projection='3d')
ax.scatter3D(data[:,0:1], data[:,1:2], data[:,2:3], s=0.2)
plt.show()

プロット結果が以下。

とりあえず見た感じ、半径1の球の表面に一様分布しているように見えます。一様分布をランダムに生成しているのではなくて、ベクトル(1, 0, 0)を一様ランダムに回転させて得られた図です。

0 件のコメント :