问题
I am trying to change the lsb value of image pixels such that it matches with the string "abc" but adding 1 or 0 to a pixel with odd value is returning 0. here is the code:
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;
}
}
}
and the output is:
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:
The modulo of a negative odd number does not return 1 in java, so your if (pixel[i] % 2 == 1)
block is not executed. From the link above, you can get a positive number by writing instead if ((((pixel[i] % 2) + 2) % 2) == 1)
. However, the parity of a number is exclusive, so it's either even or odd. It'd be recommended to change your code to this instead
if (pixel[i] % 2 == 0) {
...
}
else {
...
}
Your code has another bug though. The line f += Integer.toBinaryString(b[i]);
converts a character into a binary string, but if the value of the ascii character is less than 128, the conversion will use the minimum number of bits. For example, a = '1100001'
, which is only 7 bits. You want to pad with 0s to the left to get 8 bits. A quick search yields this and the above line should change to
f += String.format("%8s", Integer.toBinaryString(b[i])).replace(' ', '0');
If I may recommend some optional improvements to your code, I would do it as follows
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);
}
Notes:
Arrays.copyOf()
is for the sake of keeping a copy of the original image to compare the differences. Normally, I'd just straight edit pixel
on the spot.
You don't need to convert the bytes to a string of 1s and 0s, since you'll need those numbers as integers later on. The following loop extracts the bits one by one from the most significant bit (leftmost) to the least using the right bitshift and the bitwise and operations.
for (int j = 7; j >=0; j--) {
int lsb = (b[i] >> j) & 1;
}
Instead of checking for the value of the lsb of a pixel, you can zero it out and then add the lsb from the above loop. You can use a bitwise and operation to achieve this. A pixel is made of four bytes, with a max value of 255 (0xff) for each. The bytes correspond to the alpha transparency, red, green and blue channels (known as ARGB). You can read about it here.
newpixel[count] = (pixel[count] & 0xfffffffe) + lsb;
The extraction process is the opposite to the embedding. But here lies a trick. The program doesn't know how many pixels to read before extracting the whole message. What you want to do is introduce a length variable which is equal to 8 * b.length
. You can allocate the first 16 pixels to hide this number in 1s and 0s just like your characters. The extraction then reads this first 16 pixels, calculates how many pixels to read and does so starting from the 17th pixel.
来源:https://stackoverflow.com/questions/26610638/changing-lsb-value-of-image-rgb-value-giving-inconsistent-value