【问题标题】:Storing message into R,G,B instead of Alpha将消息存储到 R,G,B 而不是 Alpha
【发布时间】:2014-02-13 09:05:36
【问题描述】:

如何更改它以将消息存储到 R、G、B 的最低有效位。 下面的代码只将消息嵌入到 Alpha(0~7bit)中

embedInteger 处理在前 32 个像素中嵌入消息的长度。

embedByte 会一一嵌入您的消息字符。每次调用它时,它都会将消息中的下一个字符(字节形式)作为输入,b[i]。在那里,它每个像素嵌入一位,每个字节总共 8 位。

private void embedMessage(BufferedImage img, byte[] mess) {
    int messageLength = mess.length;
    int imageWidth = img.getWidth(), imageHeight = img.getHeight(),
            imageSize = imageWidth * imageHeight;

    if(messageLength * 8 + 32 > imageSize) {   
        System.out.println("Message is too logn");
        return;
    }
    embedInteger(img, messageLength, 0, 0);
    for(int i=0; i<mess.length; i++){
        embedByte(img, mess[i], i*8+32, 0);

    }
}

private void embedInteger(BufferedImage img, int n, int start, int storageBit) {
    int maxX = img.getWidth(), maxY = img.getHeight(), 
            startX = start/maxY, startY = start - startX*maxY, count=0;
    for(int i=startX; i<maxX && count<32; i++) {
        for(int j=startY; j<maxY && count<32; j++) {
            int rgb = img.getRGB(i, j), bit = getBitValue(n, count); 
            rgb = setBitValue(rgb, storageBit, bit); 
            img.setRGB(i, j, rgb); 
            count++;
        }
    }
}

private void embedByte(BufferedImage img, byte b, int start, int storageBit) {
    int maxX = img.getWidth(), maxY = img.getHeight(), 
            startX = start/maxY, startY = start - startX*maxY, count=0;
    for(int i=startX; i<maxX && count<8; i++) {
        for(int j=startY; j<maxY && count<8; j++) {
            int rgb = img.getRGB(i, j), bit = getBitValue(b, count); 
            rgb = setBitValue(rgb, storageBit, bit);
            img.setRGB(i, j, rgb);
            count++;
        }
    }
}

private int getBitValue(int n, int location) { //n=messageLength, location=count

    int v = n & (int) Math.round(Math.pow(2, location));
    return v==0?0:1;
}

private int setBitValue(int n, int location, int bit) { 
    int toggle = (int) Math.pow(2, location), bv = getBitValue(n, location); 
    if(bv == bit)

        return n;
    if(bv == 0 && bit == 1){
        n |= toggle;
        System.out.println("n{toggle: "+n);
    }else if(bv == 1 && bit == 0){
        n ^= toggle;
    }
    return n;

}

