はじめに
- この記事はOpenCVアドベントカレンダー2023 24日目の記事です。その他の記事は目次から参照可能です
TL;DR
- あんな、回転行列って6種類ぐらいあると言ったな、あれは嘘だ。
- 回転角を計算する際に軸の順序を指定するオプションは存在しないと言ったな、あれは嘘だ。
OpenCVのAPIについて
- 2日目の記事で以下の通り紹介した
回転角を計算する際に軸の順序を指定するオプションは存在しない
- が、あのあとど真ん中のAPIを発見してしまい、知らなかったとは言え嘘を書いてしまったので、「謝罪記事」の体で本日の記事である。
- OpenCVには
cv::Quat<_Tp>
クラスが存在する。一般的にはcv::Quatd
クラスを使い、これはcv::Quat<double>
の別名である。 - 参考までにOpenCV4.8.0時点での該当ドキュメント
- このクラスには
toEulerAngle
関数が存在し、クオータニオンを回転角に変換してくれる - しかも、回転角を計算する際に回転軸の順序をオプションとして取る。
OpenCV 4.5.1から登場
- Gitの履歴を紐解くに、commit hash 11cfa64、PR 18335で今から約3年前の2020年11月20日にマージされた
6種類じゃきかなかった回転行列
- 2日目の記事で以下の通り紹介した
前述したように、回転行列は回転軸の順序によって6種類存在する
- が、
cv::Quatd::toEulerAngle
メンバ関数のドキュメントを読むと、引数として、回転軸の順序を指定するためにcv::QuatEnum::EulerAnglesType
を指定するパラメタが存在し、このenum
は、以下の24通り(!)存在する
enum EulerAnglesType
{
INT_XYZ, ///< Intrinsic rotations with the Euler angles type X-Y-Z
INT_XZY, ///< Intrinsic rotations with the Euler angles type X-Z-Y
INT_YXZ, ///< Intrinsic rotations with the Euler angles type Y-X-Z
INT_YZX, ///< Intrinsic rotations with the Euler angles type Y-Z-X
INT_ZXY, ///< Intrinsic rotations with the Euler angles type Z-X-Y
INT_ZYX, ///< Intrinsic rotations with the Euler angles type Z-Y-X
INT_XYX, ///< Intrinsic rotations with the Euler angles type X-Y-X
INT_XZX, ///< Intrinsic rotations with the Euler angles type X-Z-X
INT_YXY, ///< Intrinsic rotations with the Euler angles type Y-X-Y
INT_YZY, ///< Intrinsic rotations with the Euler angles type Y-Z-Y
INT_ZXZ, ///< Intrinsic rotations with the Euler angles type Z-X-Z
INT_ZYZ, ///< Intrinsic rotations with the Euler angles type Z-Y-Z
EXT_XYZ, ///< Extrinsic rotations with the Euler angles type X-Y-Z
EXT_XZY, ///< Extrinsic rotations with the Euler angles type X-Z-Y
EXT_YXZ, ///< Extrinsic rotations with the Euler angles type Y-X-Z
EXT_YZX, ///< Extrinsic rotations with the Euler angles type Y-Z-X
EXT_ZXY, ///< Extrinsic rotations with the Euler angles type Z-X-Y
EXT_ZYX, ///< Extrinsic rotations with the Euler angles type Z-Y-X
EXT_XYX, ///< Extrinsic rotations with the Euler angles type X-Y-X
EXT_XZX, ///< Extrinsic rotations with the Euler angles type X-Z-X
EXT_YXY, ///< Extrinsic rotations with the Euler angles type Y-X-Y
EXT_YZY, ///< Extrinsic rotations with the Euler angles type Y-Z-Y
EXT_ZXZ, ///< Extrinsic rotations with the Euler angles type Z-X-Z
EXT_ZYZ, ///< Extrinsic rotations with the Euler angles type Z-Y-Z
#ifndef CV_DOXYGEN
EULER_ANGLES_MAX_VALUE // 24だよっ!
#endif
};
-
INT
とEXT
の接頭辞はそれぞれ回転軸の順序を逆順に並べ替えることに相当する- 例:2日目の記事では
XYZ
はZ軸
→Y軸
→X軸
の順に回転させる、と解説した。これはINT
接頭辞がつく方の説明に相当する -
EXT
接頭辞がつくパターンはXYZ
はX軸
→Y軸
→Z軸
の順に回転させる、
- 例:2日目の記事では
- また、2日目の記事では触れなかった
INT_XYX
なるパターンも存在する- これはどんな回転行列でも、回転軸2つの回転各を適切にとることで、
XYX
のように、3軸のうち2軸だけの回転だけで回転行列を表す組み合わせが存在する(ただし、その際2軸のうち1軸を2階適用する) - ここについては私も理解が不十分なので、詳細は割愛します
- これはどんな回転行列でも、回転軸2つの回転各を適切にとることで、
- すくなくともOpenCVでの実装は、2日目の記事で紹介した範囲より、4倍広い範囲をカバーしているといえる
ジンバルロック
- 2日目の記事で、用語の裏付けを取る余裕がなくて敢えて触れなかったのだが、
回転角は特定の条件下で回転角2つが混ざった状態でしか復元できなくなる。
- この特定条件にはジンバルロック(Gimbal Lock) と名前が付いている
-
toEulerAngles
メンバ関数内ではジンバルロックが発生しているかチェックし、発生した場合にはコンソールに以下の警告メッセージが表示される
[ WARN:0@87.260] global quaternion.inl.hpp:1030 toEulerAngles Gimbal Lock occurs. Euler angles are non-unique, we set the third angle to 0
- 余談ではあるがこのクラス、関数の存在に気づいたのはこのログメッセージがきっかけである