我知道使用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;
}
答案 0 :(得分: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。