Реконструкция фильтра Java PNG

#java #filter #png

#java #Фильтр #png

Вопрос:

Я пытаюсь восстановить изображение PNG с заданным байтом [], которое уже не сжато (раздуто). Вот мой код для восстановления фильтра:

 private byte[] unfilterBytes(byte[] input) {
        byte[] unfiltered_data = new byte[input.length - mHeight];
        //calculate width based on color type
        int stride = 0;
        int max_bytes_per_pixels = 0;
        switch(mColorType){
            case 0:     //Greyscale - Each pixel is a greyscale sample
                if(mBitDepth <= 8){
                    stride = mWidth;
                }else{
                    stride = mWidth*2;
                }
                max_bytes_per_pixels = 1;
                break;
            case 2:     //Truecolor - Each pixel is an RGB triple
                stride = mWidth*(mBitDepth>>>3)*3;
                max_bytes_per_pixels = 3;
                break;
            case 3:     //Indexed-Color
                stride = mWidth;
                max_bytes_per_pixels = 1;
                break;
            case 4:     //Greyscale with Alpha
                stride = mWidth*(mBitDepth>>>3)*2;
                max_bytes_per_pixels = 2;
                break;
            case 6:     //Truecolor with Alpha
                stride = mWidth*(mBitDepth>>>3)*4;
                max_bytes_per_pixels = 4;
                break;
            default:
                break;
        }
        int i = 0, j, bytes_written = 0;
        byte[] previous_scanline = new byte[stride];
        for(i=0; i<stride;   i){
            previous_scanline[i] = 0x00;
        }
        i = 0;
        while(i < input.length){
            byte filter_code = input[i  ];
            //unfilter
            //if(filter_code != 0) System.err.printf("Filter[%d]: %dn", i, filter_code);
            switch(filter_code){
                case 0x0:
                    //None
                    System.arraycopy(input, i, unfiltered_data, bytes_written, stride);
                    System.arraycopy(input, i, previous_scanline, 0, stride);
                    i  = stride;
                    break;
                case 0x1:
                    //Sub
                    System.arraycopy(input, i, unfiltered_data, bytes_written, max_bytes_per_pixels);
                    System.arraycopy(input, i, previous_scanline, 0, max_bytes_per_pixels);
                    i  = max_bytes_per_pixels;
                    for(j=max_bytes_per_pixels; j<stride;   j,   i){
                        unfiltered_data[i] = (byte)( (input[i]   unfiltered_data[i-max_bytes_per_pixels]) amp; 0xff );
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    break;
                case 0x2:
                    //Up
                    for(j=0; j<stride;   j,   i){
                        unfiltered_data[i] = (byte)( (input[i]   previous_scanline[j]) amp; 0xff );
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    break;
                case 0x3:
                    //Average
                    for(j=0; j<max_bytes_per_pixels;   j,   i){
                        unfiltered_data[i] = (byte) Math.floor( previous_scanline[j]>>>1 );
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    for(j=max_bytes_per_pixels; j<stride;   j,   i){
                        unfiltered_data[i] = (byte) Math.floor( (unfiltered_data[i-max_bytes_per_pixels]   previous_scanline[j])>>>1 );
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    break;
                case 0x4:
                    //Paeth
                    byte a, b, c;
                    for(j=0; j<max_bytes_per_pixels;   j,   i){
                        a = 0x0;
                        b = previous_scanline[j];
                        c = 0x0;
                        unfiltered_data[i] = paeth(a, b, c);
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    for(j=max_bytes_per_pixels; j<stride;   j,   i){
                        a = unfiltered_data[i-max_bytes_per_pixels];
                        b = previous_scanline[j];
                        c = previous_scanline[j-max_bytes_per_pixels];
                        unfiltered_data[i] = (byte) ( (input[i]   paeth(a, b, c)) amp; 0xff );
                        previous_scanline[j] = unfiltered_data[i];
                    }
                    break;
                default:
                    System.err.println("Filter byte code: "   Integer.toHexString(filter_code));
                    break;
            }
            bytes_written  = stride;
        }

        return unfiltered_data;
    }
  

Вот моя функция paeth:

 private byte paeth(byte a, byte b, byte c){
        short p = (short) (a   b - c);
        short pa = (short) Math.abs(p - a);
        short pb = (short) Math.abs(p - b);
        short pc = (short) Math.abs(p - c);
        short pr;
        if(pa <= pb amp;amp; pa <= pc){
            pr = a;
        }else if(pb <= pc){
            pr = b;
        }else{
            pr = c;
        }
        return (byte)(pr amp; 0xff);
    }
  

Здесь я использую short, поскольку Java подписана. Я не уверен, правильно ли я интерпретирую спецификацию файла PNG во время восстановления фильтра. Приветствуется любая помощь, указывающая, что не так с моей логикой восстановления фильтра.

Исходное изображение

Декодированное изображение

Было бы полезно, если бы я мог получить некоторое подтверждение по следующей логике. Учитывая, что образец изображения является:

128×128 с 8-битным истинным цветом альфа

  • Строка сканирования равна 128 * 4 байтам = 512 байтам (RRGGBBAA)
  • Всего 128 строк сканирования и в общей сложности 65536 байт
  • Предыдущий байт x — это просто строка сканирования[i-1], и если i-1 меньше 0, то просто используйте значение 0
  • После восстановления каждой строки сканирования мы сохраняем ее как предыдущую строку сканирования для других типов фильтрации, которые будут использоваться на следующей итерации
  • Байт перед x в предыдущей строке сканирования — это просто previous_scanline[i-1], и если i-1 меньше 0, то просто используйте значение 0

Любое из приведенных выше предположений неверно?

Комментарии:

1. Было бы полезно, если бы вы указали, почему вы считаете , что ваша реализация неверна.

2. Ну, изображение, декодированное в RRGGBBAA, визуально отличается, и поскольку я проверил правильность моей инфляции, это единственное место, где она неверна. Кажется, я не могу быть уверен, что означает спецификация PNG при восстановлении filtwr.

3. Я обнаружил, что буквенное следование спецификациям очень полезно при выполнении этого в C. Не могу вспомнить, однако, как мне удалось устранить ошибки. У вас есть набор тестовых изображений, каждое из которых использует только один из разных фильтров?

4. Я знаю, что фильтр нулевого типа работает, поскольку он самый простой: отбросьте байт фильтрации. Я могу подтвердить, что он работает, потому что он может декодировать цвет индекса и True Color Alpha, при этом все байты фильтра равны нулю. Как бы вы нашли изображение, в котором указан только тип фильтра 1?

5. Проверьте свои функции фильтра. На первый взгляд кажется, что вы переключаетесь между использованием previous и unfiltered . Выберите один метод. Сравните removeRowFilters функцию в моем собственном исходном коде pngdefry C . Ваш искаженный результат также показывает что-то подозрительное в вашем обновлении stride — возможно, вы захотите сначала исправить это.

Ответ №1:

Любое из приведенных выше предположений неверно?

Это зависит от того, что вы называете «scanline». Включает ли он байт фильтра или нет?

Всего 128 строк сканирования и в общей сложности 65536 байт

Имеется 128 строк сканирования, каждая из которых содержит 512 байт плюс один байт фильтра. Следовательно, общее количество байтов в блоках IDAT равно 128 * 513 = 65664.

Предыдущий байт x — это просто строка сканирования[i-1], и если i-1 меньше 0, то просто используйте значение 0

Я полагаю, вы имеете в виду предыдущий байт в той же строке. Это неправильно. Вам нужно использовать, scanline[i-b] где b = байты на пиксель (в вашем случае, b = 4). Это объясняется в документах

Байт перед x в предыдущей строке сканирования — это просто previous_scanline[i-1], и если i-1 меньше 0, то просто используйте значение 0

То же замечание, что и раньше.

После восстановления каждой строки сканирования мы сохраняем ее как предыдущую строку сканирования для других типов фильтрации, которые будут использоваться на следующей итерации

Все в порядке.

Комментарии:

1. Спасибо за ответ. Я попытаюсь соответствующим образом декодировать и посмотреть, сработает ли это.

2. Самая большая ошибка заключается в том, что я неправильно подсчитал индекс нефильтрованных данных. Для каждой обработанной мной строки сканирования мне нужно сдвинуть индекс обратно на 1, чтобы избежать байта кода фильтра.