using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
namespace OpenCVForUnity.UnityUtils
{
public struct PoseData
{
public Vector3 pos;
public Quaternion rot;
}
///
/// AR utils.
///
public class ARUtils
{
///
/// Convertes rvec value to rotation transform.
///
/// Rvec.
/// Rotation.
public static Quaternion ConvertRvecToRot (double[] rvec)
{
if (rvec.Length < 3)
return new Quaternion ();
Vector3 _rvec = new Vector3 ((float)rvec[0], (float)rvec[1], (float)rvec[2]);
float theta = _rvec.magnitude;
_rvec.Normalize ();
// http://stackoverflow.com/questions/12933284/rodrigues-into-eulerangles-and-vice-versa
return Quaternion.AngleAxis (theta * Mathf.Rad2Deg, _rvec);
}
///
/// Convertes tvec value to position transform.
///
/// Tvec.
/// Position.
public static Vector3 ConvertTvecToPos (double[] tvec)
{
if (tvec.Length < 3)
return new Vector3 ();
return new Vector3 ((float)tvec[0], (float)tvec[1], (float)tvec[2]);
}
///
/// Convertes rvec and tvec value to PoseData.
///
/// Rvec.
/// Tvec.
/// PoseData.
public static PoseData ConvertRvecTvecToPoseData (double[] rvec, double[] tvec)
{
PoseData data = new PoseData ();
data.pos = ConvertTvecToPos (tvec);
data.rot = ConvertRvecToRot (rvec);
return data;
}
private static Vector3 vector_one = Vector3.one;
private static Matrix4x4 invertYMatrix = Matrix4x4.TRS (Vector3.zero, Quaternion.identity, new Vector3 (1, -1, 1));
private static Matrix4x4 invertZMatrix = Matrix4x4.TRS (Vector3.zero, Quaternion.identity, new Vector3 (1, 1, -1));
///
/// Convertes PoseData to transform matrix.
///
/// PoseData.
/// Determines if convert the transformation matrix to the left-hand coordinate system.
/// Determines if invert Z axis of the transform matrix.
/// Transform matrix.
public static Matrix4x4 ConvertPoseDataToMatrix (ref PoseData poseData, bool toLeftHandCoordinateSystem = false, bool invertZAxis = false)
{
Matrix4x4 matrix = Matrix4x4.TRS (poseData.pos, poseData.rot, vector_one);
// right-handed coordinates system (OpenCV) to left-handed one (Unity)
if (toLeftHandCoordinateSystem)
matrix = invertYMatrix * matrix;
// Apply Z axis inverted matrix.
if (invertZAxis)
matrix = matrix * invertZMatrix;
return matrix;
}
///
/// Convertes transform matrix to PoseData.
///
/// Transform matrix.
/// PoseData.
public static PoseData ConvertMatrixToPoseData (ref Matrix4x4 matrix)
{
PoseData data = new PoseData ();
data.pos = ExtractTranslationFromMatrix (ref matrix);
data.rot = ExtractRotationFromMatrix (ref matrix);
return data;
}
///
/// Creates pose data dictionary.
///
/// Marker count.
/// ids.
/// Rvecs.
/// Tvecs.
/// PoseData dictionary.
public static Dictionary CreatePoseDataDict (int markerCount, int[] ids, double[] rvecs, double[] tvecs)
{
Dictionary dict = new Dictionary ();
if (markerCount == 0) return dict;
Vector3 rvec = new Vector3 ();
for (int i = 0; i < markerCount; i++)
{
PoseData data = new PoseData ();
data.pos.Set ((float)tvecs[i * 3], (float)tvecs[i * 3 + 1], (float)tvecs[i * 3 + 2]);
rvec.Set ((float)rvecs[i * 3], (float)rvecs[i * 3 + 1], (float)rvecs[i * 3 + 2]);
float theta = rvec.magnitude;
rvec.Normalize ();
data.rot = Quaternion.AngleAxis (theta * Mathf.Rad2Deg, rvec);
dict[ids[i]] = data;
}
return dict;
}
///
/// Performs a lowpass check on the position and rotation in newPose, comparing them to oldPose.
///
/// Old PoseData.
/// New PoseData.
/// Positon threshold.
/// Rotation threshold.
public static void LowpassPoseData (ref PoseData oldPose, ref PoseData newPose, float posThreshold, float rotThreshold)
{
posThreshold *= posThreshold;
float posDiff = (newPose.pos - oldPose.pos).sqrMagnitude;
float rotDiff = Quaternion.Angle (newPose.rot, oldPose.rot);
if (posDiff < posThreshold)
{
newPose.pos = oldPose.pos;
}
if (rotDiff < rotThreshold)
{
newPose.rot = oldPose.rot;
}
}
///
/// Performs a lowpass check on the position and rotation of each marker in newDict, comparing them to those in oldDict.
///
/// Old dictionary.
/// New dictionary.
/// Positon threshold.
/// Rotation threshold.
public static void LowpassPoseDataDict (Dictionary oldDict, Dictionary newDict, float posThreshold, float rotThreshold)
{
posThreshold *= posThreshold;
List keys = new List (newDict.Keys);
foreach (int key in keys)
{
if (!oldDict.ContainsKey (key)) continue;
PoseData oldPose = oldDict[key];
PoseData newPose = newDict[key];
float posDiff = (newPose.pos - oldPose.pos).sqrMagnitude;
float rotDiff = Quaternion.Angle (newPose.rot, oldPose.rot);
if (posDiff < posThreshold)
{
newPose.pos = oldPose.pos;
}
if (rotDiff < rotThreshold)
{
newPose.rot = oldPose.rot;
}
newDict[key] = newPose;
}
}
///
/// Extract translation from transform matrix.
///
/// Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.
///
/// Translation offset.
///
public static Vector3 ExtractTranslationFromMatrix (ref Matrix4x4 matrix)
{
Vector3 translate;
translate.x = matrix.m03;
translate.y = matrix.m13;
translate.z = matrix.m23;
return translate;
}
///
/// Extract rotation quaternion from transform matrix.
///
/// Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.
///
/// Quaternion representation of rotation transform.
///
public static Quaternion ExtractRotationFromMatrix (ref Matrix4x4 matrix)
{
Vector3 forward;
forward.x = matrix.m02;
forward.y = matrix.m12;
forward.z = matrix.m22;
Vector3 upwards;
upwards.x = matrix.m01;
upwards.y = matrix.m11;
upwards.z = matrix.m21;
return Quaternion.LookRotation (forward, upwards);
}
///
/// Extract scale from transform matrix.
///
/// Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.
///
/// Scale vector.
///
public static Vector3 ExtractScaleFromMatrix (ref Matrix4x4 matrix)
{
Vector3 scale;
scale.x = new Vector4 (matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
scale.y = new Vector4 (matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
scale.z = new Vector4 (matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
return scale;
}
///
/// Extract position, rotation and scale from TRS matrix.
///
/// Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.
/// Output position.
/// Output rotation.
/// Output scale.
public static void DecomposeMatrix (ref Matrix4x4 matrix, out Vector3 localPosition, out Quaternion localRotation, out Vector3 localScale)
{
localPosition = ExtractTranslationFromMatrix (ref matrix);
localRotation = ExtractRotationFromMatrix (ref matrix);
localScale = ExtractScaleFromMatrix (ref matrix);
}
///
/// Set transform component from TRS matrix.
///
/// Transform component.
/// Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.
public static void SetTransformFromMatrix (Transform transform, ref Matrix4x4 matrix)
{
transform.localPosition = ExtractTranslationFromMatrix (ref matrix);
transform.localRotation = ExtractRotationFromMatrix (ref matrix);
transform.localScale = ExtractScaleFromMatrix (ref matrix);
}
///
/// Calculate projection matrix from camera matrix values.
///
/// Focal length x.
/// Focal length y.
/// Image center point x.(principal point x)
/// Image center point y.(principal point y)
/// Image width.
/// Image height.
/// The near clipping plane distance.
/// The far clipping plane distance.
///
/// Projection matrix.
///
public static Matrix4x4 CalculateProjectionMatrixFromCameraMatrixValues (float fx, float fy, float cx, float cy, float width, float height, float near, float far)
{
Matrix4x4 projectionMatrix = new Matrix4x4 ();
projectionMatrix.m00 = 2.0f * fx / width;
projectionMatrix.m02 = 1.0f - 2.0f * cx / width;
projectionMatrix.m11 = 2.0f * fy / height;
projectionMatrix.m12 = -1.0f + 2.0f * cy / height;
projectionMatrix.m22 = -(far + near) / (far - near);
projectionMatrix.m23 = -2.0f * far * near / (far - near);
projectionMatrix.m32 = -1.0f;
return projectionMatrix;
}
///
/// Calculate camera matrix values from projection matrix.
///
/// Projection matrix.
/// Image width.
/// Image height.
/// Vertical field of view.
///
/// Camera matrix values. (fx = matrx.m00, fy = matrx.m11, cx = matrx.m02, cy = matrx.m12)
///
public static Matrix4x4 CameraMatrixValuesFromCalculateProjectionMatrix (Matrix4x4 projectionMatrix, float width, float height, float fovV)
{
float fovH = 2.0f * Mathf.Atan (width / height * Mathf.Tan (fovV * Mathf.Deg2Rad / 2.0f)) * Mathf.Rad2Deg;
Matrix4x4 cameraMatrix = new Matrix4x4 ();
cameraMatrix.m00 = CalculateDistance (width, fovH);
cameraMatrix.m02 = -((projectionMatrix.m02 * width - width) / 2);
cameraMatrix.m11 = CalculateDistance (height, fovV);
cameraMatrix.m12 = (projectionMatrix.m12 * height + height) / 2;
cameraMatrix.m22 = 1.0f;
return cameraMatrix;
}
///
/// Calculate frustum size.
/// https://docs.unity3d.com/Manual/FrustumSizeAtDistance.html
///
/// Distance.
/// Field of view. (horizontal or vertical direction)
///
/// Frustum height.
///
public static float CalculateFrustumSize (float distance, float fov)
{
return 2.0f * distance * Mathf.Tan (fov * 0.5f * Mathf.Deg2Rad);
}
///
/// Calculate distance.
/// https://docs.unity3d.com/Manual/FrustumSizeAtDistance.html
///
/// One side size of a frustum.
/// Field of view. (horizontal or vertical direction)
///
/// Distance.
///
public static float CalculateDistance (float frustumSize, float fov)
{
return frustumSize * 0.5f / Mathf.Tan (fov * 0.5f * Mathf.Deg2Rad);
}
///
/// Calculate FOV angle.
/// https://docs.unity3d.com/Manual/FrustumSizeAtDistance.html
///
/// One side size of a frustum.
/// Distance.
///
/// FOV angle.
///
public static float CalculateFOVAngle (float frustumSize, float distance)
{
return 2.0f * Mathf.Atan (frustumSize * 0.5f / distance) * Mathf.Rad2Deg;
}
}
}