我有这个代码,尝试使用最低有效位方法在图像中进行编码消息。我无法弄清楚的是,当没有要编码的消息时,如何结束消息的编码。
它一直持续到循环结束。
我试过像限制这样的计数器。与我在解码部分所做的相同,但无法解释为什么我将其删除。
我包含了这些方法,因为它们被使用并且可用于包含计数器以识别消息是否已完成编码并且可以停止迭代
编码和解码在构造函数中。
public class Steganography {
public static SteganographyGUI gui;
public static String binarizedMessage = "", encodedMessage = "", decodedMessage = "";
public static int count = 0, limit = 0;
public static BufferedImage image;
public static int width, height, numLSB;
public Steganography() {
if(gui.isEncode()){
try {
String messageToBeHidden = gui.getMessage();
binarizedMessage = stringToBinary(messageToBeHidden);
File input = new File(gui.getPath());
image = ImageIO.read(input);
width = image.getWidth();
height = image.getHeight();
gui.appendStatus("File Name: " + gui.getFileName() + "\nWidth: " + width + "\nHeight: " + height + "\nLSB: " + gui.getSelectedLSB());
numLSB = gui.getSelectedLSB();
//encoding
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
int red = binaryToInteger(insertMessage(integerToBinary((int)(c.getRed())),numLSB));
int green = binaryToInteger(insertMessage(integerToBinary((int)(c.getGreen())),numLSB));
int blue = binaryToInteger(insertMessage(integerToBinary((int)(c.getBlue())),numLSB));
Color newColor = new Color(red,green,blue);
image.setRGB(j,i,newColor.getRGB());
}
}
gui.appendStatus("Binarized message is: " + binarizedMessage);
File output = new File(gui.getOutput()+"lossy.jpg");
ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(1f);
FileImageOutputStream outputStream = new FileImageOutputStream(output); //For example, FileImageOutputStream
jpgWriter.setOutput(outputStream);
IIOImage outputImage = new IIOImage(image, null, null);
jpgWriter.write(null, outputImage, jpgWriteParam);
jpgWriter.dispose();
File output2 = new File(gui.getOutput()+"lossless.jpg");
ImageIO.write(image, "png", output2);
gui.appendStatus("Message \""+ messageToBeHidden +"\" was encoded in "+ gui.getFileName() +".\nOutput files are: " + gui.getOutput() + "lossy.jpg \n\t"+ gui.getOutput() + "lossless.jpg");
} catch (Exception e) {}
}
else{
File input = new File(gui.getPath());
String encodedData = "";
try {
image = ImageIO.read(input);
} catch (IOException ex) {}
width = image.getWidth();
height = image.getHeight();
gui.appendStatus("File Name: " + gui.getFileName() + "\nWidth: " + width + "\nHeight: " + height + "\nLSB: " + gui.getSelectedLSB());
numLSB = gui.getSelectedLSB();
String eData = "";
//decoding
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
encodedData += getLSB(integerToBinary((int)(c.getRed())),numLSB);
encodedData += getLSB(integerToBinary((int)(c.getGreen())),numLSB);
encodedData += getLSB(integerToBinary((int)(c.getBlue())),numLSB);
if(limit >= 8 * numLSB){
break;
}
}
}
int counter = 0;
while(counter * 8 < encodedData.length()){
int index = counter * 8;
String str = encodedData.substring(index, index + 8);
eData += str;
if(!str.equals("00000000")){
encodedMessage += new Character((char)Integer.parseInt(str, 2)).toString();
counter++;
}
else{
eData = eData.substring(0,eData.length()-8);
break;
}
}
gui.appendStatus("Data decoded was: \"" + eData + "\".");
gui.appendStatus("Message decoded was: \"" + encodedMessage + "\".");
gui.appendStatus("Number of characters: " + counter);
}
reinitialize();
}
public static void reinitialize(){
binarizedMessage = encodedMessage = decodedMessage = "";
count = limit = width = height = numLSB = 0;
}
public static String stringToBinary(String s){
byte[] bytes = s.getBytes(); // http://stackoverflow.com/questions/917163/convert-a-string-like-testing123-to-binary-in-java
StringBuilder binary = new StringBuilder();
for (byte b : bytes) {
int val = b;
for (int i = 0; i < 8; i++){
binary.append((val & 128) == 0 ? 0 : 1);
val <<= 1;
}
}
return binary.toString();
}
public static String integerToBinary(int i){
return String.format("%8s", Integer.toBinaryString(i)).replace(' ', '0'); //http://stackoverflow.com/questions/21856626/java-integer-to-binary-string
}
public static int binaryToInteger(String s){
return Integer.parseInt(s, 2); //http://stackoverflow.com/questions/7437987/how-to-convert-binary-string-value-to-decimal
} //http://stackoverflow.com/questions/10178980/how-to-convert-a-binary-string-to-a-base-10-integer-in-java
public static String clearLSB(String s, int x){
StringBuilder result = new StringBuilder(s); //http://stackoverflow.com/questions/6952363/java-replace-a-character-at-a-specific-index-in-a-string
for (int i = 8 - x; i < 8; i++){
result.setCharAt(i, '0');
}
return result.toString(); //http://www.tutorialspoint.com/java/lang/stringbuilder_tostring.htm
}
public static String insertMessage(String s, int x){
String result = clearLSB(s, x);
StringBuilder temp = new StringBuilder(result);
for (int i = 8 - x; i < 8; i++){
if(count < binarizedMessage.length())
temp.setCharAt(i, binarizedMessage.charAt(count++));
}
return temp.toString();
}
public static String getLSB(String s, int x){
String result = "";
for (int i = 8 - x; i < 8; i++){
result += s.charAt(i);
if(s.charAt(i) == '0')
limit += 1;
else
limit = 0;
}
return result;
}
public static String binaryToText(String s){
StringBuilder sb = new StringBuilder();
count = 0;
while(count<s.length()){
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 8; i++){
if(count<s.length())
sb2.append(s.charAt(count++));
else
sb2.append('0');
break;
}
//binary to char and append to sb
sb.append((char)Integer.parseInt( sb.toString(), 2 ) );
}
return sb.toString();
}
public static void main(String[] args) {
gui = new SteganographyGUI();
gui.setVisible(true);
}
}
修改
似乎第一个问题已修复,但又造成了另一个问题。
现在我根据Reti43的答案改变了我的解码和编码,我无法使我的dataComparison工作。我尝试应用你指出的解码线,但我没有得到图片中的编码数据。
这是原始的DataComparison
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import javax.imageio.ImageIO;
public class DataComparison {
public static DataComparisonGUI gui;
public static BufferedImage image, image2;
public static int width, height, lsb, limit = 0;
public static String lossyData = "", losslessData = "", lsData = "", lyData = "";
public static String lossyMessage = "", losslessMessage = "";
public static int[][][] cLossyData, cLosslessData;
public DataComparison(){
lsb = gui.getLSB();
try{
File lossy = new File(gui.getLossyPath());
File lossless = new File(gui.getLosslessPath());
image = ImageIO.read(lossy);
image2 = ImageIO.read(lossless);
width = image.getWidth();
height = image.getHeight();
cLossyData = new int[height][width][3];
cLosslessData = new int[height][width][3];
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
Color c2 = new Color(image2.getRGB(j, i));
lossyData += getLSB(integerToBinary((int)(c.getRed())),lsb);
lossyData += getLSB(integerToBinary((int)(c.getGreen())),lsb);
lossyData += getLSB(integerToBinary((int)(c.getBlue())),lsb);
losslessData += getLSB(integerToBinary((int)(c2.getRed())),lsb);
losslessData += getLSB(integerToBinary((int)(c2.getGreen())),lsb);
losslessData += getLSB(integerToBinary((int)(c2.getBlue())),lsb);
cLossyData[i][j][0] = c.getRed();
cLossyData[i][j][1] = c.getGreen();
cLossyData[i][j][2] = c.getBlue();
cLosslessData[i][j][0] = c2.getRed();
cLosslessData[i][j][1] = c2.getGreen();
cLosslessData[i][j][2] = c2.getBlue();
if(limit >= 8 * lsb){
break;
}
}
}
int counter = 0;
while(counter * 8 < losslessData.length()){
int index = counter * 8;
String str = lossyData.substring(index, index + 8);
String str2 = losslessData.substring(index, index + 8);
lyData += str;
lsData += str2;
if(!str2.equals("00000000")){
lossyMessage += new Character((char)Integer.parseInt(str, 2)).toString();
losslessMessage += new Character((char)Integer.parseInt(str2, 2)).toString();
counter++;
}
else{
lyData = lyData.substring(0,lyData.length()-8);
lsData = lsData.substring(0,lsData.length()-8);
break;
}
}
int i = 0, lostBits = 0;
while(i < lyData.length()){
if(lyData.charAt(i) != lsData.charAt(i)){
lostBits++;
}
i++;
}
gui.appendStatus("Data decoded was (Lossless):\n\"" + lsData + "\".");
gui.appendStatus("Data decoded was (Lossy):\n\"" + lyData + "\".");
gui.appendStatus("Number of lsb: " + lsb);
gui.appendStatus("Number of bits (hidden message): " + counter * 8);
gui.appendStatus("Number of lost bits (hidden message): " + lostBits);
float z = ((lostBits*100)/(counter*8));
String percentage = String.format("%.04f", z);
gui.appendStatus("Percentage of lost bits (hidden message): " + percentage + "%");
gui.appendStatus("Message decoded was (Lossless): \"" + losslessMessage + "\".");
gui.appendStatus("Message decoded was (Lossy): \"" + lossyMessage + "\".");
gui.appendStatus("Number of characters: " + counter);
int counterR = 0, counterG = 0, counterB = 0;
for(int p = 0; p < height; p++){
for(int q = 0; q < width; q++){
if(cLosslessData[p][q][0] != cLossyData[p][q][0]){
counterR++;
}
else if(cLosslessData[p][q][1] != cLossyData[p][q][1]){
counterG++;
}
else if(cLosslessData[p][q][2] != cLossyData[p][q][2]){
counterB++;
}
}
}
gui.appendStatus("Total RGB values: " + width * height * 3);
gui.appendStatus("Altered Red values: " + counterR);
gui.appendStatus("Altered Green values: " + counterG);
gui.appendStatus("Altered Blue values: " + counterB);
gui.appendStatus("Total Altered values: " + (counterR + counterG + counterB));
z = ((counterR + counterG + counterB)*10000)/(width * height * 3);
percentage = String.format("%.02f", z/100);
gui.appendStatus("Percentage Altered values: " + percentage + "%");
reinitialize();
} catch (Exception e) {}
}
public static void reinitialize(){
losslessData = lossyData = lsData = lyData = losslessMessage = lossyMessage = "";
limit = width = height = lsb = 0;
Arrays.fill(cLossyData, 0);
Arrays.fill(cLosslessData, 0);
}
public static String integerToBinary(int i){
return String.format("%8s", Integer.toBinaryString(i)).replace(' ', '0'); //http://stackoverflow.com/questions/21856626/java-integer-to-binary-string
}
public static String getLSB(String s, int x){
String result = "";
for (int i = 8 - x; i < 8; i++){
result += s.charAt(i);
if(s.charAt(i) == '0')
limit += 1;
else
limit = 0;
}
return result;
}
public static void main(String[] args) {
// TODO code application logic here
gui = new DataComparisonGUI();
gui.setVisible(true);
}
}
上面的代码基于我原来的编码/解码算法。那个没有停止循环的人。我尝试编辑代码并应用新的解码算法,但我无法使其工作。
答案 0 :(得分:3)
首先,对于编码部分,您需要更改以下行
binarizedMessage = stringToBinary(messageToBeHidden);
到
binarizedMessage = stringToBinary(messageToBeHidden) + "00000000";
这是因为在提取中,当您连续找到8个0时,会到达消息的结尾。
现在,为了终止编码序列,您需要相应地修改循环。
encoding:
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
int red = binaryToInteger(insertMessage(integerToBinary((int)(c.getRed())),numLSB));
int green = binaryToInteger(insertMessage(integerToBinary((int)(c.getGreen())),numLSB));
int blue = binaryToInteger(insertMessage(integerToBinary((int)(c.getBlue())),numLSB));
Color newColor = new Color(red,green,blue);
image.setRGB(j,i,newColor.getRGB());
if (count == binarizedMessage.length()) break encoding;
}
}
每次在count
方法中嵌入一个位时,变量insertMessage
都会递增。一旦达到binarizedMessage.length()
的值,即使它被调用,它也会停止嵌入。因此,您只需要检查每次访问(i,j)处的新像素时是否嵌入了所有位。
然而,嵌入可以用像素的红色完成,但是调用insertMessage
绿色和蓝色,即使它不会嵌入任何新的,它仍然会清除这些颜色的lsbs。所以,让我们修改insertMessage
以防止这些不必要的修改。
public static String insertMessage(String s, int x){
String result;
if (count < binarizedMessage.length()) {
result = clearLSB(s, x);
} else {
result = s;
}
StringBuilder temp = new StringBuilder(result);
for (int i = 8 - x; i < 8; i++){
if(count < binarizedMessage.length())
temp.setCharAt(i, binarizedMessage.charAt(count++));
}
return temp.toString();
}
decoding:
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
encodedData += getLSB(integerToBinary((int)(c.getRed())),numLSB);
if(limit >= 8) break decoding;
encodedData += getLSB(integerToBinary((int)(c.getGreen())),numLSB);
if(limit >= 8) break decoding;
encodedData += getLSB(integerToBinary((int)(c.getBlue())),numLSB);
if(limit >= 8) break decoding;
}
}
每次从颜色中提取新位时,都使用limit
变量来检查(至少)最后8位是否为0,表示消息结束。嵌入每种颜色后,必须检查此条件,而不是在蓝色后。考虑一下在红色后找到消息结尾的情况,但是继续使用绿色和蓝色,你会遇到一个重置limit
的。
最后,您需要更改以下块
int counter = 0;
while(counter * 8 < encodedData.length()){
int index = counter * 8;
String str = encodedData.substring(index, index + 8);
eData += str;
if(!str.equals("00000000")){
encodedMessage += new Character((char)Integer.parseInt(str, 2)).toString();
counter++;
}
else{
eData = eData.substring(0,eData.length()-8);
break;
}
}
到
for (int index = 0; index < encodedData.length()/8; index++) {
String str = encodedData.substring(8*index, 8*index+8);
eData += str;
encodedMessage += new Character((char)Integer.parseInt(str, 2)).toString();
}
由于检查最后8位为0的方式,请考虑消息“hello world”。这是11个字符,因此88位+ 8用于终止消息= 96位。但是,“d”的二进制数是01100100,所以一旦你提取了94位,就会遇到8个0并打破提取序列。如果我们用8进行94的整数除法,我们得到整数11,这是我们的消息的字符数。所以我们将前88位转换为字符,我们的工作就完成了。
这不是答案的范围,而是一条建议。考虑使用bitwise operations AND
和OR
清除,修改和提取位。它更优雅,因为您不必将整数转换为字符串然后再转换为整数,它会提供较小的,但在您的情况下不会显着提高性能。