//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
#pragma once
#include "NuiKinectFusionApi.h"
#include
#include
///
/// Set Identity in a Matrix4
///
/// The matrix to set to identity
void SetIdentityMatrix(Matrix4 &mat);
///
/// Extract translation values from the 4x4 Matrix4 transformation in M41,M42,M43
///
/// The transform matrix.
/// Array of 3 floating point values for translation.
void ExtractVector3Translation(const Matrix4 &transform, _Out_cap_c_(3) float *translation);
///
/// Extract translation Vector3 from the 4x4 Matrix transformation in M41,M42,M43
///
/// The transform matrix.
/// Returns a Vector3 containing the translation.
Vector3 ExtractVector3Translation(const Matrix4 &transform);
///
/// Extract 3x3 rotation from the 4x4 Matrix and return in new Matrix4
///
/// The transform matrix.
/// Returns a Matrix4 containing the rotation.
Matrix4 Extract3x3Rotation(const Matrix4 &transform);
///
/// Extract 3x3 rotation matrix from the Matrix4 4x4 transformation:
/// Then convert to Euler angles.
///
/// The transform matrix.
/// Array of 3 floating point values for euler angles.
void ExtractRot2Euler(const Matrix4 &transform, _Out_cap_c_(3) float *rotation);
///
/// Test whether the camera moved too far between sequential frames by looking at starting and end transformation matrix.
/// We assume that if the camera moves or rotates beyond a reasonable threshold, that we have lost track.
/// Note that on lower end machines, if the processing frame rate decreases below 30Hz, this limit will potentially have
/// to be increased as frames will be dropped and hence there will be a greater motion between successive frames.
///
/// The transform matrix from the previous frame.
/// The transform matrix from the current frame.
/// The maximum translation in meters we expect per x,y,z component between frames under normal motion.
/// The maximum rotation in degrees we expect about the x,y,z axes between frames under normal motion.
/// true if camera transformation is greater than the threshold, otherwise false
bool CameraTransformFailed(const Matrix4 &T_initial, const Matrix4 &T_final, float maxTrans, float maxRotDegrees);
///
/// Invert the 3x3 Rotation Matrix Component of a 4x4 matrix
///
/// The rotation matrix to invert.
void InvertRotation(Matrix4 &rot);
///
/// Negate the 3x3 Rotation Matrix Component of a 4x4 matrix
///
/// The rotation matrix to negate.
void NegateRotation(Matrix4 &rot);
///
/// Rotate a vector with the 3x3 Rotation Matrix Component of a 4x4 matrix
///
/// The Vector3 to rotate.
/// Rotation matrix.
Vector3 RotateVector(const Vector3 &vec, const Matrix4 &rot);
///
/// Invert Matrix4 Pose either from WorldToCameraTransform (view) matrix to CameraToWorldTransform pose matrix (world/SE3) or vice versa
///
/// The camera pose transform matrix.
/// Returns a Matrix4 containing the inverted camera pose.
Matrix4 InvertMatrix4Pose(const Matrix4 &transform);
///
/// Write Binary .STL mesh file
/// see http://en.wikipedia.org/wiki/STL_(file_format) for STL format
///
/// The Kinect Fusion mesh object.
/// The full path and filename of the file to save.
/// Flag to determine whether the Y and Z values are flipped on save.
/// indicates success or failure
HRESULT WriteBinarySTLMeshFile(INuiFusionColorMesh *mesh, LPOLESTR lpOleFileName, bool flipYZ = true);
///
/// Write ASCII Wavefront .OBJ mesh file
/// See http://en.wikipedia.org/wiki/Wavefront_.obj_file for .OBJ format
///
/// The Kinect Fusion mesh object.
/// The full path and filename of the file to save.
/// Flag to determine whether the Y and Z values are flipped on save.
/// indicates success or failure
HRESULT WriteAsciiObjMeshFile(INuiFusionColorMesh *mesh, LPOLESTR lpOleFileName, bool flipYZ = true);
///
/// Write ASCII .PLY file
/// See http://paulbourke.net/dataformats/ply/ for .PLY format
///
/// The Kinect Fusion mesh object.
/// The full path and filename of the file to save.
/// Flag to determine whether the Y and Z values are flipped on save.
/// Set this true to write out the surface color to the file when it has been captured.
/// indicates success or failure
HRESULT WriteAsciiPlyMeshFile(INuiFusionColorMesh *mesh, LPOLESTR lpOleFileName, bool flipYZ = true, bool outputColor = false);
///
/// Write ASCII Wavefront .OBJ file with bitmap texture and material file
/// See http://en.wikipedia.org/wiki/Wavefront_.obj_file for .OBJ format
///
/// The Kinect Fusion mesh object.
/// The full path and filename of the file to save.
/// Flag to determine whether the Y and Z values are flipped on save.
/// The Kinect Fusion color texture image.
/// Three Vector3 texture coordinates per mesh triangle, normalized by the image size.
/// S_OK on success, otherwise failure code
HRESULT WriteTexturedeAsciiObjMeshFile(INuiFusionColorMesh *mesh, LPOLESTR lpOleFileName, bool flipYZ, NUI_FUSION_IMAGE_FRAME *pTexture, const std::vector &texcoords);
///
/// Returns whether this is running as a 32 or 64bit application.
///
/// TRUE indicates a 64bit app.
BOOL Is64BitApp();
///
/// Write 32bit BMP image file
///
/// The full path and filename of the file to save.
/// A pointer to the image bytes to save.
/// The width of the image to save.
/// The width of the image to save.
/// indicates success or failure
HRESULT SaveBMPFile(LPCWSTR pszFile, const byte *pImageBytes, unsigned int width, unsigned int height);
///
/// Copy an image with identical sizes and parameters.
///
/// A pointer to the source image.
/// A pointer to the destination image.
/// indicates success or failure
HRESULT CopyImageFrame(const NUI_FUSION_IMAGE_FRAME *pSrc, const NUI_FUSION_IMAGE_FRAME *pDest);
///
/// Horizontally mirror a 32bit (color/float) image in-place.
///
/// A pointer to the image to mirror.
/// S_OK on success, otherwise failure code
HRESULT HorizontalMirror32bitImageInPlace(const NUI_FUSION_IMAGE_FRAME *pImage);
///
/// Horizontally mirror a 32bit (color/float) image.
///
/// A pointer to the image to mirror.
/// A pointer to the destination mirrored image.
/// S_OK on success, otherwise failure code
HRESULT HorizontalMirror32bitImage(const NUI_FUSION_IMAGE_FRAME *pSrcImage, const NUI_FUSION_IMAGE_FRAME *pDestImage);
///
/// Color the residual/delta image from the AlignDepthFloatToReconstruction call
///
/// A pointer to the source pFloatDeltaFromReference image.
/// A pointer to the destination ShadedDeltaFromReference image.
/// S_OK on success, otherwise failure code
HRESULT ColorResiduals(const NUI_FUSION_IMAGE_FRAME *pFloatDeltaFromReference, const NUI_FUSION_IMAGE_FRAME *pShadedDeltaFromReference);
///
/// Statistics calculated for a FloatDeltaFromReference Image after the
/// AlignDepthFloatToReconstruction and CalculateResidualStatistics calls.
///
struct DeltaFromReferenceImageStatistics
{
unsigned int totalPixels;
unsigned int zeroPixels;
unsigned int validPixels;
unsigned int invalidDepthOutsideVolumePixels;
float totalValidPixelsDistance;
};
///
/// Calculate statistics on the residual/delta image from the AlignDepthFloatToReconstruction call.
///
/// A pointer to the source FloatDeltaFromReference image.
/// A pointer to a DeltaFromReferenceImageStatistics struct to fill with the statistics.
/// S_OK on success, otherwise failure code
HRESULT CalculateResidualStatistics(const NUI_FUSION_IMAGE_FRAME *pFloatDeltaFromReference, DeltaFromReferenceImageStatistics *stats);
///
/// Down sample color, depth float or point cloud frame with nearest neighbor
///
/// The source color, depth float or pointcloud image.
/// The destination down sampled color, depth float or pointcloud image.
/// The down sample factor (1=just copy, 2=x/2,y/2, 4=x/4,y/4).
/// S_OK on success, otherwise failure code
HRESULT DownsampleFrameNearestNeighbor(NUI_FUSION_IMAGE_FRAME *src, NUI_FUSION_IMAGE_FRAME *dest, unsigned int factor);
///
/// Up sample color or depth float (32 bits/pixel) frame with nearest neighbor - replicates pixels
///
/// The source color image.
/// The destination up-sampled color image.
/// The up sample factor (1=just copy, 2=x*2,y*2, 4=x*4,y*4).
/// S_OK on success, otherwise failure code
HRESULT UpsampleFrameNearestNeighbor(NUI_FUSION_IMAGE_FRAME *src, NUI_FUSION_IMAGE_FRAME *dest, unsigned int factor);
///
/// Down sample color frame with nearest neighbor to the depth frame resolution
///
/// The source color image.
/// The destination down sampled image.
/// S_OK on success, otherwise failure code
HRESULT DownsampleColorFrameToDepthResolution(NUI_FUSION_IMAGE_FRAME *src, NUI_FUSION_IMAGE_FRAME *dest);
///
/// Convert int to string
///
/// The int value to convert.
/// Returns a string containing the int value.
inline std::string to_string(int theValue)
{
char buffer[65];
errno_t err = _itoa_s(theValue, buffer, ARRAYSIZE(buffer), 10);
if (0 != err)
{
return std::string("");
}
return std::string(buffer);
}
///
/// Convert float to string
///
/// The float value to convert.
/// Returns a string containing the float value.
inline std::string to_string(float theValue)
{
char buffer[_CVTBUFSIZE];
errno_t err = _gcvt_s(buffer, _CVTBUFSIZE, theValue, 6);
if (0 != err)
{
return std::string("");
}
return std::string(buffer);
}
///
/// Clamp a value if outside two given thresholds
///
/// The value to clamp.
/// The minimum inclusive threshold.
/// The maximum inclusive threshold.
/// Returns the clamped value.
template
inline T clamp(const T& x, const T& a, const T& b)
{
if (x < a)
return a;
else if (x > b)
return b;
else
return x;
}
///
/// Load an 24bit RGB color from a packed int pixel image and return as float values
///
/// The int image array.
/// The x coordinate of the pixel to return.
/// The y coordinate of the pixel to return.
/// The width of the image in pixels.
/// Returns a Vector3 containing the rgb color components.
inline Vector3 load_color(const unsigned int *colorImage, int x, int y, int imageWidth)
{
Vector3 rgb;
unsigned int packedValue = colorImage[(y * imageWidth) + x];
rgb.x = static_cast(packedValue & 255); // r
rgb.y = static_cast((packedValue >> 8) & 255); // g
rgb.z = static_cast((packedValue >> 16) & 255); // b
return rgb;
}
///
/// Linearly interpolate (Lerp) between two values.
///
/// The first value.
/// The second value.
/// The amount to interpolate between the values.
/// Returns the interpolated value.
template
inline auto lerp(const T& a, const T& b, Tf f) -> decltype(a + f * (b - a))
{
return a + f * (b - a);
}
///
/// Linearly interpolate (Lerp) between two Vector3-based RGB color pixels.
///
/// The first color pixel.
/// The second color pixel.
/// The amount to interpolate between the values.
/// Returns the interpolated value.
inline Vector3 lerp_color(const Vector3 &a, const Vector3 &b, float f)
{
Vector3 rgb;
rgb.x = lerp(a.x, b.x, f); // r
rgb.y = lerp(a.y, b.y, f); // g
rgb.z = lerp(a.z, b.z, f); // b
return rgb;
}
///
/// Bilinear sample an RGB int image
///
/// The int image array.
/// The x coordinate of the pixel to return.
/// The y coordinate of the pixel to return.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// Returns a packed int containing the rgb color components.
inline unsigned int bilinear_sample(const unsigned int *colorImage, float x, float y, int imageWidth, int imageHeight)
{
const float half = 0.5f;
const unsigned int xi = static_cast(x - half);
const unsigned int yi = static_cast(y - half);
const float xf = x - half - static_cast(xi);
const float yf = y - half - static_cast(yi);
const unsigned int posax = clamp(xi, 0, imageWidth - 1);
const unsigned int posay = clamp(yi, 0, imageHeight - 1);
const unsigned int posbx = clamp(xi+1, 0, imageWidth - 1);
const unsigned int posby = clamp(yi+1, 0, imageHeight - 1);
// Load the corners
Vector3 d00 = load_color(colorImage, posax, posay, imageWidth);
Vector3 d01 = load_color(colorImage, posax, posby, imageWidth);
Vector3 d10 = load_color(colorImage, posbx, posay, imageWidth);
Vector3 d11 = load_color(colorImage, posbx, posby, imageWidth);
// Interpolate over x
auto dx0 = lerp_color(d00, d10, xf);
auto dx1 = lerp_color(d01, d11, xf);
// Interpolate over y
auto dxy = lerp_color(dx0, dx1, yf);
return (255 << 24 | static_cast(dxy.z) << 16 | static_cast(dxy.y) << 8 | static_cast(dxy.x) ); // always full alpha
}
///
/// Calculate the squared difference between two Vector3 vertices
///
/// The first vertex.
/// The second vertex.
/// Returns the squared difference.
inline float squared_difference(const Vector3 &v1, const Vector3 &v2)
{
float dx = v1.x-v2.x;
float dy = v1.y-v2.y;
float dz = v1.z-v2.z;
return (dx*dx) + (dy*dy) + (dz*dz);
}
///
/// Calculate the distance between two Vector3 vertices
///
/// The first vertex.
/// The second vertex.
/// Returns the distance.
inline float distance(const Vector3 &v1, const Vector3 &v2)
{
return sqrtf(squared_difference(v1, v2));
}
///
/// Calculate the normalized dot product between two vectors.
/// Must be normalized input Vector3s.
/// Output: 1 if parallel, same dir, 0 if 90 degrees, -1 if parallel, looking opposite direction
///
/// The first vector.
/// The second vector.
/// Returns the dot product.
inline float dot_normalized(const Vector3 &v1, const Vector3 &v2)
{
return (v1.x*v2.x) + (v1.y*v2.y) + (v1.z*v2.z);
}
///
/// Transform a vertex in the world coordinate system by the worldToCamera pose,
/// into the camera coordinate system.
///
/// The vertex to transform.
/// The worldToCamera pose.
/// Returns the transformed vertex.
inline Vector3 transform(const Vector3 &v1, const Matrix4 &worldToCamera)
{
// Transform the point from the global frame into the local camera frame.
Vector3 R;
R.x = worldToCamera.M41 + (worldToCamera.M11 * v1.x) + (worldToCamera.M21 * v1.y) + (worldToCamera.M31 * v1.z);
R.y = worldToCamera.M42 + (worldToCamera.M12 * v1.x) + (worldToCamera.M22 * v1.y) + (worldToCamera.M32 * v1.z);
R.z = worldToCamera.M43 + (worldToCamera.M13 * v1.x) + (worldToCamera.M23 * v1.y) + (worldToCamera.M33 * v1.z);
return R;
}
///
/// Project a 3D vertex in the world coordinate system into a 2D camera image,
/// given its known intrinsic parameters and camera pose.
///
/// The vertex to transform.
/// The focal length in the x axis, in pixels.
/// The focal length in the y axis, in pixels.
/// The principal point of the image in the x axis, in pixels.
/// The principal point of the image in the y axis, in pixels.
/// The worldToCamera pose.
/// Returns the 3D vertex transformed into a pixel in the 2D camera image.
inline Vector3 fast_project(const Vector3 &v1, float flx, float fly, float ppx, float ppy, const Matrix4 &worldToCamera)
{
// Transform from world to camera coordinate system
Vector3 R = transform(v1, worldToCamera);
Vector3 uv;
uv.x = R.x / R.z;
uv.y = R.y / R.z;
// Project from camera plane in world to image
uv.x = ppx + flx * uv.x;
uv.y = ppy + fly * uv.y;
uv.z = R.z;
return uv;
}