FeatureMatrixStandardScaler.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Allocators.h>
  9. #include <AzCore/IO/GenericStreams.h>
  10. #include <AzCore/IO/SystemFile.h>
  11. #include <AzCore/std/string/conversions.h>
  12. #include <FeatureMatrixStandardScaler.h>
  13. namespace EMotionFX::MotionMatching
  14. {
  15. AZ_CLASS_ALLOCATOR_IMPL(StandardScaler, MotionMatchAllocator);
  16. bool StandardScaler::Fit(const FeatureMatrix& featureMatrix, [[maybe_unused]] const Settings& settings)
  17. {
  18. const FeatureMatrix::Index numRows = featureMatrix.rows();
  19. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  20. m_means.clear();
  21. m_means.resize(numColumns);
  22. m_standardDeviations.clear();
  23. m_standardDeviations.resize(numColumns);
  24. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  25. {
  26. // Calculate mean value
  27. float accumulated = 0.0f;
  28. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  29. {
  30. accumulated += featureMatrix(row, column);
  31. }
  32. const float mean = accumulated / numRows;
  33. m_means[column] = mean;
  34. // Calculate the standard deviation
  35. float rss = 0.0f; // Residual sum of squares
  36. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  37. {
  38. const float value = featureMatrix(row, column);
  39. rss += (value - mean) * (value - mean);
  40. }
  41. const float variance = rss / numRows;
  42. const float standardDeviation = sqrtf(variance);
  43. m_standardDeviations[column] = standardDeviation;
  44. }
  45. return true;
  46. }
  47. //-------------------------------------------------------------------------
  48. float StandardScaler::Transform(float value, FeatureMatrix::Index column) const
  49. {
  50. const float mean = m_means[column];
  51. float standardDeviation = m_standardDeviations[column];
  52. if (standardDeviation < s_epsilon)
  53. {
  54. standardDeviation = 1.0f;
  55. }
  56. // Subtract the mean and scale to unit variance
  57. return (value - mean) / standardDeviation;
  58. }
  59. AZ::Vector2 StandardScaler::Transform(const AZ::Vector2& value, FeatureMatrix::Index column) const
  60. {
  61. return AZ::Vector2(Transform(value.GetX(), column + 0), Transform(value.GetY(), column + 1));
  62. }
  63. AZ::Vector3 StandardScaler::Transform(const AZ::Vector3& value, FeatureMatrix::Index column) const
  64. {
  65. return AZ::Vector3(Transform(value.GetX(), column + 0), Transform(value.GetY(), column + 1), Transform(value.GetZ(), column + 2));
  66. }
  67. void StandardScaler::Transform(AZStd::span<float> data) const
  68. {
  69. const size_t numValues = data.size();
  70. AZ_Assert(numValues == m_means.size(), "Input data needs to have the same number of elements.");
  71. for (size_t i = 0; i < numValues; ++i)
  72. {
  73. data[i] = Transform(data[i], i);
  74. }
  75. }
  76. FeatureMatrix StandardScaler::Transform(const FeatureMatrix& featureMatrix) const
  77. {
  78. const FeatureMatrix::Index numRows = featureMatrix.rows();
  79. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  80. FeatureMatrix result;
  81. result.resize(numRows, numColumns);
  82. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  83. {
  84. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  85. {
  86. result(row, column) = Transform(featureMatrix(row, column), column);
  87. }
  88. }
  89. return result;
  90. }
  91. //-------------------------------------------------------------------------
  92. FeatureMatrix StandardScaler::InverseTransform(const FeatureMatrix& featureMatrix) const
  93. {
  94. const FeatureMatrix::Index numRows = featureMatrix.rows();
  95. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  96. FeatureMatrix result;
  97. result.resize(numRows, numColumns);
  98. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  99. {
  100. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  101. {
  102. result(row, column) = InverseTransform(featureMatrix(row, column), column);
  103. }
  104. }
  105. return result;
  106. }
  107. AZ::Vector2 StandardScaler::InverseTransform(const AZ::Vector2& value, FeatureMatrix::Index column) const
  108. {
  109. return AZ::Vector2(InverseTransform(value.GetX(), column + 0), InverseTransform(value.GetY(), column + 1));
  110. }
  111. AZ::Vector3 StandardScaler::InverseTransform(const AZ::Vector3& value, FeatureMatrix::Index column) const
  112. {
  113. return AZ::Vector3(
  114. InverseTransform(value.GetX(), column + 0),
  115. InverseTransform(value.GetY(), column + 1),
  116. InverseTransform(value.GetZ(), column + 2));
  117. }
  118. float StandardScaler::InverseTransform(float value, FeatureMatrix::Index column) const
  119. {
  120. float standardDeviation = m_standardDeviations[column];
  121. if (standardDeviation < s_epsilon)
  122. {
  123. standardDeviation = 1.0f;
  124. }
  125. return value * standardDeviation + m_means[column];
  126. }
  127. void StandardScaler::SaveAsCsv(const char* filename, const AZStd::vector<AZStd::string>& columnNames)
  128. {
  129. AZStd::string data;
  130. // Save column names in the first row
  131. if (!columnNames.empty())
  132. {
  133. for (size_t i = 0; i < columnNames.size(); ++i)
  134. {
  135. if (i != 0)
  136. {
  137. data += ",";
  138. }
  139. data += columnNames[i].c_str();
  140. }
  141. data += "\n";
  142. }
  143. for (size_t i = 0; i < m_means.size(); ++i)
  144. {
  145. if (i != 0)
  146. {
  147. data += ",";
  148. }
  149. data += AZStd::to_string(m_means[i]);
  150. }
  151. data += "\n";
  152. for (size_t i = 0; i < m_standardDeviations.size(); ++i)
  153. {
  154. if (i != 0)
  155. {
  156. data += ",";
  157. }
  158. data += AZStd::to_string(m_standardDeviations[i]);
  159. }
  160. data += "\n";
  161. AZ::IO::SystemFile file;
  162. if (file.Open(
  163. filename,
  164. AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  165. {
  166. file.Write(data.data(), data.size());
  167. file.Close();
  168. }
  169. }
  170. } // namespace EMotionFX::MotionMatching