pythonでオイラー角をクォータニオンで実装し、ベジェ曲線補完をしたいのですがいまいち実装方法がわかりません...
import numpy as np firstFlame=0 afterFlame=5 firstEuler=np.array([20,0,0]) #0フレーム目における姿勢のアングル(オイラー角) afterEuler=np.array([-19,30,0]) #5フレーム目における姿勢のアングル(オイラー角) BezierPoint=np.array([[0,0],[20,20],[107,107],[127,127]]) #ベジェ曲線における直線補間 BezierPoint2=np.array([[0,0],[0,127],[127,0],[127,127]]) #ベジェ曲線における曲線補間 time = np.arange(0,1,0.01) #0~5の間をクォータニオンでベジェ曲線補完し、最終的にオイラー角に変換したい。 #実行結果を以下のようにしたい。 #ベジェ曲線における直線補間の場合(BezierPoint) #[0.2 0.4 0.6 0.8 1.0]#ベジェ曲線の補間係数の値 #****************************************** #[20, 0, 0]0フレーム目 #[12.3, 6.2, 1.7] 1フレーム目 #[4.4, 12.1, 2.5] 2フレーム目 #[-3.4, 17.9, 2.5] 3フレーム目 #[-11.3, 23.8, 1.7] 4フレーム目 #ベジェ曲線における曲線補間の場合(BezierPoint2) #[0.0, 0.01747555, 0.11148985, 0.88851015, 0.98252445]ベジェ曲線の補間係数の値 #****************************************** #[20, 0, 0]0フレーム目 #[19.3, 0.6, 0.2] 1フレーム目 #[15.7, 3.5, 1.1] 2フレーム目 #[-14.7, 26.5, 1.1] 3フレーム目 #[-18.3, 29.4, 0.2] 4フレーム目 #現在の実装コードではオイラー角をそのままベジェ曲線補間しているため、以下のような出力結果となってしまう。 #ベジェ曲線における直線補間の場合(BezierPoint) #[0.0, 0.01747555, 0.11148985, 0.88851015, 0.98252445]ベジェ曲線の補間係数の値 #****************************************** #[[ 20. 0. 0. ] 0フレーム目 # [ 12.2 6. 0. ] 1フレーム目 # [ 4.4 12. 0. ] 2フレーム目 # [ -3.4 18. 0. ] 3フレーム目 # [-11.2 24. 0. ]] 4フレーム目 #ベジェ曲線における曲線補間の場合(BezierPoint2) #[0.0, 0.01747555, 0.11148985, 0.88851015, 0.98252445]ベジェ曲線の補間係数の値 #****************************************** #[[ 20. 0. 0. ] 0フレーム目 #[ 19.31845359 0.52426647 0. ] 1フレーム目 #[ 15.65189583 3.34469552 0. ] 2フレーム目 #[-14.65189583 26.65530448 0. ] 3フレーム目 #[-18.31845359 29.47573353 0. ]]4フレーム目 実装コードの全体像 #******************************************************************** import numpy as np import bisect import matplotlib.pyplot as plt def bezier(N,cuttime,P,X): time = np.arange(0,1,cuttime)#ベジエ曲線におけるt firstEuler= np.array([20,0,0])#0フレーム目における姿勢のアングル(オイラー角) afterEuler = np.array([-19,30,0])#5フレーム目における姿勢のアングル(オイラー角) num = np.arange(N + 1) Y = np.outer(P,(np.array(time ** np.c_[num[::-1]]).T * np.array((1 - time) ** np.c_[num]).T * np.array([1,N,N,1])).T[::-1]).T#ベジエ曲線を計算 D = np.reshape(Y,(len(time) * (N + 1),len(P),(N + 1),(N + 1),2))#ベジエ曲線を計算 point = np.array([[sum([D.transpose(3,2,1,0,4)[i][j][k][len(time) * i:len(time) * i + len(time)] for i in range(N + 1)])for j in range(N + 1)]for k in range(len(P))])#求まったベジエ曲線の座標点を整理 bezierCoeff = point.T#ベジェ曲線の(time=0,0.1,0.2...1.1)における(X,Y)座標(bezierCoeff[1]はY軸成分、bezierCoeff[0]はX軸成分) plt.scatter(bezierCoeff[0].T[0][0],bezierCoeff[1].T[0][0])#0フレーム目の[0,0,127,0,0,127,127,127]におけるベジエ曲線 plt.show() coef = np.array((bezierCoeff[1][1:] - bezierCoeff[1][:-1]) / (bezierCoeff[0][1:] - bezierCoeff[0][:-1]))#ベジエ曲線の時間timeにおける変化量を計算 intercept = np.array((bezierCoeff[1][:-1] - coef * bezierCoeff[0][:-1]))#直線の切片を計算 res = np.apply_along_axis(lambda b: [np.digitize(X[i], b)for i in range(len(X))], 2, bezierCoeff[0].T) - 1 res = np.array([res.transpose(0,2,1)[i][i]for i in range(res.shape[0] - 1)]) res = np.array([[res[i][j][:-1]for j in range(res.shape[1])]for i in range(res.shape[0])])#切片と変化量のインデックス absEuler = afterEuler - firstEuler #姿勢のアングルの差 bezierCoef=(X[0][:-1] * coef.T[0][0][res[0][0]] + intercept.T[0][0][res[0][0]]) / 127#0フレーム目の[0,0,127,0,0,127,127,127]におけるベジエ曲線の係数 print(np.outer(bezierCoef,absEuler) + firstEuler)#オイラー角をそのままベジェ曲線補間しているのでz軸が反映されない...四元数を用いて上記のような結果にしたい。 #return Z def main(): frame = np.array([0,5,8,13,15,17,18,24,28,30,0,15,30])#フレーム frame_abs = frame[1:] - frame[:-1] #(各フレームで対応付けられた4つのベジエ曲線の方向点を示す8つのパラメータ) point = np.array([[[0,0,127,0,0,127,127,127],[0,0,30,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127]],#0フレーム目 [[0,0,0,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,127,20,107,107,127,127],[0,0,20,20,107,107,127,127]],#5フレーム目 [[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,0,40,17,127,127,127],[0,0,20,20,107,107,127,127]],#8フレーム目 [[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127]],#13フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127]],#15フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127]],#17フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,0,120,107,107,127,127]],#18フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,107,107,127,127],[0,0,0,120,107,107,127,127]],#24フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,127,107,127,127],[0,0,0,120,127,107,127,127]],#28フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,127,107,127,127],[0,0,0,120,127,107,127,127]],#30フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,127,107,127,127],[0,0,0,120,127,107,127,127]],#0フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,127,107,127,127],[0,0,0,120,127,107,127,127]],#15フレーム目 [[0,0,0,127,0,127,127,127],[0,0,20,20,107,107,127,127],[0,0,20,20,127,107,127,127],[0,0,0,120,127,107,127,127]]])#30フレーム目 point = np.array(np.split(point, 4, axis=2)).transpose(1,2,0,3)#ベジエ曲線の8つのパラメータをshape=(4,2)に分割 X = 127 / frame_abs * np.array([np.arange(item + 1)for i,item in enumerate(frame_abs)]) Y = bezier(3,0.01,point,X) if __name__ == "__main__": main()
計算式、あるいは処理方法がわからないというはなしでしょうか
それとも、計算式はわかってるけど実装法がわからないということでしょうか。
ご回答ありがとうございます。
どちらかというと計算式と処理方法の方でしょうか。
どのような計算式で与えられるかを教えていただければあとは自力で実装したいです。
あれから色々調べまして、
pyquaternionのQuaternion.Slerpなんかも使ってみましだが、やはりz軸は反映されませんでした,,,(使い方が悪いだけかも)
なにが分からないのですか?
文を読み限り、オイラー角 -> クォータニオン -> ベジェ曲線補完という流れになっていますが。このうちのどこが分からないのでしょうか?
ご返信ありがとうございます。
分かりにくくて申し訳ございません。
オイラー角→クオータニオンの変換は数式がわからない感じです。
また、クオータニオン→ベジェ曲線補間は実装方法がわからないです。
https://qiita.com/edo_m18/items/5db35b60112e281f840e
Unityで実行してみるという項目に計算式載ってるよ。
考え方としては一回マトリックス経由するみたいだね。
https://myenigma.hatenablog.com/entry/2016/10/12/073335
クオータニオンからベジェ曲線補間はよくわかんなかったわ。
Quaternion.Slerpはベジェではない線形補間だからね...
あなたの回答
tips
プレビュー