java - 请求的数组大小超出 VM 限制

标签 java embed rgb decode steganography

基于此的示例代码[已发布]( Storing message into R,G,B instead of Alpha )

这次我只想使用 RGB 而不是 ARGB,但是这次我收到的字节长度是 2147483647。下面是我更改的代码部分。

输入仅为 128 字节数组。

嵌入消息

private void openImage() {
    File f = new File("C:/TEMP/CROP.png");

        try {
            sourceImage = ImageIO.read(f); 


             sourceImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_RGB); 
             Graphics2D g = sourceImage.createGraphics(); 
             g.drawImage(ImageIO.read(f), 0, 0, null); 
             g.dispose();

            this.embedMessage();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

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);
         int 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);
             img.setRGB(i, j, rgb); 
             count = count+3;

        }
    }
}

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);
         int 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);
             img.setRGB(i, j, rgb);
             count = count+3;
        }
    }
}

解码消息

private void openImage() throws Exception {
    File f = new File("C:/TEMP/Four Area/Crop image/chest-CROP2.png");
    //File f = new File("C:/TEMP/chest2.png");

        try {
            image = ImageIO.read(f); 

            image = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); 
             Graphics2D g = image.createGraphics(); 
             g.drawImage(ImageIO.read(f), 0, 0, null); 
             g.dispose();

            this.decodeMessage();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


}


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);
         int 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);
             count = count+3;

        }
    }
    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);
           int 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);
             count = count+3;
        }
    }
    return b;
}

最佳答案

您的嵌入是错误的。在 RGBA 像素中嵌入一个字节很容易,因为每个像素可以方便地容纳半个字节。但对于 RGB,您可以容纳 3/8 个字节,这不是整数。下面演示了 RGB 的复杂程度(假设我们从像素 (0, 0) 开始):

// Byte 1
(0, 0, R) (0, 0, G) (0, 0, B)
(0, 1, R) (0, 1, G) (0, 1, B)
(0, 2, R) (0, 2, G)

// Byte 2
                    (0, 2, B)
(0, 3, R) (0, 3, G) (0, 3, B)
(0, 4, R) (0, 4, G) (0, 4, B)
(0, 5, R)

// Byte 3
          (0, 5, G) (0, 5, B)
(0, 6, R) (0, 6, G) (0, 6, B)
(0, 7, R) (0, 7, G) (0, 7, B)

正如您所看到的,有时您需要嵌入 3 或 4 个不同的像素,并且并不总是从 R 组件开始/结束。实现此目的所需的代码如下。

编码消息

private void embedMessage(BufferedImage img, String mess) {
   int messageLength = mess.length();

   int imageWidth = img.getWidth(), imageHeight = img.getHeight(),
      imageSize = imageWidth * imageHeight;
   if((messageLength * 8 + 32)/3 > 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*8+32);
   }

private void embedInteger(BufferedImage img, int n, int start) {
   int mod = start%3;
   start = start/3;
   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 = 0, pp = 0;
         if(count <= 29) {
            for(pp=mod; pp<3; pp++) {
               bit = getBitValue(n, count); rgb = setBitValue(rgb, 8*pp, bit);
               count += 1;
               }
            mod = 0;
            }
         else {
            for(pp=0; pp<(33-count); pp++) {
               bit = getBitValue(n, count); rgb = setBitValue(rgb, 8*pp, bit);
               count += 1;
               }
            }
         img.setRGB(i, j, rgb);
         }
      }
   }

private void embedByte(BufferedImage img, byte b, int start) {
   int mod = start%3;
   start = start/3;
   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 = 0, pp = 0;
         if(count <= 5) {
            for(pp=mod; pp<3; pp++) {
               bit = getBitValue(b, count); rgb = setBitValue(rgb, 8*pp, bit);
               count += 1;
                  }
            mod = 0;
            }
         else {
            for(pp=0; pp<(9-count); pp++) {
               bit = getBitValue(b, count); rgb = setBitValue(rgb, 8*pp, bit);
               count += 1;
               }
            }        
         img.setRGB(i, j, rgb);
         }
      }
   }

解码消息

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*8+32);
   message.setText(new String(b));
   }

private int extractInteger(BufferedImage img, int start) {
   int mod = start%3;
   start = start/3;
   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 = 0, pp = 0;
         if(count <= 29) {
            for(pp=mod; pp<3; pp++) {
               bit = getBitValue(rgb, 8*pp); length = setBitValue(length, count, bit);
               count += 1;
               }
            mod = 0;
            }
         else {
            for(pp=0; pp<(33-count); pp++) {
               bit = getBitValue(rgb, 8*pp); length = setBitValue(length, count, bit);
               count += 1;
               }
            }
         }
      }
   return length;
   }

