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

时间:2014-10-28 14:17:11

标签: java rgb bufferedimage steganography

我正在尝试更改图像像素的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)

1 个答案:

答案 0 :(得分:1)

The modulo of a negative odd number does not return 1 in java,因此您的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位。快速搜索会产生this,上面的行应更改为

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的字符串,因为您以后需要将这些数字作为整数。以下循环使用right bitshiftbitwise and操作从最高位(最左侧)到最小位逐个提取位。

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

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

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

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