在C#中进行ushort数组压缩

本文关键字:ushort 数组 压缩 | 更新日期: 2023-09-27 18:29:02

我有一个ushort数组(实际上是来自相机的图像),我想在持久存储之前对其进行无损压缩。我使用的是System.IO.Compression.GZipStream中提供的GZipStream函数。据我所知,这种方法需要将ushort数组转换为字节数组。我的解决方案似乎运行正常,但没有我希望的那么快。图像的原始大小约为2兆字节,压缩时间范围(在我的慢速机器上)为200-400毫秒,解压缩时间范围为100-200毫秒。寻求改善我表现的建议。

public static class Zip
    {
        public static ushort[] Decompress_ByteToShort(byte[] zippedData)
        {
            byte[] decompressedData = null;            
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (MemoryStream inputStream = new MemoryStream(zippedData))
                {
                    using (GZipStream zip = new GZipStream(inputStream, CompressionMode.Decompress))
                    {
                        zip.CopyTo(outputStream);
                    }
                }
                decompressedData = outputStream.ToArray();
            }
            ushort[] decompressShort = new ushort[decompressedData.Length / sizeof(ushort)];
            Buffer.BlockCopy(decompressedData, 0, decompressShort, 0, decompressedData.Length);
            return decompressShort;
        }

        public static byte[] Compress_ShortToByte(ushort[] plainData)
        {
            byte[] compressesData = null;
            byte[] uncompressedData = new byte[plainData.Length * sizeof(ushort)];
            Buffer.BlockCopy(plainData, 0, uncompressedData, 0, plainData.Length * sizeof(ushort));
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (GZipStream zip = new GZipStream(outputStream, CompressionMode.Compress))
                {
                    zip.Write(uncompressedData, 0, uncompressedData.Length);
                }
                //Dont get the MemoryStream data before the GZipStream is closed 
                //since it doesn’t yet contain complete compressed data.
                //GZipStream writes additional data including footer information when its been disposed
                compressesData = outputStream.ToArray();
            }
            return compressesData;
        }
    }

在C#中进行ushort数组压缩

我看到的方法中的第一个问题是使用字节数组,而不是直接加载和写入文件。使用较小的临时缓冲区并直接以块的形式读取/写入流和文件应该会快得多。

在这里,我提出了一些函数和重载,可以用来从字节数组、到字节数组、从流、到流、从文件和到文件进行解压缩。

性能改进应该在10%到20%之间。根据需要尝试调整常数。我使用了DeflateStream而不是GZipStream,这稍微提高了性能。如果您愿意,可以返回GZipStream。

我只尝试了代码的字节到ushort和ushort到byte[]版本,它大约快了10%。直接访问文件而不是将其加载到大缓冲区应该会进一步提高性能。

警告:这种以这种方式读取和写入图像的方法与小端/大端无关-这意味着从Intel/AMD机器保存的文件与ARM机器不兼容,例如在一些平板电脑中!附带说明:)

    /// <summary>The average file size, used to preallocate the right amount of memory for compression.</summary>
    private const int AverageFileSize = 100000;
    /// <summary>The default size of the buffer used to convert data. WARNING: Must be a multiple of 2!</summary>
    private const int BufferSize = 32768;

    /// <summary>Decompresses a byte array to unsigned shorts.</summary>
    public static ushort[] Decompress_ByteToShort(byte[] zippedData)
    {
        using (var inputStream = new MemoryStream(zippedData))
            return Decompress_File(inputStream);
    }
    /// <summary>Decompresses a file to unsigned shorts.</summary>
    public static ushort[] Decompress_File(string inputFilePath)
    {
        using (var stream = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            return Decompress_File(stream);
    }
    /// <summary>Decompresses a file stream to unsigned shorts.</summary>
    public static ushort[] Decompress_File(Stream zippedData)
    {
        using (var zip = new DeflateStream(zippedData, CompressionMode.Decompress, true))
        {
            // Our temporary buffer.
            var buffer = new byte[BufferSize];
            // Read the number of bytes, written initially as header in the file.
            zip.Read(buffer, 0, sizeof(int));
            var resultLength = BitConverter.ToInt32(buffer, 0);
            // Creates the result array
            var result = new ushort[resultLength];
            // Decompress the file chunk by chunk
            var resultOffset = 0;
            for (; ; )
            {
                // Read a chunk of data
                var count = zip.Read(buffer, 0, BufferSize);
                if (count <= 0)
                    break;
                // Copy a piece of the decompressed buffer
                Buffer.BlockCopy(buffer, 0, result, resultOffset, count);
                // Advance counter
                resultOffset += count;
            }
            return result;
        }
    }
    /// <summary>Compresses an ushort array to a file array.</summary>
    public static byte[] Compress_ShortToByte(ushort[] plainData)
    {
        using (var outputStream = new MemoryStream(AverageFileSize))
        {
            Compress_File(plainData, outputStream);
            return outputStream.ToArray();
        }
    }
    /// <summary>Compresses an ushort array directly to a file.</summary>
    public static void Compress_File(ushort[] plainData, string outputFilePath)
    {
        using (var stream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
            Compress_File(plainData, stream);
    }
    /// <summary>Compresses an ushort array directly to a file stream.</summary>
    public static void Compress_File(ushort[] plainData, Stream outputStream)
    {
        using (var zip = new DeflateStream(outputStream, CompressionMode.Compress, true))
        {
            // Our temporary buffer.
            var buffer = new byte[BufferSize];
            // Writes the length of the plain data
            zip.Write(BitConverter.GetBytes(plainData.Length), 0, sizeof(int));
            var inputOffset = 0;
            var availableBytes = plainData.Length * sizeof(ushort);
            while (availableBytes > 0)
            {
                // Compute the amount of bytes to copy.
                var bytesCount = Math.Min(BufferSize, availableBytes);
                // Copy a chunk of plain data into the temporary buffer
                Buffer.BlockCopy(plainData, inputOffset, buffer, 0, bytesCount);
                // Write the buffer
                zip.Write(buffer, 0, bytesCount);
                // Advance counters
                inputOffset += bytesCount;
                availableBytes -= bytesCount;
            }
        }
    }

有GZipStream构造函数(Stream,CompressionLevel),您可以更改CompressionLevel以加快压缩速度。在这个枚举中,有一个级别称为Fastest

相关文档链接:
http://msdn.microsoft.com/pl-pl/library/hh137341(v=vs.110).aspx
http://msdn.microsoft.com/pl-pl/library/system.io.compression.compressionlevel(v=vs.110).aspx