您想更改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;
}