将消息存储到R,G,B而不是Alpha

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

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

embedByte一个接一个地嵌入你的消息字符。 每次调用它时,它都以字节formsb [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; } 

您想要在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非常相似,但具有透明度的额外信息。 维基百科是一个很好的阅读的地方。 实际上,不同之处在于RGB有3个组件,每个像素总共24位,而RGBA有4个组件,每个像素32位。 通过嵌入多个组件,您可以将隐藏容量提高3或4倍。

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

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

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

编码

 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 

解码

 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 

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

我修改了代码,所以这次你可以选择在GUI中隐藏秘密的颜色组件。 这实际上优于隐藏在所有RGBA中的上述版本。 在这里,您具有隐藏消息的颜色组件的多function性,如果您有一个非常长的消息,您可以将它分成四个部分。 为此,我在代码的各个部分进行了以下更改:

  • 根据您是否分别选择了A,R,G或B,将storageBit的值内部更改为storageBit或24。
  • 此选择在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 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 

解码

 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 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