Я читаю изображение и меняю его. Но изменения не сохраняются

Я пытаюсь реализовать стеганографию. Я читаю изображение «a.jpeg» и вставляю в него байт, изменяя его последовательные 7 байтов в наименее значащем бите, начиная со смещения 50. Это делается успешно, так как когда я печатаю байты, последние биты соответственно изменяются. Затем я сохранил его как «ao.jpeg». Но когда я читаю значения байтов из 50, они не совпадают с теми, которые я сохранил. вот мой код

public static void main(String[] args) throws IOException {
        BufferedImage inputImage = ImageIO.read(new File("a.jpeg"));
        int offset=50;
        byte data = 7;
        byte[] image = get_byte_data(inputImage);//function converts bufferedimage to byte array
        //add data at end of each byte starting from offset
        System.out.println("bytes altered are :");
        for(int bit=7; bit>=0; --bit, ++offset)//for each bit of data
            {
            int b = (data >>> bit) & 1;
            image[offset] = (byte)((image[offset] & 0xFE) | b );
            String s1 = String.format("%8s", Integer.toBinaryString(image[offset] & 0xFF)).replace(' ', '0');
            System.out.println(s1);
            }
        //write changed image to ao.jpeg
        BufferedImage outputImage = ImageIO.read(new ByteArrayInputStream(image)); 
        File outputfile = new File("ao.jpeg");
        ImageIO.write(outputImage,"jpeg",outputfile);
        //read data from ao.jpeg
        System.out.println("bytes from encoded image are :");
        byte result=0;
        offset=50;
        BufferedImage oImage = ImageIO.read(new File("aoc.jpeg"));
        byte[] image1 = get_byte_data(oImage);//function converts bufferedimage to byte array
            for(int i=0; i<8; i++, ++offset)
             {
             result = (byte)((result << 1) | (image1[offset] & 1));
             String s1 = String.format("%8s", Integer.toBinaryString(image1[offset] & 0xFF)).replace(' ', '0');
             System.out.println(s1);
             }
            System.out.println("recovered data is :");
            System.out.print(result);
        }

выходной образец: вставленные данные равны 7. Если вы заметили, что младший значащий бит каждого байта составляет 7. Но когда я читаю его снова, это случайные байты.

bytes altered are :
00010100
00011100
00011010
00011110
00011110
00011101
00011011
00011101
bytes from encoded image are :
00011110
00011101
00011010
00011100
00011100
00100000
00100100
00101110
recovered data is :
64

По предложению Константина В. Салихова я попробовал другой формат файла (gif), и это сработало. Но можно ли как-нибудь использовать "jpeg"?


person Hitesh Agrawal    schedule 29.08.2014    source источник
comment
Может быть, это из-за сжатия с потерями JPEG?   -  person Konstantin V. Salikhov    schedule 29.08.2014
comment
я попробую другой формат и опубликую результат   -  person Hitesh Agrawal    schedule 29.08.2014
comment
любое решение вы предлагаете ??   -  person Hitesh Agrawal    schedule 29.08.2014
comment
эй, это сработало с gif.   -  person Hitesh Agrawal    schedule 29.08.2014
comment
но я хотел бы использовать jpeg тоже. любое решение будет оценено.   -  person Hitesh Agrawal    schedule 29.08.2014
comment
Измените более значимый бит. Основной принцип JPEG заключается в том, чтобы не точно сохранять информацию о младших битах, поэтому он так хорошо сжимается.   -  person Jongware    schedule 29.08.2014
comment
Изменение более значимого бита не сработает по той же причине. Сжатие JPEG эффективно изменяет значение пикселя, иногда на значение 1. А что, если у вас есть 00011111, а оно изменено на 00100000? Вы потеряли информацию, которую закодировали в 5-м младшем разряде.   -  person Reti43    schedule 29.08.2014


Ответы (2)


Почему ваш подход не работает

Как предположил Салинков, формат jpeg вызывает сжатие данных. Подводя итог тому, что вы сделали:

  • Вы загрузили данные изображения в массив байтов.
  • Вы изменили некоторые байты.
  • Вы загрузили массив байтов как изображение.
  • Вы сохранили это изображение в формате jpeg, что вызывает повторное сжатие.

Вот где ваш метод разваливается. Формат без потерь не создаст этих проблем, поэтому gif работает. Или png, bmp и т.д.

Можете ли вы использовать свой метод для jpeg?

Ууууу, нет, не совсем. Во-первых, нам нужно понять, какие данные содержит изображение в формате jpeg.

Короткий ответ заключается в том, что отдельные байты не соответствуют реальным пикселям изображения.

Долгая история заключается в том, что вы разбиваете изображение на массивы 8x8 и используете DCT для получения частотных коэффициентов. После шага квантования многие из них станут равными 0, особенно более высокие частотные коэффициенты (нижний правый угол массива DCT — см. здесь). Это шаг jpeg с потерями. Вы жертвуете более высокими частотными коэффициентами ради некоторого допустимого искажения изображения (потеря информации). Теперь вы сохраняете ненулевые коэффициенты в зависимости от их положения в матрице, то есть (0, 0, -26), (0, 1, -3) и т. д. Это можно дополнительно сжать с помощью кодирования Хаффмана. Кстати, изменение любой одной частотной составляющей влияет на все 64 пикселя.

Итак, как обычно выполняется стеганография jpeg? В основном это следует за процессом кодирования jpeg:

  • Разбить изображение на массивы 8x8.
  • DCT каждый массив 8x8 и квантуем коэффициенты.

Теперь мы получили квантованные коэффициенты DCT и делаем перерыв в процессе кодирования jpeg.

  • Примените некоторый стеганографический алгоритм, изменив значение квантованных коэффициентов.

  • Хаффман сжимает коэффициенты и продолжает остальную часть процесса кодирования jpeg с этими измененными коэффициентами DCT.

person Reti43    schedule 29.08.2014

JPEG — это механизм хранения с потерями. Это означает, что НЕ требуется (или даже желательно), чтобы каждый байт представлялся точно так же, как оригинал. На самом деле, в этом весь смысл, он жертвует небольшими недостатками, чтобы добиться большой экономии места. Если вам нужно хранилище с точностью до байта, вам придется выбрать другой формат, например GIF, PNG или некоторые разновидности BMP.

Как указано ниже, технически возможно создать JPEG без потерь, но это было поздним добавлением, не полностью поддерживаемым, и, в частности, Java не поддерживает его изначально. См. этот ответ для получения дополнительной информации.

person Dwayne Towell    schedule 29.08.2014
comment
На самом деле существуют модификации JPEG без потерь, если OP так сильно хочет использовать JPEG. en.wikipedia.org/wiki/Lossless_JPEG - person Konstantin V. Salikhov; 29.08.2014
comment
GIF поддерживает только режим индексации цвета, поэтому изменение даже одного бита по существу приведет к изменению пикселя на любой случайный цвет (включая, возможно, прозрачные). Наш OP уверенно заявил, что он работал с gif, но, конечно же, у него был целый набор собственных видимых артефактов. - person Jongware; 29.08.2014
comment
спасибо за ответ. Я не хотел использовать jpeg, я просто хотел знать, возможно ли это. И когда я использовал gif, изображение не было заметно изменено (возможно, это потому, что я изменил только несколько байтов). - person Hitesh Agrawal; 29.08.2014