Получить массив байтов с наибольшим значением - PullRequest
0 голосов
/ 07 октября 2018

У меня есть массив, содержащий миллионы байтов.Эти байты являются значениями типа int (Int16, Int24 или Int32).Теперь я хочу получить x-байты со значением max int из количества байтов.

Итак, чтобы объяснить это лучше, давайте представим массив с 10 записями:

byte[] arr = {255, 10, 55, 60, 128, 90, 88, 66, 199, 56};

Я буду знать, будем ли мы использовать In16, Int24 или Int32, поэтому для этого примера давайте представим, что мы используем Int16.Это означает, что мы используем 2 байта для представления Int16.Таким образом, Ints состоят из:

{255, 10},
{55, 60},
{128, 90},
{88, 66},
{199, 56}

Проблема 1: Поскольку это необходимо для обработки звука, 1046 ниже, чем -2096.Таким образом, существует необходимость сравнения независимо от негатива

Проблема 2: Поскольку это должно быть очень производительным, преобразование байтов в Ints для сравнения кажется неэффективным, и должен быть другой способ.

Этоэто начальная точка:

    /// <summary>
    /// Gets the maximum value of a number of bytes representing Int-Values
/// </summary>
/// <returns>The channels.</returns>
/// <param name="leftChannel">Left channel.</param>
/// <param name="rightChannel">Right channel.</param>
/// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
/// <param name="countBytesToCombine">The number of bytes to look for the highest value</param>
private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countBytesToCombine)
{

}

/// <summary>
/// Gets the highest byte[] value 
/// </summary>
/// <returns>The highest value. The size of the byte array is equal the bytesPerInt</returns>
/// <param name="bytes">A subarray of the given byte array of the upper method. The size of this array is equals countBytesToCombine</param>
/// <param name="bytesPerInt">The count of bytes representing an Int</param>
private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
{

}

Edit2

Это рабочее решение, но его выполнение занимает около 2 секунд с 14 миллионами байтов для каждого канала, что слишком далеко.

    /// <summary>
    /// Gets the maximum value of a number of bytes representing Int-Values
    /// </summary>
    /// <returns>The channels.</returns>
    /// <param name="leftChannel">Left channel.</param>
    /// <param name="rightChannel">Right channel.</param>
    /// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
    /// <param name="countValuesToCombine">The number of bytes to look for the highest value</param>
    private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
    {
        var cLeft = new List<byte>();
        var cRight = new List<byte>();

        for (int i = 0; i < leftChannel.Length; i += countValuesToCombine * bytesPerInt)
        {
            var arrLeft = SubArray(leftChannel, i, countValuesToCombine * bytesPerInt);
            var arrRight = SubArray(rightChannel, i, countValuesToCombine * bytesPerInt);

            cLeft.AddRange(GetHighestValue(arrLeft, bytesPerInt));
            cRight.AddRange(GetHighestValue(arrRight, bytesPerInt));
        }

        return (cLeft.ToArray(), cRight.ToArray());
    }

    /// <summary>
    /// Gets the highest byte[] value 
    /// </summary>
    /// <returns>The highest value.</returns>
    /// <param name="bytes">Bytes.</param>
    /// <param name="bytesPerInt">The count of bytes representing an Int</param>
    private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
    {
        byte[] bytesOfHighestValue = new byte[bytesPerInt];

        for (int i = 0; i < bytes.Length; i += bytesPerInt)
        {
            var arr = SubArray(bytes, i, bytesPerInt);

            if (IsValueHigher(arr, bytesOfHighestValue, bytesPerInt))
            {
                bytesOfHighestValue = arr;
            }
        }

        return bytesOfHighestValue;
    }

    private bool IsValueHigher(byte[] one, byte[] two, int bytesPerInt)
    {
        var o = ConvertToInt(one, bytesPerInt);
        var t = ConvertToInt(two, bytesPerInt);

        return Math.Abs(o) > Math.Abs(t);
    }

    private int ConvertToInt(byte[] bytes, int bytesPerInt)
    {
        switch (bytesPerInt)
        {
            case 2:
                return BitConverter.ToInt16(bytes, 0);
            case 3:
                return Int24.ToInt32(bytes, 0);
            case 4:
                return BitConverter.ToInt32(bytes, 0);
        }

        return 0;
    }

Это чрезвычайно сложно объяснить, поэтому, пожалуйста, спросите, есть ли вопросы, прежде чем голосовать.

Ответы [ 3 ]

0 голосов
/ 07 октября 2018

Я сделал метод, который возвращает максимальный индекс.Сначала сравниваются старшие байты, а при равенстве - младшие.С большими числами это работает еще быстрее.

