Стеганография Младшая емкость хранения информации

Я знаю, что использование LSB означает, что вы можете хранить сообщения размером около 12% от размера носителя изображения. Я сделал java-программу, которая разбивает сообщение на n фрагментов и заполняет носитель изображения этими фрагментами до тех пор, пока все 12% не будут заняты. Я делаю это, чтобы при обрезке изображения сообщение не потерялось. Проблема в том, что полученное изображение искажено и отличается от исходного изображения. Я думал, что если я заполню только 12% изображения, точнее младший бит изображения, изображение не будет искажено.

    int numHides = imLen/(totalLen*DATA_SIZE); // the number of messages I can store in the image
    int offset = 0;
    for(int h=0; h < numHides; h++) //hide all frags, numHides times
    for(int i=0; i < NUM_FRAGS; i++) {//NUM_FRAGS ..the number of fragments 
          hideStegoFrag(imBytes, stegoFrags[i], offset);//the method that hides the fragment into the picture starting at the offset position
          offset += stegoFrags[i].length*DATA_SIZE;
    }

   private static boolean hideStegoFrag(byte[] imBytes,byte[] stego,int off){
     int offset=off;
     for (int i = 0; i < stego.length; i++) { // loop through stego
      int byteVal = stego[i];
      for(int j=7; j >= 0; j--) { // loop through 8 bits of stego byte
         int bitVal = (byteVal >>> j) & 1;
         // change last bit of image byte to be the stego bit
         imBytes[offset] = (byte)((imBytes[offset] & 0xFE) | bitVal);
         offset++;
      }
    }
    return true;
}

Код преобразования буферизованного изображения в биты

       private static byte[] accessBytes(BufferedImage image)
       {
        WritableRaster raster = image.getRaster();
        DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer();
        return buffer.getData();
       }

Код, создающий новое изображение с заданным именем и буферизованное изображение исходного изображения.

      public static boolean writeImageToFile(String imFnm , BufferedImage im){
    try {
        ImageIO.write(im, "png", new File(imFnm));
    } catch (IOException ex) {
        Logger.getLogger(MultiSteg.class.getName()).log(Level.SEVERE, null, ex);
    }
return true;

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


person Emil Grigore    schedule 16.08.2013    source источник
comment
Какой файл вы меняете? Это будет работать только с файлом BMP, больше ничего.   -  person Vadim    schedule 16.08.2013
comment
@Vadim BMP - это формат хранения. Мы предполагаем, что imBytes — это необработанные данные о цвете, которые уже были загружены и декодированы из файла. Исходный формат файла значения не имеет.   -  person Jason C    schedule 16.08.2013
comment
@EmilGrigore В этой заметке, каков формат данных в imBytes, и можете ли вы опубликовать изображение искажения, которое вы видите?   -  person Jason C    schedule 16.08.2013
comment
@Jason C Полученное изображение имеет формат bmp ... исходное изображение также имеет формат bmp   -  person Emil Grigore    schedule 16.08.2013
comment
@JasonC, я согласен с тобой. Просто хотел убедиться.   -  person Vadim    schedule 16.08.2013
comment
Покажите нам код, в котором вы загружаете и сохраняете файл изображения.   -  person Jason C    schedule 16.08.2013
comment
@ Вадим, я думаю, ты был на правильном пути.   -  person Jason C    schedule 16.08.2013


Ответы (1)


Выходное изображение, которое вы разместили, представляет собой изображение с палитрой из 16 цветов.

Данные, которые я вижу, показывают, что вы фактически применили свои изменения к индексу палитры, а не к цветам изображения. Причина, по которой вы видите искажение, заключается в том, как организована палитра, вы не изменяете младший бит цвета, вы изменяете младший бит индекса, который может изменить его на совершенно другой (и очень заметный). , как видите) цвет. (На самом деле вы изменяете LSB каждого другого индекса, 16-цветная форма — это 4 бита на пиксель, 2 пикселя на байт.)

Похоже, вы загрузили необработанные данные изображения и не декодировали их в информацию о цвете RGB. Ваш алгоритм будет работать только с необработанными данными RGB (или необработанными оттенками серого); 3 байта (или 1 для оттенков серого) на пиксель. Вам нужно преобразовать изображение в RGB888 или что-то подобное, прежде чем работать с ним. Когда вы сохраняете его, вам также необходимо сохранить его в полноцветном формате без потерь (если вы действительно не можете уместить все свои цвета в палитре), иначе вы рискуете потерять свою информацию.

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

Когда вы загружаете данные изображения, вам необходимо преобразовать их в формат RGB. Наиболее удобным форматом для вашего приложения будет BufferedImage.TYPE_3BYTE_BGR, который хранит каждый пиксель в виде трех байтов в порядке синего, зеленого, красного (так что ваш массив байтов будет B, G, R, B, G, R, B, G, R,... ). Вы можете сделать это так:

public static BufferedImage loadRgbImage (String filename) {
    // load the original image
    BufferedImage originalImage = ImageIO.read(filename); 
    // create buffer for converted image in RGB format
    BufferedImage rgbImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
    // render original image into buffer, changes to destination format
    rgbImage.getGraphics().drawImage(originalImage, 0, 0, null);
    return rgbImage; 
}

Если вы все равно часто работаете с исходными изображениями, которые уже находятся в формате BGR, вы можете сделать одну простую оптимизацию, чтобы не преобразовывать изображение, если оно уже находится в нужном вам формате:

public static BufferedImage loadRgbImage (String filename) {
    BufferedImage originalImage = ImageIO.read(filename); 
    BufferedImage rgbImage;
    if (originalImage.getType() == BufferedImage.TYPE_3BYTE_BGR) {
        rgbImage = originalImage; // no need to convert, just return original
    } else {
        rgbImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
        rgbImage.getGraphics().drawImage(originalImage, 0, 0, null);
    }
    return rgbImage; 
}

Затем вы можете просто использовать преобразованное изображение для всех своих операций. Обратите внимание, что массив байтов из преобразованного изображения будет содержать 3 * rgbImage.getWidth() * rgbImage.getHeight() байт.

Вам не нужно вносить какие-либо изменения в текущий код сохранения изображения; ImageIO обнаружит, что изображение RGB, и запишет 24-битный PNG.

person Jason C    schedule 16.08.2013
comment
Спасибо за ответ. Можете ли вы объяснить это немного подробнее. Я новичок в стеганографии изображений и не совсем понимаю ваше решение. - person Emil Grigore; 16.08.2013