java - F5隐写术算法中的矩阵编码实现尚不清楚

标签 java steganography

我需要实现自己的F5算法。我已经阅读了F5文档,可以在here中找到该论文。

在本文的6.2矩阵编码一节中,公式16-18定义了变化密度D(k),嵌入率R(k)和效率率W(k),如下所示:

D(k) = 1 / (n + 1) = 1 / 2**k

R(k) = k / n = k / (2**k - 1)

W(k) = R(k) / D(k) = k * 2**k / (2**k - 1)


其中,n是代码字中可修改位置的数量,k是嵌入位数。 W(k)表示每次更改可嵌入的平均位数。

在源代码中,我们发现如下所述的位数。有人可以解释一下为什么usablechanged是用这种方式计算的吗?我根本不了解逻辑。

    int _changed = 0;
    int _expected = 0;
    int _one = 0;
    int _large = 0;
    int _zero = 0;
    for (i = 0; i < coeffCount; i++) {
        if (i % 64 == 0) {
            continue;
        }
        if (coeff[i] == 1) {
            _one++;
        }
        if (coeff[i] == -1) {
            _one++;
        }
        if (coeff[i] == 0) {
            _zero++;
        }
    }
    _large = coeffCount - _zero - _one - coeffCount / 64;
    _expected = _large + (int) (0.49 * _one);
for (i = 1; i < 8; i++) {
    int usable, changed, n;
    n = (1 << i) - 1;
    usable = _expected * i / n - _expected * i / n % n;
    changed = coeffCount - _zero - coeffCount / 64;
    changed = changed * i / n - changed * i / n % n;
    changed = n * changed / (n + 1) / i;
    //
    changed = _large - _large % (n + 1);
    changed = (changed + _one + _one / 2 - _one / (n + 1)) / (n + 1);
    usable /= 8;
    if (usable == 0) {
        break;
    }
    if (i == 1) {
        System.out.print("default");
    } else {
        System.out.print("(1, " + n + ", " + i + ")");
    }
    System.out.println(" code: " + usable + " bytes (efficiency: " + usable * 8 / changed + "." + usable * 8
            / changed % 10 + " bits per change)");
}


coeff是一个保存DCT系数的数组,coeffCount是DCT系数的数量,_large是可以编码的图像的理论比特数,预期是图像的预期容量(有收缩)。了解可用变量和更改变量背后的逻辑是什么

最佳答案

本文第6.2节的最后一段说了以下内容,我引用:


我们可以为每个要嵌入的消息和每个消息找到一个最佳参数k
载体介质提供足够的容量,因此消息恰好适合
载体介质。例如,如果我们要将1000位消息嵌入到
容量为50000位的载体介质,然后进行必要的嵌入
比率是R = 1000:50000 = 2%。该值介于R(k = 8)和R(k = 9)之间
表1.我们选择k = 8,并且能够嵌入50000:255 = 196个代码字
长度为n =255。(1、255、8)代码可以嵌入196·8 = 1568位。如果
我们选择了k = 9,因此无法完全嵌入该消息。


我相信这应该很简单。如果您能理解这一点,则可以按照以下步骤操作。

另一件事是整个代码中的表达式result = var - var % n;。这意味着您可以通过除去余数(取模运算)来使varn完全整除。现在进入循环块。

n = 1 << i - 1


这是本文定义的代码字长度。



usable = _expected * i / n - _expected * i / n % n;


要理解这一行,请记住i / n是嵌入率R(i)。简而言之,可用位数(_expected)乘以嵌入率(i / n)得出我们可以编码的位数。在引号中的示例中为50000/255 * 8 = 1568位。



changed = coeffCount - _zero - coeffCount / 64;
changed = changed * i / n - changed * i / n % n;
changed = n * changed / (n + 1) / i;


第一行说,我们可以通过的位数(称为总数)是系数(coeffCount)的数量,同时跳过了任何零和每个8x8块的DC分量(coeffCount / 64)。每个8x8块具有64个系数,但是DC系数只有一个,因此每64个系数中还有一个DC系数可以跳过。

第二行和第三行在一起。请注意,与第二行相比,您需要乘以嵌入率,然后使结果可以完全被代码字长整除。在第三行中,将您除以嵌入率,从而取消上一步,然后将其乘以变化密度1 / (n + 1),以求出平均要改变的位数。

之所以要经历整个过程,是因为除法和乘法的顺序很重要。举一个简单的例子,假设您有150个位和7个项目,您希望将其平均分配到尽可能多的位。您总共需要多少位?

7 * (150 / 7) = 7 * 21 = 147




注意:以下几行将覆盖当前计算的changed值。当我编写自己的_one_zerocoeffCount值时,前3行和后2行分别倾向于给出相似的答案。这两个版本之一可能是未删除的旧代码。无论如何,逻辑如下。

changed = _large - _large % (n + 1);
changed = (changed + _one + _one / 2 - _one / (n + 1)) / (n + 1);


第一行与变化密度D(i)有关,因为您使表达式可以被n + 1完全整除。由于_large的定义方式,因此与先前版本中的changed计算方式相似。

_large = coeffCount - _zero - _one - coeffCount / 64;


与此极为相似

changed = coeffCount - _zero - coeffCount / 64;


下一行对我来说有点朦胧,但这似乎可以实现。它重新引入在_one中减去的_large以及其中一半。这是由于收缩,因为它复制了_expected = _large + int(0.49*_ones)中的想法。我不太明白为什么要减去ones / (n + 1),但是将整个表达式乘以变化密度1 / (n + 1),就可以得到期望变化的位数。

结论

计算期望的更改位数的两种方法并不精确,这与事先不知道要更改多少位有关。对于给定的_zero_onecoeffCount值,它们似乎都给出相似的结果。实际上,这一切都不是必需的,因为它只是估算不同k的效率,如引文中所述。您只需要找到最大的k,您就可以使用尽可能多的载体介质来嵌入您的信息。这是通过只计算usable并在没有足够的位数来嵌入消息时立即中断循环来完成的。确切的事情在源代码中做了一些进一步的处理。

关于java - F5隐写术算法中的矩阵编码实现尚不清楚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23523224/

相关文章:

java - Effective Java - Item 25 - Generics class cast exception 混合列表和数组

java - 如何将数据传输到连接的蓝牙 LE 设备

java - Selenium Webdriver (Java) - 元素计数

java - Dynamodb 如何在项目中的列表元素中执行更新?

java - 将信息存储在音频中

linux - 使用binwalk提取所有文件

Java JTable - 透明与 Nimbus/System L&F

image - 如何将原始数据存储在图像文件中?

ffmpeg - LSB 隐写术是否会永久改变图像的像素?

image-processing - 在图像中嵌入分析代码以在显示图像时触发