C# компресира байтов масив

Не знам много за алгоритмите за компресиране. Търся прост алгоритъм за компресиране (или кодов фрагмент), който може да намали размера на байт[,,] или байт[]. Не мога да използвам System.IO.Compression. Освен това данните се повтарят много.

Опитах се да внедря алгоритъма RLE (публикуван по-долу за ваша проверка). Въпреки това, той произвежда масиви от 1,2 до 1,8 пъти по-големи.

public static class RLE
{
    public static byte[] Encode(byte[] source)
    {
        List<byte> dest = new List<byte>();
        byte runLength;

        for (int i = 0; i < source.Length; i++)
        {
            runLength = 1;
            while (runLength < byte.MaxValue 
                && i + 1 < source.Length 
                && source[i] == source[i + 1])
            {
                runLength++;
                i++;
            }
            dest.Add(runLength);
            dest.Add(source[i]);
        }

        return dest.ToArray();
    }

    public static byte[] Decode(byte[] source)
    {
        List<byte> dest = new List<byte>();
        byte runLength; 

        for (int i = 1; i < source.Length; i+=2)
        {
            runLength = source[i - 1];

            while (runLength > 0)
            {
                dest.Add(source[i]);
                runLength--;
            }
        }
        return dest.ToArray();
    }

}

Също така открих LZW имплементация на базата на Java, низове и цели числа. Преобразувах го в C# и резултатите изглеждат добре (кодът е публикуван по-долу). Не съм сигурен обаче как работи, нито как да го накарам да работи с байтове вместо низове и цели числа.

public class LZW
{
    /* Compress a string to a list of output symbols. */
    public static int[] compress(string uncompressed)
    {
        // Build the dictionary.
        int dictSize = 256;
        Dictionary<string, int> dictionary = new Dictionary<string, int>();
        for (int i = 0; i < dictSize; i++)
            dictionary.Add("" + (char)i, i);

        string w = "";
        List<int> result = new List<int>();

        for (int i = 0; i < uncompressed.Length; i++)
        {
            char c = uncompressed[i];
            string wc = w + c;
            if (dictionary.ContainsKey(wc))
                w = wc;
            else
            {
                result.Add(dictionary[w]);
                // Add wc to the dictionary.
                dictionary.Add(wc, dictSize++);
                w = "" + c;
            }
        }

        // Output the code for w.
        if (w != "")
            result.Add(dictionary[w]);
        return result.ToArray();
    }

    /* Decompress a list of output ks to a string. */
    public static string decompress(int[] compressed)
    {
        int dictSize = 256;
        Dictionary<int, string> dictionary = new Dictionary<int, string>();
        for (int i = 0; i < dictSize; i++)
            dictionary.Add(i, "" + (char)i);

        string w = "" + (char)compressed[0];
        string result = w;
        for (int i = 1; i < compressed.Length; i++)
        {
            int k = compressed[i];
            string entry = "";
            if (dictionary.ContainsKey(k))
                entry = dictionary[k];
            else if (k == dictSize)
                entry = w + w[0];

            result += entry;

            // Add w+entry[0] to the dictionary.
            dictionary.Add(dictSize++, w + entry[0]);

            w = entry;
        }

        return result;
    }
}

person zfedoran    schedule 17.07.2010    source източник
comment
Не мога да използвам System.IO.Compression - защо?   -  person Mitch Wheat    schedule 17.07.2010
comment
за да разширя малко казаното от Мич, има и други библиотеки (като SharpZipLib) така че разбирането защо не можете да използвате съществуващите неща в рамката ще ви помогне да разберете кои други опции могат да работят или не   -  person James Manning    schedule 17.07.2010
comment
Е, не е наличен на моята платформа (xbox 360).   -  person zfedoran    schedule 17.07.2010
comment
Добавен е маркер за xbox360, защото е донякъде подходящ (поне изяснява въпроса това домашно ли е?)   -  person Brendan Long    schedule 17.07.2010


Отговори (3)


Разгледайте тук. Използвах този код като основа за компресиране в един от моите работни проекти. Не съм сигурен каква част от .NET Framework е достъпна в Xbox 360 SDK, така че не съм сигурен колко добре ще работи това за вас.

person GrahamMoore    schedule 17.07.2010

Погледнете кодовете на Huffman, това е доста прост алгоритъм. По принцип използвайте по-малко битове за модели, които се показват по-често, и поддържайте таблица за това как са кодирани. И трябва да отчетете във вашите кодови думи, че няма разделители, които да ви помогнат да декодирате.

person gtrak    schedule 17.07.2010

Проблемът с този RLE алгоритъм е, че е твърде прост. Той поставя пред всеки байт колко пъти се повтаря, но това означава, че в дълги диапазони от неповтарящи се байтове всеки отделен байт има префикс "1". При данни без никакви повторения това ще удвои размера на файла.

Това може да се избегне, като вместо това се използва RLE от тип код; „Кодът“ (наричан още „Token“) ще бъде байт, който може да има две значения; или показва колко пъти се повтаря единичният следващ байт, или показва колко неповтарящи се байта следват, които трябва да бъдат копирани така, както са. Разликата между тези два кода се прави чрез активиране на най-високия бит, което означава, че все още има 7 бита налични за стойността, което означава, че количеството за копиране или повторение на такъв код може да бъде до 127.

Това означава, че дори при най-лошия сценарий, крайният размер може да бъде само около 1/127 по-голям от оригиналния размер на файла.

Добро обяснение на цялата концепция, плюс пълен работещ (и всъщност силно оптимизиран) C# код, можете да намерите тук:

http://www.shikadi.net/moddingwiki/RLE_Compression

Обърнете внимание, че понякога данните ще се окажат по-големи от оригинала все пак, просто защото в тях няма достатъчно повтарящи се байтове, за да работи RLE. Един добър начин да се справите с такива неуспешни компресии е като добавите заглавка към окончателните си данни. Ако просто добавите допълнителен байт в началото, който е на 0 за некомпресирани данни и 1 за RLE компресирани данни, тогава, когато RLE не успее да даде по-малък резултат, вие просто го запазвате некомпресиран, с 0 отпред и вашите крайни данни ще бъде точно един байт по-голям от оригинала. След това системата от другата страна може да прочете този начален байт и да го използва, за да определи дали следните данни трябва да бъдат некомпресирани или просто копирани.

person Nyerguds    schedule 10.01.2018