🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
open3d

open3dは、3Dデータ処理を行うオープンソースライブラリです。C++とPythonの両方のフロントエンドを提供。少ないコードでビルドが早く、導入も容易に行えます。RGBD画像処理が可能で、シーンを再構築できる機能なども備えています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

223閲覧

【Unity, Open3d】Unityで取得した深度画像とカラー画像から作成した点群を用いたOpen3dでの位置合わせ

taiki_inoue

総合スコア5

open3d

open3dは、3Dデータ処理を行うオープンソースライブラリです。C++とPythonの両方のフロントエンドを提供。少ないコードでビルドが早く、導入も容易に行えます。RGBD画像処理が可能で、シーンを再構築できる機能なども備えています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2024/03/18 06:53

実現したいこと

Unityで取得した深度画像とカラー画像から作成した点群とそのオブジェクトのメッシュから作成した点群の位置合わせ

前提

Open3dでUnityで指定した回転と同じ回転を行うにはどのようにしたらよいのでしょうか?

試したこと・発生している問題

Unityで位置を[x, y, z] = [3, 4, 3],回転を[x, y, z] = [30, 40, 50]に指定し深度画像とカラー画像を取得し,それをもとにOpen3dで作成した点群(pcd_scan)と,オブジェクトのメッシュから作成した点群(pcd_cad)の位置合わせをしたいです.
イメージ説明

まずはUnityとOpen3dの座標系の違いを調べました.Unityは左手系,Open3dは右手系になっており,Unityで位置は原点のまま,回転もしていないpcd_scanとpcd_cadを何もせずそのまま表示すると次の画像のようになりました.
イメージ説明
上にあるのがpcd_cad,下にあるのがpcd_scanでそれぞれの座標系も示しました.(pcd_scanの方はUnity画面で確認して手書き)
この図からUnityのx軸y軸z軸はOpen3dではそれぞれx軸z軸y軸に対応すると考えました.

また,各軸の回転角の向きを調べるためにUnityで位置を[x, y, z] = [0, 0, 0],回転を[x, y, z] = [10, 0, 0], [0, 10, 0], [0, 0, 10]に指定した3つの点群を作成しOpen3dでpcd_cadをx軸z軸y軸に±10度回転させ,Open3dでは各軸正負のどちらに回転させればUnityの回転を実現できるか調べました.

その結果が次のようになります.ピンクの点群がOpen3dでpcd_cadを10度回転させた点群,青の点群がOpen3dでpcd_cadを-10度回転させた点群です.
イメージ説明
↑ Unityでx軸に10度回転させた点群とOpen3dでpcd_cadをx軸に±10度回転させた様子

イメージ説明
↑ Unityでy軸に10度回転させた点群とOpen3dでpcd_cadをz軸に±10度回転させた様子

イメージ説明
↑ Unityでz軸に10度回転させた点群とOpen3dでpcd_cadをy軸に±10度回転させた様子

この結果から各軸の回転について次のような対応があると考えました
Unity     Open3d
x軸に10度回転⇔x軸に-10度回転
y軸に10度回転⇔z軸に10度回転
z軸に10度回転⇔y軸に-10度回転

そして,Unityのマニュアルに

Unity では、Z 軸、X 軸、Y 軸の順にオイラー角回転を行います。

との記述があったのでopen3dでは回転をy軸, x軸, z軸の順に行うことにしました.
https://docs.unity3d.com/ja/2021.3/Manual/QuaternionAndEulerRotationsInUnity.html

そうして位置合わせを行った結果が次の画像です.
イメージ説明
イメージ説明

かなりずれが生じていることが確認できます.このずれが生じる原因の検討がつかないのが現状です.

該当のソースコード

Unityで位置と回転の情報を取得するためのスクリプトとopen3dで実行したpythonの2つです

SavePosQuaToText.cs