static int getMaxIndex(byte[] data, int byteLenght)
        {
            int MaxIndex = 0;
            int signMax = data[byteLenght - 1] >> 7;// get sign
            for (int i = byteLenght; i < data.Length; i += byteLenght)
            {
                int step = byteLenght - 1;
                int compResult = 0;

                while (compResult == 0 && step > -1)
                {
                    if (step == byteLenght -1)
                    {
                        int signData = data[i + step] >> 7;

                        compResult = signData - signMax;
                        if (compResult == 0) compResult = data[MaxIndex + step] & 127 - data[i + step] & 127;
                    }
                    else compResult = data[MaxIndex + step] - data[i + step];
                    if (compResult < 0)
                    {
                        MaxIndex = i;
                        signMax = data[MaxIndex + step] >> 7;
                    }
                    step--;
                }
            }
            return MaxIndex;
        }
0 голосов
/ 12 октября 2018

Как уже упоминалось несколько раз, избегайте выражений "если" внутри вашего чтения;просто создайте отдельную функцию для чтения Int16, Int24 и Int32 и выберите, какую из них использовать заранее.

Лично я бы использовал System.IO.BinaryReader для этого;он уже содержит функции для чтения целых чисел из потоков, и в отличие от BitConverter, который технически зависит от порядкового номера системы, BinaryReader фактически гарантированно считывает значения с прямым порядком байтов;это в спецификациях MSDN.

Вот основная функция для использования BinaryReader, используя Int32 в качестве примера.В этой версии я позволил EndOfStreamException позаботиться о конце.Они говорят, что выброс / обработка исключений - довольно тяжелая операция, но в этом случае она заменяет много проверок между чтениями, поэтому это может быть оправдано.

Вы можете адаптировать это, заменивwhile (true) с фактической проверкой указателя потока.Это либо просто проверка ms.Position по длине входного байтового массива, либо отслеживание местоположения в вашей собственной переменной, которую вы увеличиваете на количество считанных байтов на каждом шаге.

public static Int32 GetHighestValueInt32(Byte[] bytes)
{
    Int32 maxval = 0;
    try
    {
        using (MemoryStream ms = new MemoryStream(bytes))
        using (BinaryReader reader = new BinaryReader(ms))
        {
            while (true)
            {
                // Clear highest bit so the value's always a positive Int32.
                Int32 val = (Int32)(reader.ReadUInt32() & 0x7FFFFFFF);
                if (val > maxval)
                    maxval = val;
            }
        }
    }
    catch (EndOfStreamException ex)
    {
        // Finished reading!
    }
    return maxval;
}

Для Int16,фактическое чтение строки val должно быть просто заменено на

Int16 val = (Int16)(reader.ReadUInt16() & 0x7FFF);

и maxval, а тип возвращаемого значения также должен быть изменен на Int16.

BinaryReader не можетхотя изначально считывай Int24 из потока.Но обходной путь для этого не слишком сложен.Вы можете просто использовать Int32 и сдвинуть его на 8 бит, а затем вручную адаптировать указатель потока, чтобы компенсировать два дополнительных байта чтения:

while (true)
{
    Int32 val = (Int32)((reader.ReadUInt32() >> 8) & 0x7FFFFF);
    ms.Position -= 2;
    if (val > maxval)
        maxval = val;
}
0 голосов
/ 07 октября 2018

Хорошо, вот простая реализация для 4-байтовых целых чисел:

private static int GetHighestValue(byte[] data)
{
  if (data.Length % 4 != 0)
     throw new ArgumentException();

  var maximum = 0, maximumAbs = 0;
  for (var i = 0; i < data.Length; i+=4)
  {
    var current = BitConverter.ToInt32 (data, i);
    var currentAbs = Math.Abs(current);

    if (currentAbs > maximumAbs)
    {
      maximum = current;
      maximumAbs = currentAbs;
    }
  }

  return maximum;
}

Выполнение этого на byte[] с 1 миллионом байтов занимает около 3 мс при компиляции с Debug.

Я не знаю, к каким скоростям вы стремитесь, но для 99% случаев это должно быть хорошо.


Редактировать: Поскольку вы обновили свой вопрос и включили здесь пример кодаэто обновление:

Вот некоторые области I, которые делают ваш код медленнее, чем нужно:

  • Нам не нужно создавать подмассивы в каждой итерацииCombineChannels.Мы можем переписать GetHighestValue так, чтобы он принимал array, offset и amount в качестве параметра.

  • Вместо одного метода CombineChannels мы должны разделить егов разные размеры байтов.Например, CombineChannelsInt32, CombineChannelsInt16 ... Таким образом, сами методы могут хранить максимум как int32 / int16 / ... без необходимости конвертировать их на каждой итерации.

Итак, вот методы, которые мы бы получили в итоге примерно так:

(byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
{
  switch(bytesPerInt)
  {
    case 2:
      return CombineChannelsInt16(leftChannel, rightChannel, countValuesToCombine);
    case 3:
      return CombineChannelsInt24(leftChannel, rightChannel, countValuesToCombine);
    case 4:
      return CombineChannelsInt32(leftChannel, rightChannel, countValuesToCombine);
  }
}

(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt16(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt24(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt32(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);

short GetHighestValueInt16(byte[] bytes, int offset, int amount);
Int24 GetHighestValueInt24(byte[] bytes, int offset, int amount);
int GetHighestValueInt32(byte[] bytes, int offset, int amount);
...