【问题讨论】:

    标签: java image embed decode argb


    【解决方案1】:

    您想更改embedMessage 方法中的以下几行。

    embedInteger(img, messageLength, 0, 0);
    embedByte(img, mess[i], i*8+32, 0);
    

    最后一个输入(在本例中为 0)确定您将嵌入位的 RGBA 像素值的位位置。下图取自您找到代码的网站,显示了像素值的位顺序。

    因此,对于 R 分量的 LSB,您需要 8,对于 G,16,对于 B 24。

    嵌入多个颜色分量

    很多文献都报道了 RGB 中的隐写术。 RGBA 非常相似,但具有额外的透明度信息。 Wikipedia 也是一个阅读它的好地方。实际上,区别在于 RGB 有 3 个分量,每像素总共 24 位,而 RGBA 有 4 个分量,每像素 32 位。通过嵌入多个组件,您可以将隐藏容量提高 3 或 4 倍。

    如果要将一个字节嵌入到 RGB 中,则需要 2 和 2/3 像素(3+3+2 分量)。但是对于 RGBA,您只需要两个像素(4+4 分量)。我将演示如何扩展您的代码以在 RGBA 中隐藏单个消息,因为在这种情况下它更简单。如上所述,这将使您的隐藏容量增加四倍。为了使这成为可能,整个代码都发生了很多变化,但它们可以归结为:

    • 放弃 storageBit,因为它不再需要了。
    • 您可以将每个字节嵌入两个像素中。在第一个像素中,您将前 4 位嵌入到第一个像素的 A、B、G 和 R 分量的 LSB 中,并将后 4 位嵌入到第二个像素的 LSB 分量中。

    要应用更改,只需从网站中提供的代码开始清理,并在编码和解码过程中完全替换以下方法。

    编码

    private void openImage() {
       java.io.File f = showFileDialog(true);
       try {   
          sourceImage = ImageIO.read(f);
          sourceImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
          Graphics2D g = sourceImage.createGraphics();
          g.drawImage(ImageIO.read(f), 0, 0, null);
          g.dispose();
          JLabel l = new JLabel(new ImageIcon(sourceImage));
          originalPane.getViewport().add(l);
          this.validate();
          } catch(Exception ex) { ex.printStackTrace(); }
       }
    
    private void embedMessage(BufferedImage img, String mess) {
       int messageLength = mess.length();
    
       int imageWidth = img.getWidth(), imageHeight = img.getHeight(),
          imageSize = imageWidth * imageHeight;
       if(messageLength * 2 + 8 > imageSize) {
          JOptionPane.showMessageDialog(this, "Message is too long for the chosen image",
             "Message too long!", JOptionPane.ERROR_MESSAGE);
          return;
          }
       embedInteger(img, messageLength, 0);
    
       byte b[] = mess.getBytes();
       for(int i=0; i<b.length; i++)
          embedByte(img, b[i], i*2+8);
       }
    
    private void embedInteger(BufferedImage img, int n, int start) {
       int maxX = img.getWidth(), maxY = img.getHeight(), 
          startX = start/maxY, startY = start - startX*maxY, count=0;
       for(int i=startX; i<maxX && count<32; i++) {
          for(int j=startY; j<maxY && count<32; j++) {
             int rgb = img.getRGB(i, j), bit = getBitValue(n, count);
             rgb = setBitValue(rgb, 0, bit);
             bit = getBitValue(n, count+1); rgb = setBitValue(rgb, 8, bit);
             bit = getBitValue(n, count+2); rgb = setBitValue(rgb, 16, bit);
             bit = getBitValue(n, count+3); rgb = setBitValue(rgb, 24, bit);
             img.setRGB(i, j, rgb); 
             count = count+4;
             }
          }
       }
    
    private void embedByte(BufferedImage img, byte b, int start) {
       int maxX = img.getWidth(), maxY = img.getHeight(), 
          startX = start/maxY, startY = start - startX*maxY, count=0;
       for(int i=startX; i<maxX && count<8; i++) {
          for(int j=startY; j<maxY && count<8; j++) {
             if(j==maxY-1){
                startY = 0;
                }
             int rgb = img.getRGB(i, j), bit = getBitValue(b, count);
             rgb = setBitValue(rgb, 0, bit);
             bit = getBitValue(b, count+1); rgb = setBitValue(rgb, 8, bit);
             bit = getBitValue(b, count+2); rgb = setBitValue(rgb, 16, bit);
             bit = getBitValue(b, count+3); rgb = setBitValue(rgb, 24, bit);
             img.setRGB(i, j, rgb);
             count = count+4;
             }
          }
       }
    

    解码

    private void openImage() {
       java.io.File f = showFileDialog(true);
       try {   
          image = ImageIO.read(f);
          image = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
          Graphics2D g = image.createGraphics();
          g.drawImage(ImageIO.read(f), 0, 0, null);
          g.dispose();
          JLabel l = new JLabel(new ImageIcon(image));
          imagePane.getViewport().add(l);
          this.validate();
          } catch(Exception ex) { ex.printStackTrace(); }
       }
    
    private void decodeMessage() {
       int len = extractInteger(image, 0);
       byte b[] = new byte[len];
       for(int i=0; i<len; i++)
          b[i] = extractByte(image, i*2+8);
       message.setText(new String(b));
       }
    
    private int extractInteger(BufferedImage img, int start) {
       int maxX = img.getWidth(), maxY = img.getHeight(), 
          startX = start/maxY, startY = start - startX*maxY, count=0;
       int length = 0;
       for(int i=startX; i<maxX && count<32; i++) {
          for(int j=startY; j<maxY && count<32; j++) {
             int rgb = img.getRGB(i, j), bit = getBitValue(rgb, 0);
             length = setBitValue(length, count, bit);
         bit = getBitValue(rgb, 8); length = setBitValue(length, count+1, bit);
             bit = getBitValue(rgb, 16); length = setBitValue(length, count+2, bit);
             bit = getBitValue(rgb, 24); length = setBitValue(length, count+3, bit);
             count = count+4;
             }
          }
       return length;
       }
    
    private byte extractByte(BufferedImage img, int start) {
       int maxX = img.getWidth(), maxY = img.getHeight(), 
          startX = start/maxY, startY = start - startX*maxY, count=0;
       byte b = 0;
       for(int i=startX; i<maxX && count<8; i++) {
          for(int j=startY; j<maxY && count<8; j++) {
             if(j==maxY-1){
                startY = 0;
                }
             int rgb = img.getRGB(i, j), bit = getBitValue(rgb, 0);
             b = (byte)setBitValue(b, count, bit);
             bit = getBitValue(rgb, 8); b = (byte)setBitValue(b, count+1, bit);
         bit = getBitValue(rgb, 16); b = (byte)setBitValue(b, count+2, bit);
         bit = getBitValue(rgb, 24); b = (byte)setBitValue(b, count+3, bit);
             count = count+4;
             }
          }
       return b;
       }
    

    在每个颜色组件中嵌入不同的秘密

    我已经修改了代码,所以这次您可以选择要在 GUI 中隐藏您的秘密的颜色组件。这实际上优于隐藏在所有 RGBA 中的上述版本。在这里,您可以使用多种颜色组件来隐藏您的信息,如果您的信息很长,您可以将其分成四个部分。为此,我对代码的各个部分进行了以下更改:

    • storageBit 的值在内部更改为 0、8、16 或 24,具体取决于您分别选择了 A、R、G 还是 B。
    • 此选择是在 GUI 上进行的,因此您不必每次都为不同的颜色组件重新编译代码。

    要应用更改,请从网站提供的代码开始,并完全替换以下方法,用于编码和解码过程。

    public class EmbedMessage extends JFrame implements ActionListener
    {
    JButton open = new JButton("Open"), embed = new JButton("Embed"),
       save = new JButton("Save into new file"), reset = new JButton("Reset");
    String[] rgbaList = { "B", "G", "R", "A" };
    JComboBox<String> chooseRGBA = new JComboBox<>(rgbaList);
    JTextArea message = new JTextArea(10,3);
    BufferedImage sourceImage = null, embeddedImage = null;
    JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
    JScrollPane originalPane = new JScrollPane(),
       embeddedPane = new JScrollPane();
    
    private void assembleInterface() {
       JPanel p = new JPanel(new FlowLayout());
       p.add(open);
       p.add(chooseRGBA);
       p.add(embed);
       p.add(save);   
       p.add(reset);
       this.getContentPane().add(p, BorderLayout.SOUTH);
       open.addActionListener(this);
       embed.addActionListener(this);
       save.addActionListener(this);   
       reset.addActionListener(this);
       open.setMnemonic('O');
       embed.setMnemonic('E');
       save.setMnemonic('S');
       reset.setMnemonic('R');
    
       p = new JPanel(new GridLayout(1,1));
       p.add(new JScrollPane(message));
       message.setFont(new Font("Arial",Font.BOLD,20));
       p.setBorder(BorderFactory.createTitledBorder("Message to be embedded"));
       this.getContentPane().add(p, BorderLayout.NORTH);
    
       sp.setLeftComponent(originalPane);
       sp.setRightComponent(embeddedPane);
       originalPane.setBorder(BorderFactory.createTitledBorder("Original Image"));
       embeddedPane.setBorder(BorderFactory.createTitledBorder("Steganographed Image"));
       this.getContentPane().add(sp, BorderLayout.CENTER);
       }
    
    public void actionPerformed(ActionEvent ae) {
       Object o = ae.getSource();
       if(o == open)
          openImage();
       else if(o == embed){
          int rgbaChoice = chooseRGBA.getSelectedIndex(), sb = 0;
          if(rgbaChoice == 0)
             sb = 24;
          else if(rgbaChoice == 1)
             sb = 16;
          else if(rgbaChoice == 2)
             sb = 8;
          else if(rgbaChoice == 3)
             sb = 0;
          embedMessage(sb);
          }
       else if(o == save) 
          saveImage();
       else if(o == reset) 
          resetInterface();
       }
    
    private void openImage() {
       java.io.File f = showFileDialog(true);
       try {   
          sourceImage = ImageIO.read(f);
          sourceImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
          Graphics2D g = sourceImage.createGraphics();
          g.drawImage(ImageIO.read(f), 0, 0, null);
          g.dispose();
          JLabel l = new JLabel(new ImageIcon(sourceImage));
          originalPane.getViewport().add(l);
          this.validate();
          } catch(Exception ex) { ex.printStackTrace(); }
       }
    
    private void embedMessage(int storageBit) {
       String mess = message.getText();
       embeddedImage = sourceImage.getSubimage(0,0,
          sourceImage.getWidth(),sourceImage.getHeight());
       embedMessage(embeddedImage, mess, storageBit);
       JLabel l = new JLabel(new ImageIcon(embeddedImage));
       embeddedPane.getViewport().add(l);
       this.validate();
       }
    
    private void embedMessage(BufferedImage img, String mess, int storageBit) {
       int messageLength = mess.length();
    
       int imageWidth = img.getWidth(), imageHeight = img.getHeight(),
          imageSize = imageWidth * imageHeight;
       if(messageLength * 8 + 32 > imageSize) {
          JOptionPane.showMessageDialog(this, "Message is too long for the chosen image",
             "Message too long!", JOptionPane.ERROR_MESSAGE);
          return;
          }
       embedInteger(img, messageLength, 0, storageBit);
    
       byte b[] = mess.getBytes();
       for(int i=0; i<b.length; i++)
          embedByte(img, b[i], i*8+32, storageBit);
       }
    

    解码

    public class DecodeMessage extends JFrame implements ActionListener
    {
    JButton open = new JButton("Open"), decode = new JButton("Decode"),
       reset = new JButton("Reset");
    String[] rgbaList = { "B", "G", "R", "A" };
    JComboBox<String> chooseRGBA = new JComboBox<>(rgbaList);
    JTextArea message = new JTextArea(10,3);
    BufferedImage image = null;
    JScrollPane imagePane = new JScrollPane();
    
    private void assembleInterface() {
       JPanel p = new JPanel(new FlowLayout());
       p.add(open);
       p.add(chooseRGBA);
       p.add(decode);
       p.add(reset);
       this.getContentPane().add(p, BorderLayout.NORTH);
       open.addActionListener(this);
       decode.addActionListener(this);
       reset.addActionListener(this);
       open.setMnemonic('O');
       decode.setMnemonic('D');
       reset.setMnemonic('R');
    
       p = new JPanel(new GridLayout(1,1));
       p.add(new JScrollPane(message));
       message.setFont(new Font("Arial",Font.BOLD,20));
       p.setBorder(BorderFactory.createTitledBorder("Decoded message"));
       message.setEditable(false);
       this.getContentPane().add(p, BorderLayout.SOUTH);
    
       imagePane.setBorder(BorderFactory.createTitledBorder("Steganographed Image"));
       this.getContentPane().add(imagePane, BorderLayout.CENTER);
       }
    
    public void actionPerformed(ActionEvent ae) {
       Object o = ae.getSource();
       if(o == open)
          openImage();
       else if(o == decode){
          int rgbaChoice = chooseRGBA.getSelectedIndex(), sb = 0;
          if(rgbaChoice == 0)
             sb = 24;
          else if(rgbaChoice == 1)
             sb = 16;
          else if(rgbaChoice == 2)
             sb = 8;
          else if(rgbaChoice == 3)
             sb = 0;
          decodeMessage(sb);
          }
       else if(o == reset) 
          resetInterface();
       }
    
    private void openImage() {
       java.io.File f = showFileDialog(true);
       try {   
          image = ImageIO.read(f);
          image = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
          Graphics2D g = image.createGraphics();
          g.drawImage(ImageIO.read(f), 0, 0, null);
          g.dispose();
          JLabel l = new JLabel(new ImageIcon(image));
          imagePane.getViewport().add(l);
          this.validate();
          } catch(Exception ex) { ex.printStackTrace(); }
       }
    
    private void decodeMessage(int storageBit) {
       int len = extractInteger(image, 0, storageBit);
       byte b[] = new byte[len];
       for(int i=0; i<len; i++)
          b[i] = extractByte(image, i*8+32, storageBit);
       message.setText(new String(b));
       }
    
    private byte extractByte(BufferedImage img, int start, int storageBit) {
       int maxX = img.getWidth(), maxY = img.getHeight(), 
          startX = start/maxY, startY = start - startX*maxY, count=0;
       byte b = 0;
       for(int i=startX; i<maxX && count<8; i++) {
          for(int j=startY; j<maxY && count<8; j++) {
             if(j==maxY-1){
                startY = 0;
                }
             int rgb = img.getRGB(i, j), bit = getBitValue(rgb, storageBit);
             b = (byte)setBitValue(b, count, bit);
             count++;
             }
          }
       return b;
       }
    

    【讨论】:

    • 我读了一些文章,他们说他们将消息嵌入到 R,G 和 B 的 LSB,但是如果只有一个组件嵌入消息就足够了,为什么需要将消息嵌入到 R, G,B,三个分量??但是,如果我想将三个消息嵌入到三个 R、G、B 组件中,我该如何在代码中准确地做到这一点,因为它只在位置上由 storeageBit 控制。
    • 我已经更新了答案,以解释为什么有时隐藏在多个组件中更可取。我还提供了有关如何在所有四个组件中隐藏消息以及如何在每个组件中隐藏不同消息的工作代码。
    • 您需要四个步骤。第一步,获取要嵌入的像素值。其次,获取要嵌入的位的值。第三,将该位嵌入到像素值中,这只是附加在rgb 对象中的一个整数值。第四,用rgb 的新值更新像素。关于set/getBitValues 的另一个问题已在另一个问题中向您解释过。
    • 您每个像素嵌入了 4 位,因此您需要像素来适应整个字节,并且需要 8 像素来适应 32 位消息长度信息。
    猜你喜欢
    • 2016-05-07
    • 2019-08-31
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 2016-03-23
    • 2015-11-01
    • 2021-02-03
    相关资源
    最近更新 更多