基于LSB-DCT的图像隐写术

3
我正在进行基于LSB-DCT的图像隐写术,在其中我需要将LSB应用于JPEG图像的DCT系数中以进行数据嵌入。我对此完全不熟悉,因此搜索并阅读了一些研究论文,但它们在DCT之后的过程中缺乏很多信息。我还在stackoverflow上阅读了许多问题和答案,但更加困惑了。
以下是问题:
1-研究论文和网络问题中都使用来自图像的8x8块大小进行DCT。如果图像的分辨率不能完全被8x8块整除,例如724 x 520,则我该怎么办?
520 / 8 = 65,但 724 / 8 = 90.5。
2-如果我有很多块和一些要隐藏的信息,假设可以适应5个块,我是否仍然需要对其余块进行DCT和IDCT?
3-我需要在DCT之后应用量化,然后应用LSB,还是可以直接应用LSB?

4-研究论文中没有提到不要触摸值为0和1以及第一个值的量化DCT系数..现在我应该使用它们还是不使用??为什么不使用??我知道关于0,因为它是高频成分,在JPEG压缩中被移除了..而我没有进行任何压缩..所以我可以使用它并且仍然产生相同的JPEG文件吗??

5-在量化中,我们将DCT系数除以量化矩阵并四舍五入值。反过来,我必须将量化矩阵与DCT系数相乘,只是..没有撤消四舍五入的方法吗??

关于DCT和IDCT的评论:

来自不同研究论文: enter image description here


请问您能否告诉我包含您提取的内容的论文名称?对于第一篇,我找到了A Mondal et. al (2015) - A Novel Approach of Image Based Steganography Using Pseudorandom Sequence Generator Function and DCT Coefficients,而对于第二篇则是D Singla et. al (2012) - Data Security Using LSB & DCT Steganography In Images。我可以在回答中详细介绍它们,但它们并不是关于将图像保存为jpeg格式的。 - Reti43
那么它们是关于什么的? - Sameer Azeem
1个回答

5

JPEG隐写术

如果您想将图像保存为JPEG格式,必须按照JPEG编码过程进行操作。不幸的是,我阅读的大多数文献都没有很好地解释这一过程。完整的过程如下(摘自维基百科摘要182页规格书):

  1. RGB转换为YCbCr(可选),
  2. 色度通道的子采样(可选),
  3. 8x8块分割,
  4. 像素值重新调整中心,
  5. DCT变换,
  6. 根据压缩比率/质量进行量化,
  7. 以Zigzag模式排序系数,以及
  8. 熵编码;最常用的是霍夫曼编码和游程编码(RLE)。

实际上还涉及到更多的细节,比如标题、部分标记、如何存储DC和AC系数的具体细节等。此外,标准只定义了一些方面,其实现在编解码器之间可能会有所不同,例如子采样算法、量化表和熵编码。尽管如此,大多数软件都遵守通用的JFIF标准,并且可以被各种软件读取。如果你想让你的JPEG文件也能这样做,就需要准备好写数百(甚至一千)行代码来编写编码器。最好借鉴已经在互联网上发布的编码器,而不是自己编写。你可以从libjpeg开始,它是用C语言编写的,并成为许多其他JPEG编解码器的基础,或者使用其C#实现,甚至是受其启发的Java版本。

在一些伪代码中,编码/解码过程可以描述如下。
function saveToJpeg(pixels, fileout) {
    // pixels is a 2D or 3D array containing your raw pixel values
    // blocks is a list of 2D arrays of size 8x8 each, containing pixel values
    blocks = splitBlocks(pixels);
    // a list similar to blocks, but for the DCT coefficients
    coeffs = dct(blocks);
    saveCoefficients(coeffs, fileout);
}

function loadJpeg(filein) {
    coeffs = readCoefficients(filein);
    blocks = idct(coeffs);
    pixels = combineBlocks(blocks);
    return pixels;
}

对于隐写术,您需要将其修改如下。
function embedSecretToJpeg(pixels, secret, fileout) {
    blocks = splitBlocks(pixels);
    coeffs = dct(blocks);
    modified_coeffs = embedSecret(coeffs, secret);
    saveCoefficients(modified_coeffs, fileout);
}

function extractSecretFromJpeg(filein) {
    coeffs = readCoefficients(filein);
    secret = extractSecret(coeffs);
    return secret;
}

