// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2020, Huawei Technologies Co., Ltd. All rights reserved. // Third party copyrights are property of their respective owners. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: Liangqian Kong // Longbu Wang #ifndef OPENCV_CORE_DUALQUATERNION_INL_HPP #define OPENCV_CORE_DUALQUATERNION_INL_HPP #ifndef OPENCV_CORE_DUALQUATERNION_HPP #error This is not a standalone header. Include dualquaternion.hpp instead. #endif /////////////////////////////////////////////////////////////////////////////////////// //Implementation namespace cv { template DualQuat::DualQuat():w(0), x(0), y(0), z(0), w_(0), x_(0), y_(0), z_(0){}; template DualQuat::DualQuat(const T vw, const T vx, const T vy, const T vz, const T _w, const T _x, const T _y, const T _z): w(vw), x(vx), y(vy), z(vz), w_(_w), x_(_x), y_(_y), z_(_z){}; template DualQuat::DualQuat(const Vec &q):w(q[0]), x(q[1]), y(q[2]), z(q[3]), w_(q[4]), x_(q[5]), y_(q[6]), z_(q[7]){}; template DualQuat DualQuat::createFromQuat(const Quat &realPart, const Quat &dualPart) { T w = realPart.w; T x = realPart.x; T y = realPart.y; T z = realPart.z; T w_ = dualPart.w; T x_ = dualPart.x; T y_ = dualPart.y; T z_ = dualPart.z; return DualQuat(w, x, y, z, w_, x_, y_, z_); } template DualQuat DualQuat::createFromAngleAxisTrans(const T angle, const Vec &axis, const Vec &trans) { Quat r = Quat::createFromAngleAxis(angle, axis); Quat t{0, trans[0], trans[1], trans[2]}; return createFromQuat(r, t * r / 2); } template DualQuat DualQuat::createFromMat(InputArray _R) { CV_CheckTypeEQ(_R.type(), cv::traits::Type::value, ""); if (_R.size() != Size(4, 4)) { CV_Error(Error::StsBadArg, "The input matrix must have 4 columns and 4 rows"); } Mat R = _R.getMat(); Quat r = Quat::createFromRotMat(R.colRange(0, 3).rowRange(0, 3)); Quat trans(0, R.at(0, 3), R.at(1, 3), R.at(2, 3)); return createFromQuat(r, trans * r / 2); } template DualQuat DualQuat::createFromAffine3(const Affine3 &R) { return createFromMat(R.matrix); } template DualQuat DualQuat::createFromPitch(const T angle, const T d, const Vec &axis, const Vec &moment) { T half_angle = angle / 2, half_d = d / 2; Quat qaxis = Quat(0, axis[0], axis[1], axis[2]).normalize(); Quat qmoment = Quat(0, moment[0], moment[1], moment[2]); qmoment -= qaxis * axis.dot(moment); Quat dual = -half_d * std::sin(half_angle) + std::sin(half_angle) * qmoment + half_d * std::cos(half_angle) * qaxis; return createFromQuat(Quat::createFromAngleAxis(angle, axis), dual); } template inline bool DualQuat::operator==(const DualQuat &q) const { return (abs(w - q.w) < CV_DUAL_QUAT_EPS && abs(x - q.x) < CV_DUAL_QUAT_EPS && abs(y - q.y) < CV_DUAL_QUAT_EPS && abs(z - q.z) < CV_DUAL_QUAT_EPS && abs(w_ - q.w_) < CV_DUAL_QUAT_EPS && abs(x_ - q.x_) < CV_DUAL_QUAT_EPS && abs(y_ - q.y_) < CV_DUAL_QUAT_EPS && abs(z_ - q.z_) < CV_DUAL_QUAT_EPS); } template inline Quat DualQuat::getRealPart() const { return Quat(w, x, y, z); } template inline Quat DualQuat::getDualPart() const { return Quat(w_, x_, y_, z_); } template inline DualQuat conjugate(const DualQuat &dq) { return dq.conjugate(); } template inline DualQuat DualQuat::conjugate() const { return DualQuat(w, -x, -y, -z, w_, -x_, -y_, -z_); } template DualQuat DualQuat::norm() const { Quat real = getRealPart(); T realNorm = real.norm(); Quat dual = getDualPart(); if (realNorm < CV_DUAL_QUAT_EPS){ return DualQuat(0, 0, 0, 0, 0, 0, 0, 0); } return DualQuat(realNorm, 0, 0, 0, real.dot(dual) / realNorm, 0, 0, 0); } template inline Quat DualQuat::getRotation(QuatAssumeType assumeUnit) const { if (assumeUnit) { return getRealPart(); } return getRealPart().normalize(); } template inline Vec DualQuat::getTranslation(QuatAssumeType assumeUnit) const { Quat trans = 2.0 * (getDualPart() * getRealPart().inv(assumeUnit)); return Vec{trans[1], trans[2], trans[3]}; } template DualQuat DualQuat::normalize() const { Quat p = getRealPart(); Quat q = getDualPart(); T p_norm = p.norm(); if (p_norm < CV_DUAL_QUAT_EPS) { CV_Error(Error::StsBadArg, "Cannot normalize this dual quaternion: the norm is too small."); } Quat p_nr = p / p_norm; Quat q_nr = q / p_norm; return createFromQuat(p_nr, q_nr - p_nr * p_nr.dot(q_nr)); } template inline T DualQuat::dot(DualQuat q) const { return q.w * w + q.x * x + q.y * y + q.z * z + q.w_ * w_ + q.x_ * x_ + q.y_ * y_ + q.z_ * z_; } template inline DualQuat inv(const DualQuat &dq, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) { return dq.inv(assumeUnit); } template inline DualQuat DualQuat::inv(QuatAssumeType assumeUnit) const { Quat real = getRealPart(); Quat dual = getDualPart(); return createFromQuat(real.inv(assumeUnit), -real.inv(assumeUnit) * dual * real.inv(assumeUnit)); } template inline DualQuat DualQuat::operator-(const DualQuat &q) const { return DualQuat(w - q.w, x - q.x, y - q.y, z - q.z, w_ - q.w_, x_ - q.x_, y_ - q.y_, z_ - q.z_); } template inline DualQuat DualQuat::operator-() const { return DualQuat(-w, -x, -y, -z, -w_, -x_, -y_, -z_); } template inline DualQuat DualQuat::operator+(const DualQuat &q) const { return DualQuat(w + q.w, x + q.x, y + q.y, z + q.z, w_ + q.w_, x_ + q.x_, y_ + q.y_, z_ + q.z_); } template inline DualQuat& DualQuat::operator+=(const DualQuat &q) { *this = *this + q; return *this; } template inline DualQuat DualQuat::operator*(const DualQuat &q) const { Quat A = getRealPart(); Quat B = getDualPart(); Quat C = q.getRealPart(); Quat D = q.getDualPart(); return DualQuat::createFromQuat(A * C, A * D + B * C); } template inline DualQuat& DualQuat::operator*=(const DualQuat &q) { *this = *this * q; return *this; } template inline DualQuat operator+(const T a, const DualQuat &q) { return DualQuat(a + q.w, q.x, q.y, q.z, q.w_, q.x_, q.y_, q.z_); } template inline DualQuat operator+(const DualQuat &q, const T a) { return DualQuat(a + q.w, q.x, q.y, q.z, q.w_, q.x_, q.y_, q.z_); } template inline DualQuat operator-(const DualQuat &q, const T a) { return DualQuat(q.w - a, q.x, q.y, q.z, q.w_, q.x_, q.y_, q.z_); } template inline DualQuat& DualQuat::operator-=(const DualQuat &q) { *this = *this - q; return *this; } template inline DualQuat operator-(const T a, const DualQuat &q) { return DualQuat(a - q.w, -q.x, -q.y, -q.z, -q.w_, -q.x_, -q.y_, -q.z_); } template inline DualQuat operator*(const T a, const DualQuat &q) { return DualQuat(q.w * a, q.x * a, q.y * a, q.z * a, q.w_ * a, q.x_ * a, q.y_ * a, q.z_ * a); } template inline DualQuat operator*(const DualQuat &q, const T a) { return DualQuat(q.w * a, q.x * a, q.y * a, q.z * a, q.w_ * a, q.x_ * a, q.y_ * a, q.z_ * a); } template inline DualQuat DualQuat::operator/(const T a) const { return DualQuat(w / a, x / a, y / a, z / a, w_ / a, x_ / a, y_ / a, z_ / a); } template inline DualQuat DualQuat::operator/(const DualQuat &q) const { return *this * q.inv(); } template inline DualQuat& DualQuat::operator/=(const DualQuat &q) { *this = *this / q; return *this; } template std::ostream & operator<<(std::ostream &os, const DualQuat &q) { os << "DualQuat " << Vec{q.w, q.x, q.y, q.z, q.w_, q.x_, q.y_, q.z_}; return os; } template inline DualQuat exp(const DualQuat &dq) { return dq.exp(); } namespace detail { template Matx<_Tp, 4, 4> jacob_exp(const Quat<_Tp> &q) { _Tp nv = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z); _Tp sinc_nv = abs(nv) < cv::DualQuat<_Tp>::CV_DUAL_QUAT_EPS ? 1 - nv * nv / 6 : std::sin(nv) / nv; _Tp csiii_nv = abs(nv) < cv::DualQuat<_Tp>::CV_DUAL_QUAT_EPS ? -(_Tp)1.0 / 3 : (std::cos(nv) - sinc_nv) / nv / nv; Matx<_Tp, 4, 4> J_exp_quat { std::cos(nv), -sinc_nv * q.x, -sinc_nv * q.y, -sinc_nv * q.z, sinc_nv * q.x, csiii_nv * q.x * q.x + sinc_nv, csiii_nv * q.x * q.y, csiii_nv * q.x * q.z, sinc_nv * q.y, csiii_nv * q.y * q.x, csiii_nv * q.y * q.y + sinc_nv, csiii_nv * q.y * q.z, sinc_nv * q.z, csiii_nv * q.z * q.x, csiii_nv * q.z * q.y, csiii_nv * q.z * q.z + sinc_nv }; return std::exp(q.w) * J_exp_quat; } } // namespace detail template DualQuat DualQuat::exp() const { Quat real = getRealPart(); return createFromQuat(real.exp(), Quat(detail::jacob_exp(real) * getDualPart().toVec())); } template DualQuat log(const DualQuat &dq, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) { return dq.log(assumeUnit); } template DualQuat DualQuat::log(QuatAssumeType assumeUnit) const { Quat plog = getRealPart().log(assumeUnit); Matx jacob = detail::jacob_exp(plog); return createFromQuat(plog, Quat(jacob.inv() * getDualPart().toVec())); } template inline DualQuat power(const DualQuat &dq, const T t, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) { return dq.power(t, assumeUnit); } template inline DualQuat DualQuat::power(const T t, QuatAssumeType assumeUnit) const { return (t * log(assumeUnit)).exp(); } template inline DualQuat power(const DualQuat &p, const DualQuat &q, QuatAssumeType assumeUnit=QUAT_ASSUME_NOT_UNIT) { return p.power(q, assumeUnit); } template inline DualQuat DualQuat::power(const DualQuat &q, QuatAssumeType assumeUnit) const { return (q * log(assumeUnit)).exp(); } template inline Vec DualQuat::toVec() const { return Vec(w, x, y, z, w_, x_, y_, z_); } template Affine3 DualQuat::toAffine3(QuatAssumeType assumeUnit) const { return Affine3(toMat(assumeUnit)); } template Matx DualQuat::toMat(QuatAssumeType assumeUnit) const { Matx rot44 = getRotation(assumeUnit).toRotMat4x4(); Vec translation = getTranslation(assumeUnit); rot44(0, 3) = translation[0]; rot44(1, 3) = translation[1]; rot44(2, 3) = translation[2]; return rot44; } template DualQuat DualQuat::sclerp(const DualQuat &q0, const DualQuat &q1, const T t, bool directChange, QuatAssumeType assumeUnit) { DualQuat v0(q0), v1(q1); if (!assumeUnit) { v0 = v0.normalize(); v1 = v1.normalize(); } Quat v0Real = v0.getRealPart(); Quat v1Real = v1.getRealPart(); if (directChange && v1Real.dot(v0Real) < 0) { v0 = -v0; } DualQuat v0inv1 = v0.inv() * v1; return v0 * v0inv1.power(t, QUAT_ASSUME_UNIT); } template DualQuat DualQuat::dqblend(const DualQuat &q1, const DualQuat &q2, const T t, QuatAssumeType assumeUnit) { DualQuat v1(q1), v2(q2); if (!assumeUnit) { v1 = v1.normalize(); v2 = v2.normalize(); } if (v1.getRotation(assumeUnit).dot(v2.getRotation(assumeUnit)) < 0) { return ((1 - t) * v1 - t * v2).normalize(); } return ((1 - t) * v1 + t * v2).normalize(); } template DualQuat DualQuat::gdqblend(InputArray _dualquat, InputArray _weight, QuatAssumeType assumeUnit) { CV_CheckTypeEQ(_weight.type(), cv::traits::Type::value, ""); CV_CheckTypeEQ(_dualquat.type(), CV_MAKETYPE(CV_MAT_DEPTH(cv::traits::Type::value), 8), ""); Size dq_s = _dualquat.size(); if (dq_s != _weight.size() || (dq_s.height != 1 && dq_s.width != 1)) { CV_Error(Error::StsBadArg, "The size of weight must be the same as dualquat, both of them should be (1, n) or (n, 1)"); } Mat dualquat = _dualquat.getMat(), weight = _weight.getMat(); const int cn = std::max(dq_s.width, dq_s.height); if (!assumeUnit) { for (int i = 0; i < cn; ++i) { dualquat.at>(i) = DualQuat{dualquat.at>(i)}.normalize().toVec(); } } Vec dq_blend = dualquat.at>(0) * weight.at(0); Quat q0 = DualQuat {dualquat.at>(0)}.getRotation(assumeUnit); for (int i = 1; i < cn; ++i) { T k = q0.dot(DualQuat{dualquat.at>(i)}.getRotation(assumeUnit)) < 0 ? -1: 1; dq_blend = dq_blend + dualquat.at>(i) * k * weight.at(i); } return DualQuat{dq_blend}.normalize(); } template template DualQuat DualQuat::gdqblend(const Vec, cn> &_dualquat, InputArray _weight, QuatAssumeType assumeUnit) { Vec, cn> dualquat(_dualquat); if (cn == 0) { return DualQuat(1, 0, 0, 0, 0, 0, 0, 0); } Mat dualquat_mat(cn, 1, CV_64FC(8)); for (int i = 0; i < cn ; ++i) { dualquat_mat.at>(i) = dualquat[i].toVec(); } return gdqblend(dualquat_mat, _weight, assumeUnit); } } //namespace cv #endif /*OPENCV_CORE_DUALQUATERNION_INL_HPP*/