Создание кода для распаковки байт-ориентированного RLE-изображения

Я пытаюсь создать код для распаковки изображения RLE, ориентированного на байты, из файла PostScript. Я уже пробовал решения, найденные в Интернете, а также пытался создать свой собственный; но ни один из них не дал нужного мне результата.

После распаковки изображения rle у меня должно быть изображение RAW, которое я могу открыть в фотошопе (с указанием ширины, высоты и количества каналов). Однако, когда я пытаюсь открыть извлеченное изображение, оно не работает; отображается только черный вывод.

Мои входные данные представляют собой двоичный файл с кодировкой ASCII (закодированный как шестнадцатеричная строка) и двоичный файл; оба RLE сжаты с байтовой ориентацией (в случае шестнадцатеричного файла это просто вопрос преобразования его в байты перед попыткой декомпрессии rle).

https://drive.google.com/drive/u/0/folders/1Q476HB9SvOG_RDwK6J7PPycbw94zjPYU Я разместил образцы здесь.

Рабочий образец.raw -> Образец изображения, который я получил с помощью другого программного обеспечения, а также его размеры.

MySample.raw -> Образец изображения, который я создал, используя свой код, а также его размеры.

Исходный файл.ppf -> Файл, содержащий исходные данные изображения и все остальное.

Извлеченный двоичный файл.bin -> Только бинарная часть из OriginalFile.ppf — облегчает чтение и работу с данными.

Этот код был предоставлен пользователем нергудс, он является частью сообщества SO. Оригинальный источник: http://www.shikadi.net/moddingwiki/RLE_Compression#Types_of_RLE Это тот, который я пытался использовать, но результаты были неправильными. И, честно говоря, мне было трудно понять его код (он сказал мне изменить несколько вещей, чтобы он работал в моем случае, но я не смог).

И вот что я пытался сделать, следуя Красной книге PostScript: Книга: https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf Часть: «Фильтр RunLengthEncode кодирует данные в формате, ориентированном на простые байты, на основе длины цикла. Формат сжатых данных представляет собой последовательность запусков, где каждый запуск состоит из байта длины, за которым следуют от 1 до 128 байтов данных. Если длина байта находится в диапазоне от 0 до 127, следующая длина + 1 байт (от 1 до 128 байт) должна быть скопирована буквально при распаковке. Если длина находится в диапазоне от 129 до 255, следующий одиночный байт должен быть реплицирован 257 раз (от 2 до 128 раз) при распаковке." Страница 142, Фильтр RunLengthEncode.

List<byte> final = new List<byte>();
                var split01 = ArraySplit(bytefile, 2);
                foreach (var binPart in split01)
                {                    
                    try
                    {
                        if (binPart.ElementAt(0) <= 127)
                        {
                            int currLen = binPart[0] + 1;
                            for (int i = 0; i <= binPart[0]; i++)
                            {
                                final.Add(binPart[1]);
                                //Console.WriteLine(binPart[1]);
                            }
                        }
                        else if (binPart[0] >= 128)
                        {
                            int currLen = 257 - binPart[0];
                            for (int i = 0; i < currLen; i++)
                            {
                                final.Add(binPart[1]);
                                // Console.WriteLine(binPart[1]);
                            }
                        }
                    }
                    catch(Exception)
                    {
                        break;
                    }


                }

                File.WriteAllBytes(@"C:\test\again.raw", final.ToArray());

 private static IEnumerable<byte[]> ArraySplit(byte[] bArray, int intBufforLengt)
        {
            int bArrayLenght = bArray.Length;
            byte[] bReturn = null;

            int i = 0;
            for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
            {
                bReturn = new byte[intBufforLengt];
                Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
                yield return bReturn;
            }

            int intBufforLeft = bArrayLenght - i * intBufforLengt;
            if (intBufforLeft > 0)
            {
                bReturn = new byte[intBufforLeft];
                Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
                yield return bReturn;
            }
        }

  private static byte[] StringToByteArray(String hex)
        {
            int iValue = 0;
            int NumberChars = hex.Length;
            if (NumberChars % 2 != 0)
            {
                string m = string.Empty;
            }
            byte[] bytes = new byte[NumberChars / 2];
            try
            {

                for (int i = 0; i < NumberChars; i += 2)
                {
                    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
                    iValue = i;
                }

            }
            catch (Exception e)
            {
                var value = iValue;
                Console.WriteLine(e.Message);
            }


            return bytes;
        }

Желаемый результат будет TIFF в оттенках серого. Однако я также могу иметь дело с PNG. Мне уже удалось извлечь данные несжатый из такого файла; с Emgu (OpenCV Wrapper) я смог создать видимое изображение и выполнить на нем свою логику.

Мои фактические результаты от RLE Compressed — это только недействительные файлы RAW, которые нельзя просмотреть даже в фотошопе или IrfanViewer.

Любой вклад приветствуется. Спасибо.

РЕДАКТИРОВАТЬ1: застрял на этой части