private byte extractByte(BufferedImage img, int start) {
   int mod = start%3;
   start = start/3;
   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 = 0, pp = 0;
         if(count <= 5) {
            for(pp=mod; pp<3; pp++) {
               bit = getBitValue(rgb, 8*pp); b = (byte)setBitValue(b, count, bit);
               count += 1;
               }
            mod = 0;
            }
         else {
            for(pp=0; pp<(9-count); pp++) {
               bit = getBitValue(rgb, 8*pp); b = (byte)setBitValue(b, count, bit);
               count += 1;
               }
            }
         }
      }
   return b;
   }

我会简单解释一下这背后的逻辑。由于编码和解码都很相似,所以我只描述前者。由于 embedInteger 和 embedByte 相似,我将仅描述 embedByte .

embedMessage ,我们需要在 embedByte 中传递 i*8+32因为我们需要到目前为止已经嵌入的位数。这是必要的,以便了解前一个字节的嵌入停止的位置(如上所示,在字节 1 之后我们必须从 B 开始,而在字节 2 之后从 G 开始)。这是通过取模运算 ( int mod = start%3 ) 实现的,它给出除以 3 的余数。例如,8%3 = 2。对于 mod = 0,我们从 R 开始,对于 mod = 1 从 G 开始,对于B 处 mod = 2。

start = start/3告诉你我们必须从哪个像素开始。整数除法为您提供向下舍入的整数结果,例如 8/3 = 向下舍入 2.666 = 2。如您所见,start 和 mod 为我们提供了必须从哪里开始的所有信息。例如,在一个字节之后,我们从像素 2(B 分量)开始。现在我们可以开始将字节嵌入 i 和 j 循环中。

在循环内,我们获取新的 RGB 像素。根据到目前为止我们嵌入了多少位,我们可以嵌入整个 RGB,或者只是它的一部分。对象count告诉我们到目前为止我们已经嵌入了多少位,总共嵌入了 8 位。这就是 if-else 语句的用武之地。实际上,我们提出的问题是“我们还有超过 3 位需要嵌入吗?”这被翻译为 8 计数 >= 3,当它以代数方式重新排列时,你会得到 count <= 5 。总结一下:

if: we have enough bits left to embed in all 3 components

else: we don't have enough bits left to embed in all 3 components

现在,pp决定我们嵌入位的颜色分量,它可以取值 0、1 和 2。java 语法是 for(pp=0; p<3; pp++) 。就是这样。然后8*pp可以是 0、8 或 16,这是 R、G 或 B 的 LSB。

在 if block 中,我们有 for(pp=mod; ...) ,因为我们可能不是从0开始。看上面的例子,其中字节2有mod=2,因为我们从蓝色分量开始。但是一旦循环结束,mod 将重置为 0,因此对于下一个像素,我们确实从 0 开始。

要理解 else block ( for(pp=0; pp<(9-count); p++) ),请查看上面字节 1 的示例。我们已经嵌入了前 2 个像素,因此还剩下两位。这意味着我们有 count=6。

pp=0;退出的条件是 pp<9-6 --> pp<3,所以我们继续。我们将第 7 位嵌入 R --> count=7。

pp=1;退出的条件是 pp<9-7 --> pp<2,所以我们继续。我们将第 8 位嵌入 G --> count=8。

pp=2;退出的条件是 pp<9-8 --> pp<1 --> 我们必须退出。

与该逻辑等效的更简单的方法如下,我不知道为什么我不这样做。

for(pp=0; pp<(8-count); pp++) {
   getBit...; setBit...;
   }
count = 8;

关于java - 请求的数组大小超出 VM 限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21356767/

相关文章:

java - 在 Play 2.4 中循环创建 InputRadioGroups

rgb - 是否有任何客户端技术能够通过 ICC 颜色配置文件将 sRGB 转换为 CMYK?

python - 如何测量独特 ROI 中的 RGB 均值?

image - HSI 和 YCbCr 之间的差异与肤色相关

java - 有人能弄清楚如何修复此代码吗?

java - SDK设置失败 : Error:Module 'app' : platform 'Google Inc.:Google APIs:21' not found

java - 实现 Java fm radio

java - 将Youtube视频嵌入Java Swing

html - 如何在 HTML 页面上显示 youtube 视频?

php - 如何实现嵌入对象的系统?