1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.IO; 5using UnityEngine; 6 7public class SavePosQuaToText : MonoBehaviour 8{ 9 // Start is called before the first frame update 10 void Start() 11 { 12 13 } 14 15 // Update is called once per frame 16 void Update() 17 { 18 if (Input.GetKeyDown(KeyCode.Space)) 19 { 20 SaveToText(); 21 } 22 } 23 24 void SaveToText() 25 { 26 Debug.Log("START Append Text"); 27 28 string[] strings = { "bunny", "bunny (1)", "bunny (2)", "bunny (3)", "bunny (4)", "bunny (5)", "bunny (6)", "bunny (7)", "bunny (8)" }; 29 30 for (int i = 0; i < strings.Length; i++) 31 { 32 string ObjectName = strings[i]; 33 GameObject FocusObject = GameObject.Find(ObjectName); 34 SavePosition(FocusObject); 35 SaveQuaternionXYZ(FocusObject); 36 } 37 38 Debug.Log("FINISH Append Text"); 39 } 40 41 void SavePosition(GameObject FocusObject) 42 { 43 Vector3 pos = FocusObject.transform.position; 44 string filePath = Application.dataPath + "/../Position.txt"; 45 File.AppendAllText(filePath, Convert.ToString(pos.x) + "," + Convert.ToString(pos.y) + "," + Convert.ToString(pos.z) + "\n"); 46 } 47 void SaveQuaternionXYZ(GameObject FocusObject) 48 { 49 float x = FocusObject.transform.localEulerAngles.x; 50 float y = FocusObject.transform.localEulerAngles.y; 51 float z = FocusObject.transform.localEulerAngles.z; 52 var rotX = Quaternion.AngleAxis(x, new Vector3(1, 0, 0)); 53 var rotY = Quaternion.AngleAxis(y, new Vector3(0, 1, 0)); 54 var rotZ = Quaternion.AngleAxis(z, new Vector3(0, 0, 1)); 55 string filePathqua = Application.dataPath + "/../Quaternion.txt"; 56 File.AppendAllText(filePathqua, Convert.ToString(rotX.x) + "," + Convert.ToString(rotX.y) + "," + Convert.ToString(rotX.z) + "," + Convert.ToString(rotX.w) + "\n"); 57 File.AppendAllText(filePathqua, Convert.ToString(rotY.x) + "," + Convert.ToString(rotY.y) + "," + Convert.ToString(rotY.z) + "," + Convert.ToString(rotY.w) + "\n"); 58 File.AppendAllText(filePathqua, Convert.ToString(rotZ.x) + "," + Convert.ToString(rotZ.y) + "," + Convert.ToString(rotZ.z) + "," + Convert.ToString(rotZ.w) + "\n"); 59 } 60}

Python

1def get_trans_mat(R, t): 2 trans_mat = np.identity(4) 3 trans_mat[0:3, 0:3] = R 4 trans_mat[0:3, 3] = t 5 return trans_mat 6 7def Rotmat_xyz(x, y, z): 8 R_x = np.array([ 9 [1, 0, 0], 10 [0, math.cos(math.radians(x)), math.sin(math.radians(x))], 11 [0, (-1)*math.sin(math.radians(x)), math.cos(math.radians(x))] 12 ]) 13 R_y = np.array([ 14 [math.cos(math.radians(y)), 0, (-1)*math.sin(math.radians(y))], 15 [0, 1, 0], 16 [math.sin(math.radians(y)), 0, math.cos(math.radians(y))] 17 ]) 18 R_z = np.array([ 19 [math.cos(math.radians(z)), math.sin(math.radians(z)), 0], 20 [(-1)*math.sin(math.radians(z)), math.cos(math.radians(z)), 0], 21 [0, 0, 1] 22 ]) 23 return R_x, R_y, R_z 24 25def Rotmat_form_quaternion(q): 26 return np.array([ 27 [q[3]*q[3]+q[0]*q[0]-q[1]*q[1]-q[2]*q[2], 2*(q[0]*q[1]-q[2]*q[3]), 2*(q[0]*q[2]+q[1]*q[3])], 28 [2*(q[0]*q[1]+q[2]*q[3]), q[3]*q[3]-q[0]*q[0]+q[1]*q[1]-q[2]*q[2], 2*(q[1]*q[2]-q[0]*q[3])], 29 [2*(q[0]*q[2]-q[1]*q[3]), 2*(q[1]*q[2]+q[0]*q[3]), q[3]*q[3]-q[0]*q[0]-q[1]*q[1]+q[2]*q[2]] 30 ]) 31 32#Unityで取得した各軸の回転クォータニオンをテキストファイルから読み込む 33num = 2 34new_mat = np.zeros((3*num, 4)) 35read_textfile(filepath_new, new_mat) 36quaX = new_mat[0] 37quaY = new_mat[1] 38quaZ = new_mat[2] 39 40#左手系のクォータニオンを右手系に変換 41quaX_right = np.array([-quaX[0], quaX[1], -quaX[2], quaX[3]]) 42quaY_right = np.array([-quaY[0], quaY[1], -quaY[2], quaY[3]]) 43quaZ_right = np.array([-quaZ[0], quaZ[1], -quaZ[2], quaZ[3]]) 44#右手系のクォータニオンから右手系の回転行列を計算 45R_quaX = Rotmat_form_quaternion(quaX_right) 46R_quaY = Rotmat_form_quaternion(quaY_right) 47R_quaZ = Rotmat_form_quaternion(quaZ_right) 48 49mesh_coordinate = o3d.geometry.TriangleMesh.create_coordinate_frame(size=3.0) 50o3d.visualization.draw_geometries([mesh_coordinate, pcd_cad, pcd_scan]) 51 52#取得点群をz軸に180度回転しモデルと向きを合わせた後にモデルの位置まで平行移動 53R_x180, R_y180, R_z180 = Rotmat_xyz(180, 180, 180) 54trans_mat_z180 = get_trans_mat(R_z180, np.zeros(3)) 55pcd_scan.transform(trans_mat_z180) 56trans_mat = get_trans_mat(np.identity(3), -(np.array([0, 0, -15]) + np.array([-3, -3, 4]))) 57pcd_scan.transform(trans_mat) 58o3d.visualization.draw_geometries([mesh_coordinate, pcd_cad, pcd_scan]) 59 60#モデル点群を回転(Open3dでの回転) 61trans_mat_quaX = get_trans_mat(R_quaX.T, np.zeros(3)) 62trans_mat_quaY = get_trans_mat(R_quaY, np.zeros(3)) 63trans_mat_quaZ = get_trans_mat(R_quaZ, np.zeros(3)) 64pcd_cad.transform(trans_mat_quaY) 65pcd_cad.transform(trans_mat_quaX) 66pcd_cad.transform(trans_mat_quaZ) 67o3d.visualization.draw_geometries([pcd_scan, pcd_cad])

補足情報

pcd_cadは次のように作成しました.

Python

1mesh = o3d.io.read_triangle_mesh("stanford_bunny.stl") 2xyz = np.asarray(mesh.vertices) 3pcd_cad = o3d.geometry.PointCloud() 4pcd_cad.points = o3d.utility.Vector3dVector(xyz) 5pcd_cad.paint_uniform_color([0.5, 0.5, 0.5])

Unityで使っているオブジェクトはstanford_bunny.stlではありませんが,Blenderでstanford_bunny.stlをインポートし何もいじらずfbxファイルでエクスポートしたものをUnityで使っています.
イメージ説明

また,pcd_cadを回転させ重心位置を計算したところ一致しませんでした.ずれの原因かどうかは分かりませんが...
イメージ説明

最後に

長文となってしまいましたが最後まで読んでいただきありがとうございます.少しのアドバイスでも構いませんので回答できる方がいればよろしくお願いいたします.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

Unity側でクォータニオンを保存する際にy軸,z軸の回転角を入れ替えて保存すればよいことに気づきました.正しく位置合わせができたため質問を閉じさせていただきます.

投稿2024/03/18 09:14

taiki_inoue

総合スコア5

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問