我正在尝试更改图像像素的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)
最佳答案
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 bitshift和bitwise 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个像素开始进行读取。