//------------------------------------------------------------------------------ // // 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; }