为了实现更小的文件大小,JPEG使用有损压缩方法。不幸的是,这种方法直接影响(一些)像素的值,从而破坏了嵌入信息的方式。为避免此问题,您需要将文件保存在无损格式中,例如BMP或PNG。
JPEG隐写术有些复杂,但概念很简单。您需要编写一个JPEG编码器,或使用已经存在的编码器。您提供的代码确实是一个编码器,稍作修改后即可用于您的项目。
如果您想理解代码,可以阅读维基百科上关于JPEG编码的文章。我将简要总结其中的一些关键步骤:
- 将图像拆分为8x8块。
- 对每个块使用离散余弦变换(DCT)获得浮点DCT系数,并将其量化为整数。
- 使用霍夫曼编码和游程编码将量化系数存储到文件中。
第二步中的量化是有损位,但之后的所有步骤都是无损的。因此,基本上是从第二步获取量化系数,使用隐写术算法修改它们并继续第三步。
现在来看看链接代码的实际修改。 Compress
方法是您需要调用的方法,以将RGB图像存储到文件中。它负责编写头数据和压缩系数。您只需要在 WriteCompressedData
方法中添加一些代码即可。现在做的事情是循环遍历每个8x8的图像块,应用DCT并量化系数,这些系数存储在 dctArray3
中。然后将此数据压缩并写入文件。这就是您需要介入的地方,即在调用 Huf.HuffmanBlockEncoder
之前修改 dctArray3
。
例如,假设您有一个字节数组作为您的秘密消息,称为message
,并且您想要在特定系数的最低位中每个8x8块嵌入一位。
public void WriteCompressedData(BufferedOutputStream outStream, byte[] message) {
byte currentByte;
int nBytes = message.length;
int iByte = 0;
int iBit = 7;
if (nBytes > 0) {
currentByte = message[0];
} else {
currentByte = (byte) 0;
}
dctArray3 = dct.quantizeBlock(dctArray2, JpegObj.QtableNumber[comp]);
if (iByte < nBytes) {
int bit = (currentByte >> iBit) & 1;
iBit--;
if (iBit == -1) {
iBit = 7;
iByte++;
if (iByte < nBytes) {
currentByte = message[iByte];
}
}
dctArray3[23] = (dctArray3[23] & 0xfffffffe) | bit;
}
Huf.HuffmanBlockEncoder(outStream, dctArray3, lastDCvalue[comp], JpegObj.DCtableNumber[comp], JpegObj.ACtableNumber[comp]);
...
}
解码是其反向过程,您需要使用适当的算法从DCT系数中读取并提取出您的秘密信息。为此,您需要一个jpeg解码器,因此我只是从F5隐写术项目中借用了相关文件。具体来说,您需要使用ortega
文件夹中的文件,然后可以像这样使用。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import ortega.HuffmanDecode;
public class Extract {
private static byte[] deZigZag = {
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31,
40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63 };
private static int[] extract(InputStream fis, int flength) throws IOException {
byte[] carrier = new byte[flength];
fis.read(carrier);
HuffmanDecode hd = new HuffmanDecode(carrier);
int[] coeff = hd.decode();
return coeff;
}
public static void main(String[] args) {
try {
File f = new File(args[0]);
FileInputStream fis = new FileInputStream(f);
int[] coeff = extract(fis, (int) f.length());
int idx = deZigZag[23];
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (iBit == -1)
而不是 0),并添加了一个解码器示例。 - Reti43