如果您的封面图片已经是jpeg格式,就不需要使用解码器将其转换为像素,然后再传递给编码器嵌入消息。您可以采用以下方法。
function embedSecretToJpeg(pixels, secret, filein, fileout) {
    coeffs = readCoefficients(filein);
    modified_coeffs = embedSecret(coeffs, secret);
    saveCoefficients(modified_coeffs, fileout);
}

就您提出的问题而言,编码器/解码器应该负责处理1、2、3和5,除非您自己写一个。

问题1:通常,您需要使用必要数量的行/列填充图像,以使宽度和高度都可被8整除。在内部,编码器将跟踪填充的行/列,以便解码器在重建后将其丢弃。这些虚拟行/列的像素值选择取决于您,但不建议使用恒定值,因为会导致振铃现象,这与正弦函数是方波傅里叶变换的事实有关。

问题2:虽然您只修改了几个块,但编码过程需要将它们全部转换,以便存储到文件中。

问题3:必须对浮点DCT系数进行量化,因为这是无损存储在文件中的内容。在量化步骤之后,您可以随心所欲地修改它们。

问题4: 没有人会阻止你修改任何系数,但你必须记住每个系数都会影响块中的所有64个像素。直流系数和低频AC系数引入了最大的失真,因此你可能要远离它们。更具体地说,由于直流系数的存储方式,修改一个系数会将失真传播到所有后续块。
由于大多数高频系数为0,它们可以有效地通过RLE压缩。修改一个0系数可能会将其翻转为1(如果你正在进行基本LSB替换),这会破坏这种有效的压缩。
最后,一些算法将它们的秘密存储在任何非零系数中,并跳过任何0。然而,如果你试图修改一个1,它可能会翻转为0,在提取过程中,你会盲目地跳过读取它。因此,这种算法不会接近任何值为1或0的系数。

问题5:在解码中,您只需将系数与相应的量化表值相乘。例如,DC系数为309.443,量化值为 round(309.443 / 16) = 19。舍入位是损失的部分,在这里不允许您重建309.433。因此,反向操作就是 19 * 16 = 304

DCT在隐写术中的其他用途

频率变换,如DCT和DWT,可用于在频域中嵌入秘密消息,但不一定要将隐写图像存储为jpeg格式。这个过程是像素 -> DCT -> 系数 -> 修改系数 -> IDCT -> 像素,这就是您发送给接收者的内容。因此,格式的选择在这里很重要。如果您决定将像素保存为jpeg格式,则DCT系数中的秘密消息可能会被jpeg编码的另一层量化所干扰


我能否直接对JPEG进行LSB操作,而不需要先进行DCT变换吗? - Sameer Azeem
我正在使用C#实现它,有一个位图对象save()方法,可以像这样做一些事情 coverimage.Save("D:\steg.jpeg", ImageFormat.Jpeg); 那么我是否仍然需要为JPEG执行所有过程?如果可能的话,我应该怎么做?例如,获取DCT嵌入数据,获取IDCT并将其存储回位图对象并使用保存函数? - Sameer Azeem
在问题的末尾添加了一张图片,它来自两篇研究论文。其他人也会说类似的话,即采用DCT嵌入数据,然后应用IDCT,就可以创建隐写图像。这就是为什么我很困惑如何自己完成JPEG构建过程以及哪些系数要触及LSB。 - Sameer Azeem
@SameerAzeem 我已更新我的答案以涵盖所讨论的两篇论文。请查看最后一节以获得更清晰的解释。我不知道任何在C#中可以完全满足你要求的编码器。但是,我已用伪代码描述了如何修改现有的编码器/解码器来适应您的需求。 - Reti43
我只想在DCT上执行LSB并保存JPEG。所以打算使用编码器/解码器。关于论文,是将其写入DCT然后应用LSB,然后再次进行IDCT吗?难道IDCT不会破坏信息吗? - Sameer Azeem
一旦系数被量化,block = round(idct2(coeffs0 * qTable)); coeffs1 = round(dct2(block) / qTable) 应该导致 coeffs0 == coeffs1。因此,IDCT 不应破坏消息。然而,你自己做量化和编码器做量化可能不是同一件事。如果你对 DCT(RGB) 进行操作,而编码器对 RGB -> YCbCr -> DCT(YCbCr) 进行操作或使用更高的压缩率,则你的秘密可能无法保存。 - Reti43

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接