FeatureMatrixMinMaxScaler.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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 <AzCore/std/string/conversions.h>
  9. #include <AzCore/IO/GenericStreams.h>
  10. #include <AzCore/IO/SystemFile.h>
  11. #include <Allocators.h>
  12. #include <AzCore/std/limits.h>
  13. #include <FeatureMatrixMinMaxScaler.h>
  14. namespace EMotionFX::MotionMatching
  15. {
  16. AZ_CLASS_ALLOCATOR_IMPL(MinMaxScaler, MotionMatchAllocator);
  17. bool MinMaxScaler::Fit(const FeatureMatrix& featureMatrix, const Settings& settings)
  18. {
  19. const FeatureMatrix::Index numRows = featureMatrix.rows();
  20. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  21. m_clip = settings.m_clip;
  22. m_featureMin = settings.m_featureMin;
  23. m_featureMax = settings.m_featureMax;
  24. m_featureRange = settings.m_featureMax - settings.m_featureMin;
  25. AZ_Assert(m_featureRange > s_epsilon, "Feature range too small. This will lead to divisions by zero.");
  26. m_dataMin.clear();
  27. m_dataMin.resize(numColumns, AZStd::numeric_limits<float>::max());
  28. m_dataMax.clear();
  29. m_dataMax.resize(numColumns, AZStd::numeric_limits<float>::lowest());
  30. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  31. {
  32. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  33. {
  34. m_dataMin[column] = AZStd::min(m_dataMin[column], featureMatrix(row, column));
  35. m_dataMax[column] = AZStd::max(m_dataMax[column], featureMatrix(row, column));
  36. }
  37. }
  38. m_dataRange.clear();
  39. m_dataRange.resize_no_construct(numColumns);
  40. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  41. {
  42. const float columnMin = m_dataMin[column];
  43. const float columnMax = m_dataMax[column];
  44. const float range = columnMax - columnMin;
  45. m_dataRange[column] = range;
  46. }
  47. return true;
  48. }
  49. //-------------------------------------------------------------------------
  50. float MinMaxScaler::Transform(float value, FeatureMatrix::Index column) const
  51. {
  52. const float& min = m_dataMin[column];
  53. const float& range = m_dataRange[column];
  54. float result = value;
  55. if (range > s_epsilon)
  56. {
  57. const float normalizedValue = (value - min) / (range);
  58. const float scaled = normalizedValue * m_featureRange + m_featureMin;
  59. result = scaled;
  60. }
  61. if (m_clip)
  62. {
  63. result = AZ::GetClamp(result, m_featureMin, m_featureMax);
  64. }
  65. return result;
  66. }
  67. AZ::Vector2 MinMaxScaler::Transform(const AZ::Vector2& value, FeatureMatrix::Index column) const
  68. {
  69. return AZ::Vector2(
  70. Transform(value.GetX(), column + 0),
  71. Transform(value.GetY(), column + 1));
  72. }
  73. AZ::Vector3 MinMaxScaler::Transform(const AZ::Vector3& value, FeatureMatrix::Index column) const
  74. {
  75. return AZ::Vector3(
  76. Transform(value.GetX(), column + 0),
  77. Transform(value.GetY(), column + 1),
  78. Transform(value.GetZ(), column + 2));
  79. }
  80. void MinMaxScaler::Transform(AZStd::span<float> data) const
  81. {
  82. const size_t numValues = data.size();
  83. AZ_Assert(numValues == m_dataMin.size(), "Input data needs to have the same number of elements.");
  84. for (size_t i = 0; i < numValues; ++i)
  85. {
  86. data[i] = Transform(data[i], i);
  87. }
  88. }
  89. FeatureMatrix MinMaxScaler::Transform(const FeatureMatrix& featureMatrix) const
  90. {
  91. const FeatureMatrix::Index numRows = featureMatrix.rows();
  92. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  93. FeatureMatrix result;
  94. result.resize(numRows, numColumns);
  95. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  96. {
  97. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  98. {
  99. result(row, column) = Transform(featureMatrix(row, column), column);
  100. }
  101. }
  102. return result;
  103. }
  104. //-------------------------------------------------------------------------
  105. FeatureMatrix MinMaxScaler::InverseTransform(const FeatureMatrix& featureMatrix) const
  106. {
  107. const FeatureMatrix::Index numRows = featureMatrix.rows();
  108. const FeatureMatrix::Index numColumns = featureMatrix.cols();
  109. FeatureMatrix result;
  110. result.resize(numRows, numColumns);
  111. for (FeatureMatrix::Index row = 0; row < numRows; ++row)
  112. {
  113. for (FeatureMatrix::Index column = 0; column < numColumns; ++column)
  114. {
  115. result(row, column) = InverseTransform(featureMatrix(row, column), column);
  116. }
  117. }
  118. return result;
  119. }
  120. AZ::Vector2 MinMaxScaler::InverseTransform(const AZ::Vector2& value, FeatureMatrix::Index column) const
  121. {
  122. return AZ::Vector2(
  123. InverseTransform(value.GetX(), column + 0),
  124. InverseTransform(value.GetY(), column + 1));
  125. }
  126. AZ::Vector3 MinMaxScaler::InverseTransform(const AZ::Vector3& value, FeatureMatrix::Index column) const
  127. {
  128. return AZ::Vector3(
  129. InverseTransform(value.GetX(), column + 0),
  130. InverseTransform(value.GetY(), column + 1),
  131. InverseTransform(value.GetZ(), column + 2));
  132. }
  133. float MinMaxScaler::InverseTransform(float value, FeatureMatrix::Index column) const
  134. {
  135. const float normalizedValue = (value - m_featureMin) / m_featureRange;
  136. return normalizedValue * m_dataRange[column] + m_dataMin[column];
  137. }
  138. void MinMaxScaler::SaveMinMaxAsCsv(const char* filename, const AZStd::vector<AZStd::string>& columnNames)
  139. {
  140. AZStd::string data;
  141. // Save column names in the first row
  142. if (!columnNames.empty())
  143. {
  144. for (size_t i = 0; i < columnNames.size(); ++i)
  145. {
  146. if (i != 0)
  147. {
  148. data += ",";
  149. }
  150. data += columnNames[i].c_str();
  151. }
  152. data += "\n";
  153. }
  154. for (size_t i = 0; i < m_dataMin.size(); ++i)
  155. {
  156. if (i != 0)
  157. {
  158. data += ",";
  159. }
  160. data += AZStd::to_string(m_dataMin[i]);
  161. }
  162. data += "\n";
  163. for (size_t i = 0; i < m_dataMax.size(); ++i)
  164. {
  165. if (i != 0)
  166. {
  167. data += ",";
  168. }
  169. data += AZStd::to_string(m_dataMax[i]);
  170. }
  171. data += "\n";
  172. AZ::IO::SystemFile file;
  173. if (file.Open(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  174. {
  175. file.Write(data.data(), data.size());
  176. file.Close();
  177. }
  178. }
  179. } // namespace EMotionFX::MotionMatching