Question

I know that using LSB means that u can store messages at around 12% of the image carrier size. I made a java program that splits a message into n fragments and fills the image carrier with these fragments until the 12 % are all occupied.I do this so that by cropping the image,the message wouldn't get lost. The problem is that the resulting image is distorted and different from the original image.I thought that if I fill only 12% of the image,more exactly the LSB of the image,the image wouldn't get distorted.

    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;
}

The code for transforming the Buffered Image into bits

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

The code that creates the new image with a provided name and the buffered image of the source image

      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;

} original image The modified image

Was it helpful?

Solution

The output image you have posted is a 16-color paletted image.

The data I am seeing shows that you have actually applied your changes to the palette index, not to the colors of the image. The reason you are seeing the distortion is because of the way the palette is organized, you aren't modifying the LSB of the color, you're modifying the LSB of the index, which could change it to a completely different (and very noticeable, as you can see) color. (Actually, you're modifying the LSB of every other index, the 16-color form is 4 bits per pixel, 2 pixels per byte.)

It looks like you loaded raw image data and didn't decode it in to RGB color information. Your algorithm will only work on raw RGB (or raw grayscale) data; 3 bytes (or 1 for grayscale) per pixel. You need to convert your image to RGB888 or something similar before you operate on it. When you save it, you need to save it in a lossless, full color (unless you actually can fit all your colors in a palette) format too, otherwise you risk losing your information.

Your problem actually doesn't lie in the steganography portion of your program, but in the loading and saving of the image data itself.

When you load the image data, you need to convert it to an RGB format. The most convenient format for your application will be BufferedImage.TYPE_3BYTE_BGR, which stores each pixel as three bytes in blue, green, red order (so your byte array will be B,G,R,B,G,R,B,G,R,...). You can do that like so:

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; 
}

If you are frequently working with source images that are already in BGR format anyways, you can make one easy optimization to not convert the image if it's already in the format you want:

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; 
}

You can then just use the converted image for all of your operations. Note that the byte array from the converted image will contain 3 * rgbImage.getWidth() * rgbImage.getHeight() bytes.

You shouldn't have to make any changes to your current image saving code; ImageIO will detect that the image is RGB and will write a 24-bit PNG.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top