dualquaternion.hpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. //
  6. // License Agreement
  7. // For Open Source Computer Vision Library
  8. //
  9. // Copyright (C) 2020, Huawei Technologies Co., Ltd. All rights reserved.
  10. // Third party copyrights are property of their respective owners.
  11. //
  12. // Licensed under the Apache License, Version 2.0 (the "License");
  13. // you may not use this file except in compliance with the License.
  14. // You may obtain a copy of the License at
  15. //
  16. // http://www.apache.org/licenses/LICENSE-2.0
  17. //
  18. // Unless required by applicable law or agreed to in writing, software
  19. // distributed under the License is distributed on an "AS IS" BASIS,
  20. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. // See the License for the specific language governing permissions and
  22. // limitations under the License.
  23. //
  24. // Author: Liangqian Kong <kongliangqian@huawei.com>
  25. // Longbu Wang <wanglongbu@huawei.com>
  26. #ifndef OPENCV_CORE_DUALQUATERNION_HPP
  27. #define OPENCV_CORE_DUALQUATERNION_HPP
  28. #include <opencv2/core/quaternion.hpp>
  29. #include <opencv2/core/affine.hpp>
  30. namespace cv{
  31. //! @addtogroup core
  32. //! @{
  33. template <typename _Tp> class DualQuat;
  34. template <typename _Tp> std::ostream& operator<<(std::ostream&, const DualQuat<_Tp>&);
  35. /**
  36. * Dual quaternions were introduced to describe rotation together with translation while ordinary
  37. * quaternions can only describe rotation. It can be used for shortest path pose interpolation,
  38. * local pose optimization or volumetric deformation. More details can be found
  39. * - https://en.wikipedia.org/wiki/Dual_quaternion
  40. * - ["A beginners guide to dual-quaternions: what they are, how they work, and how to use them for 3D character hierarchies", Ben Kenwright, 2012](https://borodust.org/public/shared/beginner_dual_quats.pdf)
  41. * - ["Dual Quaternions", Yan-Bin Jia, 2013](http://web.cs.iastate.edu/~cs577/handouts/dual-quaternion.pdf)
  42. * - ["Geometric Skinning with Approximate Dual Quaternion Blending", Kavan, 2008](https://www.cs.utah.edu/~ladislav/kavan08geometric/kavan08geometric)
  43. * - http://rodolphe-vaillant.fr/?e=29
  44. *
  45. * A unit dual quaternion can be classically represented as:
  46. * \f[
  47. * \begin{equation}
  48. * \begin{split}
  49. * \sigma &= \left(r+\frac{\epsilon}{2}tr\right)\\
  50. * &= [w, x, y, z, w\_, x\_, y\_, z\_]
  51. * \end{split}
  52. * \end{equation}
  53. * \f]
  54. * where \f$r, t\f$ represents the rotation (ordinary unit quaternion) and translation (pure ordinary quaternion) respectively.
  55. *
  56. * A general dual quaternions which consist of two quaternions is usually represented in form of:
  57. * \f[
  58. * \sigma = p + \epsilon q
  59. * \f]
  60. * where the introduced dual unit \f$\epsilon\f$ satisfies \f$\epsilon^2 = \epsilon^3 =...=0\f$, and \f$p, q\f$ are quaternions.
  61. *
  62. * Alternatively, dual quaternions can also be interpreted as four components which are all [dual numbers](https://www.cs.utah.edu/~ladislav/kavan08geometric/kavan08geometric):
  63. * \f[
  64. * \sigma = \hat{q}_w + \hat{q}_xi + \hat{q}_yj + \hat{q}_zk
  65. * \f]
  66. * If we set \f$\hat{q}_x, \hat{q}_y\f$ and \f$\hat{q}_z\f$ equal to 0, a dual quaternion is transformed to a dual number. see normalize().
  67. *
  68. * If you want to create a dual quaternion, you can use:
  69. *
  70. * ```
  71. * using namespace cv;
  72. * double angle = CV_PI;
  73. *
  74. * // create from eight number
  75. * DualQuatd dq1(1, 2, 3, 4, 5, 6, 7, 8); //p = [1,2,3,4]. q=[5,6,7,8]
  76. *
  77. * // create from Vec
  78. * Vec<double, 8> v{1,2,3,4,5,6,7,8};
  79. * DualQuatd dq_v{v};
  80. *
  81. * // create from two quaternion
  82. * Quatd p(1, 2, 3, 4);
  83. * Quatd q(5, 6, 7, 8);
  84. * DualQuatd dq2 = DualQuatd::createFromQuat(p, q);
  85. *
  86. * // create from an angle, an axis and a translation
  87. * Vec3d axis{0, 0, 1};
  88. * Vec3d trans{3, 4, 5};
  89. * DualQuatd dq3 = DualQuatd::createFromAngleAxisTrans(angle, axis, trans);
  90. *
  91. * // If you already have an instance of class Affine3, then you can use
  92. * Affine3d R = dq3.toAffine3();
  93. * DualQuatd dq4 = DualQuatd::createFromAffine3(R);
  94. *
  95. * // or create directly by affine transformation matrix Rt
  96. * // see createFromMat() in detail for the form of Rt
  97. * Matx44d Rt = dq3.toMat();
  98. * DualQuatd dq5 = DualQuatd::createFromMat(Rt);
  99. *
  100. * // Any rotation + translation movement can
  101. * // be expressed as a rotation + translation around the same line in space (expressed by Plucker
  102. * // coords), and here's a way to represent it this way.
  103. * Vec3d axis{1, 1, 1}; // axis will be normalized in createFromPitch
  104. * Vec3d trans{3, 4 ,5};
  105. * axis = axis / std::sqrt(axis.dot(axis));// The formula for computing moment that I use below requires a normalized axis
  106. * Vec3d moment = 1.0 / 2 * (trans.cross(axis) + axis.cross(trans.cross(axis)) *
  107. * std::cos(rotation_angle / 2) / std::sin(rotation_angle / 2));
  108. * double d = trans.dot(qaxis);
  109. * DualQuatd dq6 = DualQuatd::createFromPitch(angle, d, axis, moment);
  110. * ```
  111. *
  112. * A point \f$v=(x, y, z)\f$ in form of dual quaternion is \f$[1+\epsilon v]=[1,0,0,0,0,x,y,z]\f$.
  113. * The transformation of a point \f$v_1\f$ to another point \f$v_2\f$ under the dual quaternion \f$\sigma\f$ is
  114. * \f[
  115. * 1 + \epsilon v_2 = \sigma * (1 + \epsilon v_1) * \sigma^{\star}
  116. * \f]
  117. * where \f$\sigma^{\star}=p^*-\epsilon q^*.\f$
  118. *
  119. * A line in the \f$Pl\ddot{u}cker\f$ coordinates \f$(\hat{l}, m)\f$ defined by the dual quaternion \f$l=\hat{l}+\epsilon m\f$.
  120. * To transform a line, \f[l_2 = \sigma * l_1 * \sigma^*,\f] where \f$\sigma=r+\frac{\epsilon}{2}rt\f$ and
  121. * \f$\sigma^*=p^*+\epsilon q^*\f$.
  122. *
  123. * To extract the Vec<double, 8> or Vec<float, 8>, see toVec();
  124. *
  125. * To extract the affine transformation matrix, see toMat();
  126. *
  127. * To extract the instance of Affine3, see toAffine3();
  128. *
  129. * If two quaternions \f$q_0, q_1\f$ are needed to be interpolated, you can use sclerp()
  130. * ```
  131. * DualQuatd::sclerp(q0, q1, t)
  132. * ```
  133. * or dqblend().
  134. * ```
  135. * DualQuatd::dqblend(q0, q1, t)
  136. * ```
  137. * With more than two dual quaternions to be blended, you can use generalize linear dual quaternion blending
  138. * with the corresponding weights, i.e. gdqblend().
  139. *
  140. */
  141. template <typename _Tp>
  142. class CV_EXPORTS DualQuat{
  143. static_assert(std::is_floating_point<_Tp>::value, "Dual quaternion only make sense with type of float or double");
  144. using value_type = _Tp;
  145. public:
  146. static constexpr _Tp CV_DUAL_QUAT_EPS = (_Tp)1.e-6;
  147. DualQuat();
  148. /**
  149. * @brief create from eight same type numbers.
  150. */
  151. DualQuat(const _Tp w, const _Tp x, const _Tp y, const _Tp z, const _Tp w_, const _Tp x_, const _Tp y_, const _Tp z_);
  152. /**
  153. * @brief create from a double or float vector.
  154. */
  155. DualQuat(const Vec<_Tp, 8> &q);
  156. _Tp w, x, y, z, w_, x_, y_, z_;
  157. /**
  158. * @brief create Dual Quaternion from two same type quaternions p and q.
  159. * A Dual Quaternion \f$\sigma\f$ has the form:
  160. * \f[\sigma = p + \epsilon q\f]
  161. * where p and q are defined as follows:
  162. * \f[\begin{equation}
  163. * \begin{split}
  164. * p &= w + x\boldsymbol{i} + y\boldsymbol{j} + z\boldsymbol{k}\\
  165. * q &= w\_ + x\_\boldsymbol{i} + y\_\boldsymbol{j} + z\_\boldsymbol{k}.
  166. * \end{split}
  167. * \end{equation}
  168. * \f]
  169. * The p and q are the real part and dual part respectively.
  170. * @param realPart a quaternion, real part of dual quaternion.
  171. * @param dualPart a quaternion, dual part of dual quaternion.
  172. * @sa Quat
  173. */
  174. static DualQuat<_Tp> createFromQuat(const Quat<_Tp> &realPart, const Quat<_Tp> &dualPart);
  175. /**
  176. * @brief create a dual quaternion from a rotation angle \f$\theta\f$, a rotation axis
  177. * \f$\boldsymbol{u}\f$ and a translation \f$\boldsymbol{t}\f$.
  178. * It generates a dual quaternion \f$\sigma\f$ in the form of
  179. * \f[\begin{equation}
  180. * \begin{split}
  181. * \sigma &= r + \frac{\epsilon}{2}\boldsymbol{t}r \\
  182. * &= [\cos(\frac{\theta}{2}), \boldsymbol{u}\sin(\frac{\theta}{2})]
  183. * + \frac{\epsilon}{2}[0, \boldsymbol{t}][[\cos(\frac{\theta}{2}),
  184. * \boldsymbol{u}\sin(\frac{\theta}{2})]]\\
  185. * &= \cos(\frac{\theta}{2}) + \boldsymbol{u}\sin(\frac{\theta}{2})
  186. * + \frac{\epsilon}{2}(-(\boldsymbol{t} \cdot \boldsymbol{u})\sin(\frac{\theta}{2})
  187. * + \boldsymbol{t}\cos(\frac{\theta}{2}) + \boldsymbol{u} \times \boldsymbol{t} \sin(\frac{\theta}{2})).
  188. * \end{split}
  189. * \end{equation}\f]
  190. * @param angle rotation angle.
  191. * @param axis rotation axis.
  192. * @param translation a vector of length 3.
  193. * @note Axis will be normalized in this function. And translation is applied
  194. * after the rotation. Use @ref createFromQuat(r, r * t / 2) to create a dual quaternion
  195. * which translation is applied before rotation.
  196. * @sa Quat
  197. */
  198. static DualQuat<_Tp> createFromAngleAxisTrans(const _Tp angle, const Vec<_Tp, 3> &axis, const Vec<_Tp, 3> &translation);
  199. /**
  200. * @brief Transform this dual quaternion to an affine transformation matrix \f$M\f$.
  201. * Dual quaternion consists of a rotation \f$r=[a,b,c,d]\f$ and a translation \f$t=[\Delta x,\Delta y,\Delta z]\f$. The
  202. * affine transformation matrix \f$M\f$ has the form
  203. * \f[
  204. * \begin{bmatrix}
  205. * 1-2(e_2^2 +e_3^2) &2(e_1e_2-e_0e_3) &2(e_0e_2+e_1e_3) &\Delta x\\
  206. * 2(e_0e_3+e_1e_2) &1-2(e_1^2+e_3^2) &2(e_2e_3-e_0e_1) &\Delta y\\
  207. * 2(e_1e_3-e_0e_2) &2(e_0e_1+e_2e_3) &1-2(e_1^2-e_2^2) &\Delta z\\
  208. * 0&0&0&1
  209. * \end{bmatrix}
  210. * \f]
  211. * if A is a matrix consisting of n points to be transformed, this could be achieved by
  212. * \f[
  213. * new\_A = M * A
  214. * \f]
  215. * where A has the form
  216. * \f[
  217. * \begin{bmatrix}
  218. * x_0& x_1& x_2&...&x_n\\
  219. * y_0& y_1& y_2&...&y_n\\
  220. * z_0& z_1& z_2&...&z_n\\
  221. * 1&1&1&...&1
  222. * \end{bmatrix}
  223. * \f]
  224. * where the same subscript represent the same point. The size of A should be \f$[4,n]\f$.
  225. * and the same size for matrix new_A.
  226. * @param _R 4x4 matrix that represents rotations and translation.
  227. * @note Translation is applied after the rotation. Use createFromQuat(r, r * t / 2) to create
  228. * a dual quaternion which translation is applied before rotation.
  229. */
  230. static DualQuat<_Tp> createFromMat(InputArray _R);
  231. /**
  232. * @brief create dual quaternion from an affine matrix. The definition of affine matrix can refer to createFromMat()
  233. */
  234. static DualQuat<_Tp> createFromAffine3(const Affine3<_Tp> &R);
  235. /**
  236. * @brief A dual quaternion is a vector in form of
  237. * \f[
  238. * \begin{equation}
  239. * \begin{split}
  240. * \sigma &=\boldsymbol{p} + \epsilon \boldsymbol{q}\\
  241. * &= \cos\hat{\frac{\theta}{2}}+\overline{\hat{l}}\sin\frac{\hat{\theta}}{2}
  242. * \end{split}
  243. * \end{equation}
  244. * \f]
  245. * where \f$\hat{\theta}\f$ is dual angle and \f$\overline{\hat{l}}\f$ is dual axis:
  246. * \f[
  247. * \hat{\theta}=\theta + \epsilon d,\\
  248. * \overline{\hat{l}}= \hat{l} +\epsilon m.
  249. * \f]
  250. * In this representation, \f$\theta\f$ is rotation angle and \f$(\hat{l},m)\f$ is the screw axis, d is the translation distance along the axis.
  251. *
  252. * @param angle rotation angle.
  253. * @param d translation along the rotation axis.
  254. * @param axis rotation axis represented by quaternion with w = 0.
  255. * @param moment the moment of line, and it should be orthogonal to axis.
  256. * @note Translation is applied after the rotation. Use createFromQuat(r, r * t / 2) to create
  257. * a dual quaternion which translation is applied before rotation.
  258. */
  259. static DualQuat<_Tp> createFromPitch(const _Tp angle, const _Tp d, const Vec<_Tp, 3> &axis, const Vec<_Tp, 3> &moment);
  260. /**
  261. * @brief return a quaternion which represent the real part of dual quaternion.
  262. * The definition of real part is in createFromQuat().
  263. * @sa createFromQuat, getDualPart
  264. */
  265. Quat<_Tp> getRealPart() const;
  266. /**
  267. * @brief return a quaternion which represent the dual part of dual quaternion.
  268. * The definition of dual part is in createFromQuat().
  269. * @sa createFromQuat, getRealPart
  270. */
  271. Quat<_Tp> getDualPart() const;
  272. /**
  273. * @brief return the conjugate of a dual quaternion.
  274. * \f[
  275. * \begin{equation}
  276. * \begin{split}
  277. * \sigma^* &= (p + \epsilon q)^*
  278. * &= (p^* + \epsilon q^*)
  279. * \end{split}
  280. * \end{equation}
  281. * \f]
  282. * @param dq a dual quaternion.
  283. */
  284. template <typename T>
  285. friend DualQuat<T> conjugate(const DualQuat<T> &dq);
  286. /**
  287. * @brief return the conjugate of a dual quaternion.
  288. * \f[
  289. * \begin{equation}
  290. * \begin{split}
  291. * \sigma^* &= (p + \epsilon q)^*
  292. * &= (p^* + \epsilon q^*)
  293. * \end{split}
  294. * \end{equation}
  295. * \f]
  296. */
  297. DualQuat<_Tp> conjugate() const;
  298. /**
  299. * @brief return the rotation in quaternion form.
  300. */
  301. Quat<_Tp> getRotation(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  302. /**
  303. * @brief return the translation vector.
  304. * The rotation \f$r\f$ in this dual quaternion \f$\sigma\f$ is applied before translation \f$t\f$.
  305. * The dual quaternion \f$\sigma\f$ is defined as
  306. * \f[\begin{equation}
  307. * \begin{split}
  308. * \sigma &= p + \epsilon q \\
  309. * &= r + \frac{\epsilon}{2}{t}r.
  310. * \end{split}
  311. * \end{equation}\f]
  312. * Thus, the translation can be obtained as follows
  313. * \f[t = 2qp^*.\f]
  314. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  315. * and this function will save some computations.
  316. * @note This dual quaternion's translation is applied after the rotation.
  317. */
  318. Vec<_Tp, 3> getTranslation(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  319. /**
  320. * @brief return the norm \f$||\sigma||\f$ of dual quaternion \f$\sigma = p + \epsilon q\f$.
  321. * \f[
  322. * \begin{equation}
  323. * \begin{split}
  324. * ||\sigma|| &= \sqrt{\sigma * \sigma^*} \\
  325. * &= ||p|| + \epsilon \frac{p \cdot q}{||p||}.
  326. * \end{split}
  327. * \end{equation}
  328. * \f]
  329. * Generally speaking, the norm of a not unit dual
  330. * quaternion is a dual number. For convenience, we return it in the form of a dual quaternion
  331. * , i.e.
  332. * \f[ ||\sigma|| = [||p||, 0, 0, 0, \frac{p \cdot q}{||p||}, 0, 0, 0].\f]
  333. *
  334. * @note The data type of dual number is dual quaternion.
  335. */
  336. DualQuat<_Tp> norm() const;
  337. /**
  338. * @brief return a normalized dual quaternion.
  339. * A dual quaternion can be expressed as
  340. * \f[
  341. * \begin{equation}
  342. * \begin{split}
  343. * \sigma &= p + \epsilon q\\
  344. * &=||\sigma||\left(r+\frac{1}{2}tr\right)
  345. * \end{split}
  346. * \end{equation}
  347. * \f]
  348. * where \f$r, t\f$ represents the rotation (ordinary quaternion) and translation (pure ordinary quaternion) respectively,
  349. * and \f$||\sigma||\f$ is the norm of dual quaternion(a dual number).
  350. * A dual quaternion is unit if and only if
  351. * \f[
  352. * ||p||=1, p \cdot q=0
  353. * \f]
  354. * where \f$\cdot\f$ means dot product.
  355. * The process of normalization is
  356. * \f[
  357. * \sigma_{u}=\frac{\sigma}{||\sigma||}
  358. * \f]
  359. * Next, we simply proof \f$\sigma_u\f$ is a unit dual quaternion:
  360. * \f[
  361. * \renewcommand{\Im}{\operatorname{Im}}
  362. * \begin{equation}
  363. * \begin{split}
  364. * \sigma_{u}=\frac{\sigma}{||\sigma||}&=\frac{p + \epsilon q}{||p||+\epsilon\frac{p\cdot q}{||p||}}\\
  365. * &=\frac{p}{||p||}+\epsilon\left(\frac{q}{||p||}-p\frac{p\cdot q}{||p||^3}\right)\\
  366. * &=\frac{p}{||p||}+\epsilon\frac{1}{||p||^2}\left(qp^{*}-p\cdot q\right)\frac{p}{||p||}\\
  367. * &=\frac{p}{||p||}+\epsilon\frac{1}{||p||^2}\Im(qp^*)\frac{p}{||p||}.\\
  368. * \end{split}
  369. * \end{equation}
  370. * \f]
  371. * As expected, the real part is a rotation and dual part is a pure quaternion.
  372. */
  373. DualQuat<_Tp> normalize() const;
  374. /**
  375. * @brief if \f$\sigma = p + \epsilon q\f$ is a dual quaternion, p is not zero,
  376. * the inverse dual quaternion is
  377. * \f[\sigma^{-1} = \frac{\sigma^*}{||\sigma||^2}, \f]
  378. * or equivalentlly,
  379. * \f[\sigma^{-1} = p^{-1} - \epsilon p^{-1}qp^{-1}.\f]
  380. * @param dq a dual quaternion.
  381. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, dual quaternion dq assume to be a unit dual quaternion
  382. * and this function will save some computations.
  383. */
  384. template <typename T>
  385. friend DualQuat<T> inv(const DualQuat<T> &dq, QuatAssumeType assumeUnit);
  386. /**
  387. * @brief if \f$\sigma = p + \epsilon q\f$ is a dual quaternion, p is not zero,
  388. * the inverse dual quaternion is
  389. * \f[\sigma^{-1} = \frac{\sigma^*}{||\sigma||^2}, \f]
  390. * or equivalentlly,
  391. * \f[\sigma^{-1} = p^{-1} - \epsilon p^{-1}qp^{-1}.\f]
  392. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  393. * and this function will save some computations.
  394. */
  395. DualQuat<_Tp> inv(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  396. /**
  397. * @brief return the dot product of two dual quaternion.
  398. * @param p other dual quaternion.
  399. */
  400. _Tp dot(DualQuat<_Tp> p) const;
  401. /**
  402. ** @brief return the value of \f$p^t\f$ where p is a dual quaternion.
  403. * This could be calculated as:
  404. * \f[
  405. * p^t = \exp(t\ln p)
  406. * \f]
  407. * @param dq a dual quaternion.
  408. * @param t index of power function.
  409. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, dual quaternion dq assume to be a unit dual quaternion
  410. * and this function will save some computations.
  411. */
  412. template <typename T>
  413. friend DualQuat<T> power(const DualQuat<T> &dq, const T t, QuatAssumeType assumeUnit);
  414. /**
  415. ** @brief return the value of \f$p^t\f$ where p is a dual quaternion.
  416. * This could be calculated as:
  417. * \f[
  418. * p^t = \exp(t\ln p)
  419. * \f]
  420. *
  421. * @param t index of power function.
  422. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  423. * and this function will save some computations.
  424. */
  425. DualQuat<_Tp> power(const _Tp t, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  426. /**
  427. * @brief return the value of \f$p^q\f$ where p and q are dual quaternions.
  428. * This could be calculated as:
  429. * \f[
  430. * p^q = \exp(q\ln p)
  431. * \f]
  432. * @param p a dual quaternion.
  433. * @param q a dual quaternion.
  434. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, dual quaternion p assume to be a dual unit quaternion
  435. * and this function will save some computations.
  436. */
  437. template <typename T>
  438. friend DualQuat<T> power(const DualQuat<T>& p, const DualQuat<T>& q, QuatAssumeType assumeUnit);
  439. /**
  440. * @brief return the value of \f$p^q\f$ where p and q are dual quaternions.
  441. * This could be calculated as:
  442. * \f[
  443. * p^q = \exp(q\ln p)
  444. * \f]
  445. *
  446. * @param q a dual quaternion
  447. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a dual unit quaternion
  448. * and this function will save some computations.
  449. */
  450. DualQuat<_Tp> power(const DualQuat<_Tp>& q, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  451. /**
  452. * @brief return the value of exponential function value
  453. * @param dq a dual quaternion.
  454. */
  455. template <typename T>
  456. friend DualQuat<T> exp(const DualQuat<T> &dq);
  457. /**
  458. * @brief return the value of exponential function value
  459. */
  460. DualQuat<_Tp> exp() const;
  461. /**
  462. * @brief return the value of logarithm function value
  463. *
  464. * @param dq a dual quaternion.
  465. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, dual quaternion dq assume to be a unit dual quaternion
  466. * and this function will save some computations.
  467. */
  468. template <typename T>
  469. friend DualQuat<T> log(const DualQuat<T> &dq, QuatAssumeType assumeUnit);
  470. /**
  471. * @brief return the value of logarithm function value
  472. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  473. * and this function will save some computations.
  474. */
  475. DualQuat<_Tp> log(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  476. /**
  477. * @brief Transform this dual quaternion to a vector.
  478. */
  479. Vec<_Tp, 8> toVec() const;
  480. /**
  481. * @brief Transform this dual quaternion to a affine transformation matrix
  482. * the form of matrix, see createFromMat().
  483. */
  484. Matx<_Tp, 4, 4> toMat(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  485. /**
  486. * @brief Transform this dual quaternion to a instance of Affine3.
  487. */
  488. Affine3<_Tp> toAffine3(QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) const;
  489. /**
  490. * @brief The screw linear interpolation(ScLERP) is an extension of spherical linear interpolation of dual quaternion.
  491. * If \f$\sigma_1\f$ and \f$\sigma_2\f$ are two dual quaternions representing the initial and final pose.
  492. * The interpolation of ScLERP function can be defined as:
  493. * \f[
  494. * ScLERP(t;\sigma_1,\sigma_2) = \sigma_1 * (\sigma_1^{-1} * \sigma_2)^t, t\in[0,1]
  495. * \f]
  496. *
  497. * @param q1 a dual quaternion represents a initial pose.
  498. * @param q2 a dual quaternion represents a final pose.
  499. * @param t interpolation parameter
  500. * @param directChange if true, it always return the shortest path.
  501. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  502. * and this function will save some computations.
  503. *
  504. * For example
  505. * ```
  506. * double angle1 = CV_PI / 2;
  507. * Vec3d axis{0, 0, 1};
  508. * Vec3d t(0, 0, 3);
  509. * DualQuatd initial = DualQuatd::createFromAngleAxisTrans(angle1, axis, t);
  510. * double angle2 = CV_PI;
  511. * DualQuatd final = DualQuatd::createFromAngleAxisTrans(angle2, axis, t);
  512. * DualQuatd inter = DualQuatd::sclerp(initial, final, 0.5);
  513. * ```
  514. */
  515. static DualQuat<_Tp> sclerp(const DualQuat<_Tp> &q1, const DualQuat<_Tp> &q2, const _Tp t,
  516. bool directChange=true, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT);
  517. /**
  518. * @brief The method of Dual Quaternion linear Blending(DQB) is to compute a transformation between dual quaternion
  519. * \f$q_1\f$ and \f$q_2\f$ and can be defined as:
  520. * \f[
  521. * DQB(t;{\boldsymbol{q}}_1,{\boldsymbol{q}}_2)=
  522. * \frac{(1-t){\boldsymbol{q}}_1+t{\boldsymbol{q}}_2}{||(1-t){\boldsymbol{q}}_1+t{\boldsymbol{q}}_2||}.
  523. * \f]
  524. * where \f$q_1\f$ and \f$q_2\f$ are unit dual quaternions representing the input transformations.
  525. * If you want to use DQB that works for more than two rigid transformations, see @ref gdqblend
  526. *
  527. * @param q1 a unit dual quaternion representing the input transformations.
  528. * @param q2 a unit dual quaternion representing the input transformations.
  529. * @param t parameter \f$t\in[0,1]\f$.
  530. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, this dual quaternion assume to be a unit dual quaternion
  531. * and this function will save some computations.
  532. *
  533. * @sa gdqblend
  534. */
  535. static DualQuat<_Tp> dqblend(const DualQuat<_Tp> &q1, const DualQuat<_Tp> &q2, const _Tp t,
  536. QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT);
  537. /**
  538. * @brief The generalized Dual Quaternion linear Blending works for more than two rigid transformations.
  539. * If these transformations are expressed as unit dual quaternions \f$q_1,...,q_n\f$ with convex weights
  540. * \f$w = (w_1,...,w_n)\f$, the generalized DQB is simply
  541. * \f[
  542. * gDQB(\boldsymbol{w};{\boldsymbol{q}}_1,...,{\boldsymbol{q}}_n)=\frac{w_1{\boldsymbol{q}}_1+...+w_n{\boldsymbol{q}}_n}
  543. * {||w_1{\boldsymbol{q}}_1+...+w_n{\boldsymbol{q}}_n||}.
  544. * \f]
  545. * @param dualquat vector of dual quaternions
  546. * @param weights vector of weights, the size of weights should be the same as dualquat, and the weights should
  547. * satisfy \f$\sum_0^n w_{i} = 1\f$ and \f$w_i>0\f$.
  548. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, these dual quaternions assume to be unit quaternions
  549. * and this function will save some computations.
  550. * @note the type of weights' element should be the same as the date type of dual quaternion inside the dualquat.
  551. */
  552. template <int cn>
  553. static DualQuat<_Tp> gdqblend(const Vec<DualQuat<_Tp>, cn> &dualquat, InputArray weights,
  554. QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT);
  555. /**
  556. * @brief The generalized Dual Quaternion linear Blending works for more than two rigid transformations.
  557. * If these transformations are expressed as unit dual quaternions \f$q_1,...,q_n\f$ with convex weights
  558. * \f$w = (w_1,...,w_n)\f$, the generalized DQB is simply
  559. * \f[
  560. * gDQB(\boldsymbol{w};{\boldsymbol{q}}_1,...,{\boldsymbol{q}}_n)=\frac{w_1{\boldsymbol{q}}_1+...+w_n{\boldsymbol{q}}_n}
  561. * {||w_1{\boldsymbol{q}}_1+...+w_n{\boldsymbol{q}}_n||}.
  562. * \f]
  563. * @param dualquat The dual quaternions which have 8 channels and 1 row or 1 col.
  564. * @param weights vector of weights, the size of weights should be the same as dualquat, and the weights should
  565. * satisfy \f$\sum_0^n w_{i} = 1\f$ and \f$w_i>0\f$.
  566. * @param assumeUnit if @ref QUAT_ASSUME_UNIT, these dual quaternions assume to be unit quaternions
  567. * and this function will save some computations.
  568. * @note the type of weights' element should be the same as the date type of dual quaternion inside the dualquat.
  569. */
  570. static DualQuat<_Tp> gdqblend(InputArray dualquat, InputArray weights,
  571. QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT);
  572. /**
  573. * @brief Return opposite dual quaternion \f$-p\f$
  574. * which satisfies \f$p + (-p) = 0.\f$
  575. *
  576. * For example
  577. * ```
  578. * DualQuatd q{1, 2, 3, 4, 5, 6, 7, 8};
  579. * std::cout << -q << std::endl; // [-1, -2, -3, -4, -5, -6, -7, -8]
  580. * ```
  581. */
  582. DualQuat<_Tp> operator-() const;
  583. /**
  584. * @brief return true if two dual quaternions p and q are nearly equal, i.e. when the absolute
  585. * value of each \f$p_i\f$ and \f$q_i\f$ is less than CV_DUAL_QUAT_EPS.
  586. */
  587. bool operator==(const DualQuat<_Tp>&) const;
  588. /**
  589. * @brief Subtraction operator of two dual quaternions p and q.
  590. * It returns a new dual quaternion that each value is the sum of \f$p_i\f$ and \f$-q_i\f$.
  591. *
  592. * For example
  593. * ```
  594. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  595. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  596. * std::cout << p - q << std::endl; //[-4, -4, -4, -4, 4, -4, -4, -4]
  597. * ```
  598. */
  599. DualQuat<_Tp> operator-(const DualQuat<_Tp>&) const;
  600. /**
  601. * @brief Subtraction assignment operator of two dual quaternions p and q.
  602. * It subtracts right operand from the left operand and assign the result to left operand.
  603. *
  604. * For example
  605. * ```
  606. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  607. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  608. * p -= q; // equivalent to p = p - q
  609. * std::cout << p << std::endl; //[-4, -4, -4, -4, 4, -4, -4, -4]
  610. *
  611. * ```
  612. */
  613. DualQuat<_Tp>& operator-=(const DualQuat<_Tp>&);
  614. /**
  615. * @brief Addition operator of two dual quaternions p and q.
  616. * It returns a new dual quaternion that each value is the sum of \f$p_i\f$ and \f$q_i\f$.
  617. *
  618. * For example
  619. * ```
  620. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  621. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  622. * std::cout << p + q << std::endl; //[6, 8, 10, 12, 14, 16, 18, 20]
  623. * ```
  624. */
  625. DualQuat<_Tp> operator+(const DualQuat<_Tp>&) const;
  626. /**
  627. * @brief Addition assignment operator of two dual quaternions p and q.
  628. * It adds right operand to the left operand and assign the result to left operand.
  629. *
  630. * For example
  631. * ```
  632. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  633. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  634. * p += q; // equivalent to p = p + q
  635. * std::cout << p << std::endl; //[6, 8, 10, 12, 14, 16, 18, 20]
  636. *
  637. * ```
  638. */
  639. DualQuat<_Tp>& operator+=(const DualQuat<_Tp>&);
  640. /**
  641. * @brief Multiplication assignment operator of two quaternions.
  642. * It multiplies right operand with the left operand and assign the result to left operand.
  643. *
  644. * Rule of dual quaternion multiplication:
  645. * The dual quaternion can be written as an ordered pair of quaternions [A, B]. Thus
  646. * \f[
  647. * \begin{equation}
  648. * \begin{split}
  649. * p * q &= [A, B][C, D]\\
  650. * &=[AC, AD + BC]
  651. * \end{split}
  652. * \end{equation}
  653. * \f]
  654. *
  655. * For example
  656. * ```
  657. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  658. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  659. * p *= q;
  660. * std::cout << p << std::endl; //[-60, 12, 30, 24, -216, 80, 124, 120]
  661. * ```
  662. */
  663. DualQuat<_Tp>& operator*=(const DualQuat<_Tp>&);
  664. /**
  665. * @brief Multiplication assignment operator of a quaternions and a scalar.
  666. * It multiplies right operand with the left operand and assign the result to left operand.
  667. *
  668. * Rule of dual quaternion multiplication with a scalar:
  669. * \f[
  670. * \begin{equation}
  671. * \begin{split}
  672. * p * s &= [w, x, y, z, w\_, x\_, y\_, z\_] * s\\
  673. * &=[w s, x s, y s, z s, w\_ \space s, x\_ \space s, y\_ \space s, z\_ \space s].
  674. * \end{split}
  675. * \end{equation}
  676. * \f]
  677. *
  678. * For example
  679. * ```
  680. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  681. * double s = 2.0;
  682. * p *= s;
  683. * std::cout << p << std::endl; //[2, 4, 6, 8, 10, 12, 14, 16]
  684. * ```
  685. * @note the type of scalar should be equal to the dual quaternion.
  686. */
  687. DualQuat<_Tp> operator*=(const _Tp s);
  688. /**
  689. * @brief Multiplication operator of two dual quaternions q and p.
  690. * Multiplies values on either side of the operator.
  691. *
  692. * Rule of dual quaternion multiplication:
  693. * The dual quaternion can be written as an ordered pair of quaternions [A, B]. Thus
  694. * \f[
  695. * \begin{equation}
  696. * \begin{split}
  697. * p * q &= [A, B][C, D]\\
  698. * &=[AC, AD + BC]
  699. * \end{split}
  700. * \end{equation}
  701. * \f]
  702. *
  703. * For example
  704. * ```
  705. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  706. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  707. * std::cout << p * q << std::endl; //[-60, 12, 30, 24, -216, 80, 124, 120]
  708. * ```
  709. */
  710. DualQuat<_Tp> operator*(const DualQuat<_Tp>&) const;
  711. /**
  712. * @brief Division operator of a dual quaternions and a scalar.
  713. * It divides left operand with the right operand and assign the result to left operand.
  714. *
  715. * Rule of dual quaternion division with a scalar:
  716. * \f[
  717. * \begin{equation}
  718. * \begin{split}
  719. * p / s &= [w, x, y, z, w\_, x\_, y\_, z\_] / s\\
  720. * &=[w/s, x/s, y/s, z/s, w\_/s, x\_/s, y\_/s, z\_/s].
  721. * \end{split}
  722. * \end{equation}
  723. * \f]
  724. *
  725. * For example
  726. * ```
  727. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  728. * double s = 2.0;
  729. * p /= s; // equivalent to p = p / s
  730. * std::cout << p << std::endl; //[0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4]
  731. * ```
  732. * @note the type of scalar should be equal to this dual quaternion.
  733. */
  734. DualQuat<_Tp> operator/(const _Tp s) const;
  735. /**
  736. * @brief Division operator of two dual quaternions p and q.
  737. * Divides left hand operand by right hand operand.
  738. *
  739. * Rule of dual quaternion division with a dual quaternion:
  740. * \f[
  741. * \begin{equation}
  742. * \begin{split}
  743. * p / q &= p * q.inv()\\
  744. * \end{split}
  745. * \end{equation}
  746. * \f]
  747. *
  748. * For example
  749. * ```
  750. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  751. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  752. * std::cout << p / q << std::endl; // equivalent to p * q.inv()
  753. * ```
  754. */
  755. DualQuat<_Tp> operator/(const DualQuat<_Tp>&) const;
  756. /**
  757. * @brief Division assignment operator of two dual quaternions p and q;
  758. * It divides left operand with the right operand and assign the result to left operand.
  759. *
  760. * Rule of dual quaternion division with a quaternion:
  761. * \f[
  762. * \begin{equation}
  763. * \begin{split}
  764. * p / q&= p * q.inv()\\
  765. * \end{split}
  766. * \end{equation}
  767. * \f]
  768. *
  769. * For example
  770. * ```
  771. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  772. * DualQuatd q{5, 6, 7, 8, 9, 10, 11, 12};
  773. * p /= q; // equivalent to p = p * q.inv()
  774. * std::cout << p << std::endl;
  775. * ```
  776. */
  777. DualQuat<_Tp>& operator/=(const DualQuat<_Tp>&);
  778. /**
  779. * @brief Division assignment operator of a dual quaternions and a scalar.
  780. * It divides left operand with the right operand and assign the result to left operand.
  781. *
  782. * Rule of dual quaternion division with a scalar:
  783. * \f[
  784. * \begin{equation}
  785. * \begin{split}
  786. * p / s &= [w, x, y, z, w\_, x\_, y\_ ,z\_] / s\\
  787. * &=[w / s, x / s, y / s, z / s, w\_ / \space s, x\_ / \space s, y\_ / \space s, z\_ / \space s].
  788. * \end{split}
  789. * \end{equation}
  790. * \f]
  791. *
  792. * For example
  793. * ```
  794. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  795. * double s = 2.0;;
  796. * p /= s; // equivalent to p = p / s
  797. * std::cout << p << std::endl; //[0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
  798. * ```
  799. * @note the type of scalar should be equal to the dual quaternion.
  800. */
  801. Quat<_Tp>& operator/=(const _Tp s);
  802. /**
  803. * @brief Addition operator of a scalar and a dual quaternions.
  804. * Adds right hand operand from left hand operand.
  805. *
  806. * For example
  807. * ```
  808. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  809. * double scalar = 2.0;
  810. * std::cout << scalar + p << std::endl; //[3.0, 2, 3, 4, 5, 6, 7, 8]
  811. * ```
  812. * @note the type of scalar should be equal to the dual quaternion.
  813. */
  814. template <typename T>
  815. friend DualQuat<T> cv::operator+(const T s, const DualQuat<T>&);
  816. /**
  817. * @brief Addition operator of a dual quaternions and a scalar.
  818. * Adds right hand operand from left hand operand.
  819. *
  820. * For example
  821. * ```
  822. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  823. * double scalar = 2.0;
  824. * std::cout << p + scalar << std::endl; //[3.0, 2, 3, 4, 5, 6, 7, 8]
  825. * ```
  826. * @note the type of scalar should be equal to the dual quaternion.
  827. */
  828. template <typename T>
  829. friend DualQuat<T> cv::operator+(const DualQuat<T>&, const T s);
  830. /**
  831. * @brief Multiplication operator of a scalar and a dual quaternions.
  832. * It multiplies right operand with the left operand and assign the result to left operand.
  833. *
  834. * Rule of dual quaternion multiplication with a scalar:
  835. * \f[
  836. * \begin{equation}
  837. * \begin{split}
  838. * p * s &= [w, x, y, z, w\_, x\_, y\_, z\_] * s\\
  839. * &=[w s, x s, y s, z s, w\_ \space s, x\_ \space s, y\_ \space s, z\_ \space s].
  840. * \end{split}
  841. * \end{equation}
  842. * \f]
  843. *
  844. * For example
  845. * ```
  846. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  847. * double s = 2.0;
  848. * std::cout << s * p << std::endl; //[2, 4, 6, 8, 10, 12, 14, 16]
  849. * ```
  850. * @note the type of scalar should be equal to the dual quaternion.
  851. */
  852. template <typename T>
  853. friend DualQuat<T> cv::operator*(const T s, const DualQuat<T>&);
  854. /**
  855. * @brief Subtraction operator of a dual quaternion and a scalar.
  856. * Subtracts right hand operand from left hand operand.
  857. *
  858. * For example
  859. * ```
  860. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  861. * double scalar = 2.0;
  862. * std::cout << p - scalar << std::endl; //[-1, 2, 3, 4, 5, 6, 7, 8]
  863. * ```
  864. * @note the type of scalar should be equal to the dual quaternion.
  865. */
  866. template <typename T>
  867. friend DualQuat<T> cv::operator-(const DualQuat<T>&, const T s);
  868. /**
  869. * @brief Subtraction operator of a scalar and a dual quaternions.
  870. * Subtracts right hand operand from left hand operand.
  871. *
  872. * For example
  873. * ```
  874. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  875. * double scalar = 2.0;
  876. * std::cout << scalar - p << std::endl; //[1.0, -2, -3, -4, -5, -6, -7, -8]
  877. * ```
  878. * @note the type of scalar should be equal to the dual quaternion.
  879. */
  880. template <typename T>
  881. friend DualQuat<T> cv::operator-(const T s, const DualQuat<T>&);
  882. /**
  883. * @brief Multiplication operator of a dual quaternions and a scalar.
  884. * It multiplies right operand with the left operand and assign the result to left operand.
  885. *
  886. * Rule of dual quaternion multiplication with a scalar:
  887. * \f[
  888. * \begin{equation}
  889. * \begin{split}
  890. * p * s &= [w, x, y, z, w\_, x\_, y\_, z\_] * s\\
  891. * &=[w s, x s, y s, z s, w\_ \space s, x\_ \space s, y\_ \space s, z\_ \space s].
  892. * \end{split}
  893. * \end{equation}
  894. * \f]
  895. *
  896. * For example
  897. * ```
  898. * DualQuatd p{1, 2, 3, 4, 5, 6, 7, 8};
  899. * double s = 2.0;
  900. * std::cout << p * s << std::endl; //[2, 4, 6, 8, 10, 12, 14, 16]
  901. * ```
  902. * @note the type of scalar should be equal to the dual quaternion.
  903. */
  904. template <typename T>
  905. friend DualQuat<T> cv::operator*(const DualQuat<T>&, const T s);
  906. template <typename S>
  907. friend std::ostream& cv::operator<<(std::ostream&, const DualQuat<S>&);
  908. };
  909. using DualQuatd = DualQuat<double>;
  910. using DualQuatf = DualQuat<float>;
  911. //! @} core
  912. }//namespace
  913. #include "dualquaternion.inl.hpp"
  914. #endif /* OPENCV_CORE_QUATERNION_HPP */