#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, чтобы избежать байта кода фильтра.