【问题标题】:how to end the nested loop如何结束嵌套循环
【发布时间】:2015-06-30 23:29:24
【问题描述】:

我有这段代码,尝试使用最低有效位方法对图像中的消息进行编码。我想不通的是,当没有消息要编码时,如何结束消息的编码。

它一直持续到循环结束。

我尝试过像限制这样的计数器。与我在解码部分所做的相同,但无济于事,这就是我将其删除的原因。

我包含这些方法是因为它们已被使用并可用于包含计数器,以识别消息是否已完成编码并可以停止迭代

编码和解码在构造函数中。

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 工作。我尝试应用您指出的解码线,但我无法获得图片中的编码数据。

这是原始数据比较

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);
    }

}

上面的代码是基于我原来的编码/解码算法工作的。不停止循环的那个。我尝试编辑代码并应用新的解码算法,但我无法让它工作。

【问题讨论】:

  • numLSB 究竟包含什么?你能告诉我们binaryToIntegerinsertMessageintegerToBinary的方法吗?就目前而言,numLSB 位于循环之外并且不会更新,因此仅包含消息的所有 1 和 0 才有意义。但我看不到你增加一个计数器来嵌入下一个二进制位的地方。
  • @Reti43 numLSB 包含一个 int 值,用于确定每个 rgb 值中要更改的位数。方法binaryToIntegerinsertMessage insertMessage 位于下方。我在 insertmessage 方法中增加了一个计数器。从 0 开始的 count++。
  • 您不应该编辑问题来彻底改变主题。而是创建一个新问题。这个问题涉及退出一些循环,而不是它对问题中未提及的其他一些代码的影响,直到问题循环中断问题得到解决。

标签: java for-loop


【解决方案1】:

编码变化

首先,对于编码部分,您需要更改以下行

binarizedMessage = stringToBinary(messageToBeHidden);

binarizedMessage = stringToBinary(messageToBeHidden) + "00000000";

这是因为在提取过程中,当你连续找到八个 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;
    }
}

每次您在insertMessage 方法中嵌入位时,变量count 都会递增。一旦它达到binarizedMessage.length() 的值,即使调用它也会停止嵌入。因此,您只需在每次访问 (i, j) 处的新像素时检查是否嵌入了所有位。

然而,嵌入可能以像素的红色结束,但调用insertMessage 表示绿色和蓝色,即使它不会嵌入任何新内容,它仍然会清除这些颜色的 lsb。所以,让我们修改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 的 1。

最后,您需要更改以下块

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();
}

由于您检查最后八位是否为 0 的方式,请考虑消息“hello world”。那是 11 个字符,所以 88 位 + 8 用于终止消息 = 96 位。然而,“d”的二进制是 01100100,所以一旦你提取了 94 位,你就会遇到八个 0 并破坏提取序列。如果我们将 94 与 8 进行整数除法,我们得到整数 11,这是我们消息的字符数。所以我们将前 88 位转换为字符,我们的工作就完成了。


这超出了答案的范围,但提供了一条建议。考虑使用bitwise operationsANDOR 清除、修改和提取位。它更优雅,因为您不必将整数转换为字符串然后再转换回整数,并且它会提供较小的性能改进,尽管在您的情况下并不明显。

【讨论】:

  • 明天试试这个。从来没有想过将这 8 个零直接添加到 binarizedMessage 中。会及时通知你。考试要来了,明天看复仇者联盟:)感谢您努力理解我的代码,同时解释这个答案:)
  • 现在我已经改变了我的解码和编码。我无法让我的 dataComparison 工作。我尝试应用您指出的解码行,但我没有得到完整的信息。只要消息之间有空格,它就会结束。
  • 一个空格的 ascii 代码为 32。消息结尾为 0,因此这不应成为提前终止的原因。我亲自为各种消息(有些长,有些有空格,有些有标点符号)和lsb 的各种值尝试了这段代码,它们都工作得很好。该算法有效。但是,它仅适用于 png 而不是 jpeg,因为它已在 different question 中指出。在应用我的修复建议之前,您能否让dataComparison 工作?
  • 是的。我让它工作了。只是比你慢。 dataComparison 在我将使用的编码是我以前的编码时工作。当我切换到你的。编码没有问题。只是未能在dataComparison 中解码,因为它是基于我的第一个编码。我尝试编辑编码以使其看起来像您的编码但没有成功。我希望你能再次启发我。我将编辑关于另一个问题的帖子,并添加我根据您的回答编辑的解码算法。希望您能看到问题所在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 2015-06-19
  • 1970-01-01
  • 2015-09-08
  • 2020-10-10
相关资源
最近更新 更多