using System;
using UnityEngine;
using Unity.MLAgents.Sensors;
namespace Unity.MLAgents
{
internal static class Utilities
{
///
/// Puts a Texture2D into a ObservationWriter.
///
///
/// The texture to be put into the tensor.
///
///
/// Writer to fill with Texture data.
///
///
/// If set to true the textures will be converted to grayscale before
/// being stored in the tensor.
///
/// The number of floats written
internal static int TextureToTensorProxy(
Texture2D texture,
ObservationWriter obsWriter,
bool grayScale)
{
var width = texture.width;
var height = texture.height;
var texturePixels = texture.GetPixels32();
// During training, we convert from Texture to PNG before sending to the trainer, which has the
// effect of flipping the image. We need another flip here at inference time to match this.
for (var h = height - 1; h >= 0; h--)
{
for (var w = 0; w < width; w++)
{
var currentPixel = texturePixels[(height - h - 1) * width + w];
if (grayScale)
{
obsWriter[h, w, 0] =
(currentPixel.r + currentPixel.g + currentPixel.b) / 3f / 255.0f;
}
else
{
// For Color32, the r, g and b values are between 0 and 255.
obsWriter[h, w, 0] = currentPixel.r / 255.0f;
obsWriter[h, w, 1] = currentPixel.g / 255.0f;
obsWriter[h, w, 2] = currentPixel.b / 255.0f;
}
}
}
return height * width * (grayScale ? 1 : 3);
}
///
/// Calculates the cumulative sum of an integer array. The result array will be one element
/// larger than the input array since it has a padded 0 at the beginning.
/// If the input is [a, b, c], the result will be [0, a, a+b, a+b+c]
///
///
/// Input array whose elements will be cumulatively added
///
/// The cumulative sum of the input array.
internal static int[] CumSum(int[] input)
{
var runningSum = 0;
var result = new int[input.Length + 1];
for (var actionIndex = 0; actionIndex < input.Length; actionIndex++)
{
runningSum += input[actionIndex];
result[actionIndex + 1] = runningSum;
}
return result;
}
#if DEBUG
internal static void DebugCheckNanAndInfinity(float value, string valueCategory, string caller)
{
if (float.IsNaN(value))
{
throw new ArgumentException($"NaN {valueCategory} passed to {caller}.");
}
if (float.IsInfinity(value))
{
throw new ArgumentException($"Inifinity {valueCategory} passed to {caller}.");
}
}
#endif
}
}