for(int i=0; i < bytefile.Length; i+=2)
            {
                try
                {
                    var lengthByte = bytefile[i];
                    if (lengthByte <= 127)
                    {
                        int currLen = lengthByte + 1;
                        for (int j = 0; j < currLen; j++)
                        {
                            final.Add(bytefile[i]);
                            i++;
                        }

                    }
                    if (bytefile[i] >= 128)
                    {
                        int currLen = 257 - bytefile[i];
                        for (int k = 0; k < currLen; k++)
                        {
                            final.Add(bytefile[i + 1]);
                        }
                    }
                }
                catch(Exception)
                {
                    break;
                }          
            }

Это логика, которой я следую. Раньше это вызывало исключение, но я понял это (это было потому, что я забыл добавить конечный байт; это не имеет значения в конечном результате).

Вы туда не несжатые байты копируете, а только binPart[1] - не должно быть binPart[i + 1] или что-то подобное?

500 - Internal Server Error 11.04.2019 16:39

Это было мое понимание объяснения ... может быть, я ошибся в понимании английского? Не знаю. У меня очень хороший английский, но я действительно запутался... Если длина байта >= 128, я должен реплицировать следующий байт 257 - длина раз. Эта часть у меня очень хорошо получилась. Возможно, я немного запутался в первой части (первое условие). Давай попробуем здесь.

paboobhzx 11.04.2019 16:42

Скопировано сверху: If the length byte is in the range 0 to 127, the FOLLOWING length + 1 BYTES (1 to 128 bytes) are to be copied literally upon decompression - обратите внимание на мой акцент.

500 - Internal Server Error 11.04.2019 16:44

Учитывая, что длина байта равна 5. Я должен скопировать следующие шесть (5 + 1) байтов, а затем перейти к следующей итерации цикла?

paboobhzx 11.04.2019 16:50

Почти. Скопируйте следующие 5 байтов, с [1] по [6], затем установите следующий индекс чтения на 7 для следующей итерации цикла.

500 - Internal Server Error 11.04.2019 16:52

Извините, мой плохой, вы правы, если значение 5, то нужно скопировать 6 байт.

500 - Internal Server Error 11.04.2019 16:54

Если это так, прежде всего я должен изменить цикл на обычный цикл (вместо foreach). Держите вторую попытку внести изменения

paboobhzx 11.04.2019 16:54

Я отредактировал вопрос. Я добавил, где я застрял на данный момент .... не могу понять, как добавить следующие байты без какого-либо RangeException.

paboobhzx 11.04.2019 17:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
414
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Хотя это не указано явно, я полагаю, что вы пытаетесь извлечь изображение с кодировкой RunLength из файла Postscript и сохранить его как TIFF в оттенках серого.

В качестве отправной точки для чего-то подобного вы пытались просто сохранить изображение несжатый из файла Postscript в виде TIFF в оттенках серого, чтобы убедиться, что логика вашего приложения, отвечающая за создание данных изображения TIFF, действительно работает так, как вы ожидаете? Я хотел бы предупредить, что это будет хорошим первым шагом, прежде чем переходить к поддержке распаковки данных RLE для последующего преобразования в TIFF.

Причина, по которой я считаю это важным, заключается в том, что ваша проблема может не иметь ничего общего с как, когда вы распаковываете данные RLE, а скорее с тем, как вы создаете выходной TIFF из предположительно правильно декодированных данных.

Привет. Да, я сделал. И это сработало, я смог создать TIFF с помощью Emgu (OpenCV Wrapper). Сейчас я отредактирую вопрос и добавлю текущий прогресс.

paboobhzx 11.04.2019 16:26

я отредактировал вопрос и добавил информацию. Как я уже сказал, логика создания изображения из несжатых данных в порядке; никаких проблем по этому поводу. Мои проблемы связаны со сжатием RLE. Мне удалось извлечь только «части» исходного изображения, но часть его имеет только черные пятна (например, отсутствие данных). Анализируя файл в шестнадцатеричном редакторе, я также вижу много нулей (где у меня должны быть данные).

paboobhzx 11.04.2019 16:35
Ответ принят как подходящий

Попробуйте эту базовую схему:

int i = 0;
while (i < bytefile.length)    
{
    var lengthByte = bytefile[i++];
    if (lengthByte <= 127)
    {
        int currLen = lengthByte + 1;
        for (int j = 0; j < currLen; j++)
            final.Add(bytefile[i++]);
    }
    else
    {
        int currLen = 257 - lengthByte;
        byte byteToCopy = bytefile[i++];
        for (int j = 0; j < currLen; j++)
            final.Add(byteToCopy);
    }
}

Во всяком случае, так я понимаю то, что указано выше.

Я думаю, что ваш второй currlen неправильный. Это всего лишь 257 - lengthByte, не более того. Я изменил эту строку, и изображение заработало. Я попробую больше ампул, но эй - спасибо! Через 5 дней я добился некоторого прогресса. Кажется, я был близок к этому...

paboobhzx 11.04.2019 18:32

просто вопрос: я не вижу, чтобы вы где-либо обновляли значение I. Код останавливается, потому что я добавил условие исключения, которое прерывается, когда оно выходит за пределы допустимого диапазона. Я получаю его приращение внутри байтового файла?

paboobhzx 11.04.2019 19:35
i++ увеличивает i (и возвращает его значение до того, как оно было увеличено).
500 - Internal Server Error 12.04.2019 13:30

Другие вопросы по теме