2

我知道使用 LSB 意味着您可以将消息存储在图像载体大小的 12% 左右。我制作了一个java程序,将一条消息分成n个片段,并用这些片段填充图像载体,直到12%都被占用。我这样做是为了通过裁剪图像,消息不会丢失。问题是生成的图像失真并且与原始图像不同。我认为如果我只填充图像的 12%,更准确地说是图像的 LSB,图像不会失真。

    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;

} 原始图像 修改后的图像

4

1 回答 1

4

您发布的输出图像是 16 色调色板图像。

我看到的数据表明您实际上已将更改应用于调色板索引,而不是图像的颜色。您看到失真的原因是因为调色板的组织方式,您没有修改颜色的 LSB,您正在修改索引的 LSB,这可能会将其更改为完全不同的(并且非常明显,如您所见)颜色。(实际上,您正在修改每个其他索引的 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。

于 2013-08-16T11:46:45.593 回答