更改图像rgb值的lsb值,给出不一致的值

我试图更改图像像素的lsb值,使其与字符串“abc”匹配,但将1或0添加到奇数值的像素返回0. 这里是代码:

public static void main(String[] args) { BufferedImage img = null; try { img = ImageIO.read(new File("a.jpg")); } catch (IOException ex) { } int pixel[] = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()); String s = "abc"; byte[] b = s.getBytes(); String f = ""; for (int i = 0; i < b.length; i++) { f += Integer.toBinaryString(b[i]); } f.trim(); int[] newpixel = new int[pixel.length]; for (int i = 0; i < pixel.length; i++) { if (i < f.length()) { if (pixel[i] % 2 == 0) { if (f.charAt(i) == '0') { newpixel[i] = pixel[i]; } if (f.charAt(i) == '1') { newpixel[i] = pixel[i] + 1; } } if (pixel[i] % 2 == 1) { if (f.charAt(i) == '0') { newpixel[i] = pixel[i] - 1; } if (f.charAt(i) == '1') { newpixel[i] = pixel[i]; } } } else { newpixel[i] = pixel[i]; } } o: for (int i = 0; i < img.getWidth() * img.getHeight(); i++) { if (i < f.length()) { System.out.print(" " + f.charAt(i) + ":(" + pixel[i] + "," + newpixel[i] + ")"); } else { break o; } } } 

输出是:

1:( – 11235948,-11235947)1:( – 11893363,0)0:( – 11893617,0)0:( – 10577497,0)0:( – 11695976,-11695976)0:( – 12090996,-12090996 )1:( – 11170168,-11170167)1:( – 10775924,-10775923)1:( – 9724765,0)0:( – 9658965,0)0:( – 9856341,0)0 🙁 – 11236466, – 11236466)1:( – 11564174,-11564173)0 🙁 – 11431819,0)1:( – 10380136,-10380135)1:( – 10973290,-10973289)0:( – 12093056,-12093056)0 🙁 – 10842985,0)0:( – 10118999,0)1:( – 11368034,-11368033)1:( – 11630686,-11630685)

负奇数的模数在java中不返回1 ,因此不执行if (pixel[i] % 2 == 1)块。 从上面的链接中,您可以通过写入而获得正数if ((((pixel[i] % 2) + 2) % 2) == 1) 。 但是,数字的奇偶校验是独占的,所以它是偶数或奇数。 建议您将代码更改为此代码

 if (pixel[i] % 2 == 0) { ... } else { ... } 

您的代码有另一个错误。 行f += Integer.toBinaryString(b[i]); 将字符转换为二进制字符串,但如果ascii字符的值小于128,则转换将使用最小位数。 例如, a = '1100001' ,仅为7位。 你想用左边的0填充得到8位。 快速搜索产生这一点 ,上面的行应该改为

 f += String.format("%8s", Integer.toBinaryString(b[i])).replace(' ', '0'); 

如果我可以为您的代码推荐一些可选的改进,我会按如下方式进行

 import java.util.Arrays; public static void main(String[] args) { BufferedImage img = null; try { img = ImageIO.read(new File("a.jpg")); } catch (IOException ex) { } int pixel[] = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()); int[] newpixel = Arrays.copyOf(pixel, pixel.length); String s = "abc"; byte[] b = s.getBytes(); int count = 0; for (int i = 0; i < b.length; i++) { byte current_byte = b[i]; for (int j = 7; j >= 0; j--) { int lsb = (current_byte >> j) & 1; newpixel[count] = (pixel[count] & 0xfffffffe) + lsb; System.out.println(lsb + ":(" + pixel[count] + "," + newpixel[count] + ")"); count++; } } // Extraction sequence String secret = ""; int bit = 0; for (int i = 0; i < b.length; i++) { int ascii = 0; for (int j = 7; j >=0; j--) { ascii += (newpixel[bit] & 1) << j; bit++; } secret += (char)ascii; } System.out.print(secret); } 

笔记:

Arrays.copyOf()是为了保留原始图像的副本以比较差异。 通常情况下,我只是在现场直接编辑pixel

您不需要将字节转换为1和0的字符串,因为稍后您将需要这些数字作为整数。 以下循环使用右位移和按位和操作从最高有效位(最左侧)到最小位逐个提取位。

 for (int j = 7; j >=0; j--) { int lsb = (b[i] >> j) & 1; } 

您可以将其归零,然后从上面的循环中添加lsb,而不是检查像素的lsb的值。 您可以使用按位和操作来实现此目的。 像素由四个字节组成,每个字节的最大值为255(0xff)。 字节对应于Alpha透明度,红色,绿色和蓝色通道(称为ARGB)。 你可以在这里阅读它。

 newpixel[count] = (pixel[count] & 0xfffffffe) + lsb; 

提取过程与嵌入相反。 但这里有一个诀窍。 在提取整个消息之前,程序不知道要读取多少像素。 你想要做的是引入一个等于8 * b.length的长度变量。 你可以分配前16个像素来隐藏这个数字,分别为1和0,就像你的角色一样。 然后提取读取前16个像素,计算要读取的像素数,并从第17个像素开始计算。