DCT隐写术

DCT Steganography

我正在尝试完成 DCT 隐写术项目,但出现了一些有关它的问题。有人可以帮助我吗?我做了什么:

1) 将图像分割成 8x8 像素

public BufferedImage[] getBlocksOfImage(BufferedImage image){
    int width = image.getWidth();
    int height = image.getHeight();
    int arrayIndex = 0;
    //TODO specify dynamic length
    //TODO for 512x512 --> 4096 (8x8 block)
    BufferedImage[] blocksOfImage = new BufferedImage[4096];
    for (int i = 0; i<width; i=i+8) {
        for (int j = 0; j<height; j=j+8) {
            blocksOfImage[arrayIndex] = image.getSubimage(i,j,8,8);
            arrayIndex++;
        }
    }
    return blocksOfImage;
}

2) 使用以下方法计算每个块的矩阵:

//get matrix of pixels (used in DCT]
public int[][] getMatrixPixels(BufferedImage image){
    int[][] matrixPixels = new int[image.getWidth()][image.getHeight()];
    for (int i = 0; i < matrixPixels[0].length; i++) {
        for (int j = 0; j < matrixPixels.length; j++) {
            matrixPixels[i][j] = image.getRGB(i, j);
        }
    }
    return matrixPixels;
}

3) 从块中,我使用以下方法计算 DCT 矩阵:

public double[][] getDTCTransformMatrix(int[][] imageMatrix){

    double[][] dctTransformMatrix = new double[m][n];
    double ci, cj, tmpDCTValue, tmpSum;
    tmpSum = 0;

    //TODO simplify 4x for --> BAD
    //TODO consulting
    for(int i = 0; i<m; i++) {
        for(int j = 0; j<n; j++) {

            if (i == 0){
                ci = 1 / Math.sqrt(m);
            } else {
                ci = Math.sqrt(2) / Math.sqrt(m);
            }
            if (j == 0){
                cj = 1 / Math.sqrt(n);
            } else {
                cj = Math.sqrt(2) / Math.sqrt(m);
            }

            for (int i_image = 0; i_image < m; i_image++) {
                for (int j_image = 0; j_image < n; j_image++) {

                    tmpDCTValue = imageMatrix[i_image][j_image] *
                            Math.cos((2 * i_image + 1) * i * Math.PI / (2 * m)) *
                            Math.cos((2 * j_image + 1) * j * Math.PI / (2 * n));
                    tmpSum += tmpDCTValue;
                }
            }
            dctTransformMatrix[i][j] = ci * cj * tmpSum;
        }
    }
    return dctTransformMatrix;
}

这是我的问题:

1) 我应该将什么传递给 getDTCTransformMatrix() 函数?现在我正在传递像素的整数值,我认为这是错误的。我见过一些例子,其中有人传递 0-255 的值,所以我应该将图像转换为灰度吗?或者我应该为每种颜色 (R,G,B) 做吗?

2) 执行 getDTCTransformMatrix() 后,我得到双精度矩阵。如何编辑 double 值的 LSB?这是正确的方法吗?

3) 在我将 LSB 更改为 double 值之后,接下来我应该做什么?如何确保信息将存储在图像中。

谢谢大家的回答:)

--- 编辑 ---

我已经编辑了代码,现在我分别传递每个通道(R、G、B)并添加了以下函数:

public int[][] getQuantiseCoefficients(double[][] DTCTransformMatrix) {
    int[][] quantiseResult = new int[DTCTransformMatrix.length][DTCTransformMatrix[1].length];
    int[][] quantiseMatrix =   {{16, 11, 10, 16, 24, 40, 51, 61},
                                {12, 12, 14, 19, 26, 58, 60, 55},
                                {14, 13, 16, 24, 40, 57, 69, 56},
                                {14, 17, 22, 29, 51, 87, 80, 62},
                                {18, 22, 37, 56, 68, 109, 103, 77},
                                {24, 35, 55, 64, 81, 104, 113, 92},
                                {49, 64, 78, 87, 103, 121, 120, 101},
                                {72, 92, 95, 98, 112, 100, 103, 99}};

    //TODO delete static 8
    for (int i = 0; i<8; i++) {
        for (int j = 0; j<8; j++) {
            //Bij = round(Gij/Qij)
            quantiseResult[i][j] = (int)Math.round(DTCTransformMatrix[i][j]/quantiseMatrix[i][j]);
        }
    }
    return quantiseResult;
}

并添加消息:

public ArrayList<int[][]> addMessageFirst(ArrayList<int[][]> quantiseMatrixRGB, String message) {
    Utils utils = new Utils();
    int bitTextShift = 7;
    int byteTextShift = 0;
    byte[] textByteArray = text.getBytesFromText(message);

    for (int i = 0; i < textByteArray.length*8; i++) {
        byte[] firstValueByte = utils.integerToByte(quantiseMatrixRGB.get(i)[0][0]);

        firstValueByte[3] = (byte) ((firstValueByte[3] & 0xFE) | ((int) textByteArray[byteTextShift] >>> bitTextShift) & 1);
        if (bitTextShift <= 0) {
            bitTextShift = 7;
            byteTextShift++;
        } else {
            bitTextShift--;
        }
    }
    return quantiseMatrixRGB;
}

一切似乎都很好,但现在我正在努力将图像重新组合在一起。我完成的代码如下,但我认为这非常困难。 有没有更简单的方法?

例如使用一些已经创建的 JPEG 编码器?

public int[][] revertGetQuantiseCoefficients (int[][] quantiseMatrixRGB) {
    int[][] quantiseMatrix =   {{16, 11, 10, 16, 24, 40, 51, 61},
                                {12, 12, 14, 19, 26, 58, 60, 55},
                                {14, 13, 16, 24, 40, 57, 69, 56},
                                {14, 17, 22, 29, 51, 87, 80, 62},
                                {18, 22, 37, 56, 68, 109, 103, 77},
                                {24, 35, 55, 64, 81, 104, 113, 92},
                                {49, 64, 78, 87, 103, 121, 120, 101},
                                {72, 92, 95, 98, 112, 100, 103, 99}};
    for (int j = 0; j<8; j++) {
        for (int k = 0; k<8; k++) {
            quantiseMatrixRGB[j][k]=quantiseMatrixRGB[j][k]*quantiseMatrix[j][k];
        }
    }
    return quantiseMatrixRGB;
}

public int[][] revertGetDTCTransformMatrix(int[][] quantiseMatrixRGB) {
    double[][] dctTransformMatrix = new double[m][n];
    double ck, cl, tmpDCTValue, tmpSum;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                tmpSum = 0;
                for (int i_image = 0; i_image < m; i_image++) {
                    for (int j_image = 0; j_image < n; j_image++) {

                        if (i_image == 0) {
                            ck = 1 / Math.sqrt(m);
                        } else {
                            ck = Math.sqrt(2) / Math.sqrt(m);
                        }
                        if (j_image == 0) {
                            cl = 1 / Math.sqrt(n);
                        } else {
                            cl = Math.sqrt(2) / Math.sqrt(n);
                        }

                        tmpDCTValue = quantiseMatrixRGB[i_image][j_image] *
                                Math.cos((2 * i_image + 1) * i * pi / (2 * m)) *
                                Math.cos((2 * j_image + 1) * j * pi / (2 * n));
                        tmpSum = tmpSum + ck*cl*tmpDCTValue;
                    }
                }
                dctTransformMatrix[i][j] =  tmpSum;
            }
        }
    return quantiseMatrixRGB;
}

public int[][] getMergeRGB (int[][] r, int[][] g, int[][] b) {
    int[][] mergeRGB = new int[r.length][r[1].length];
    for (int i = 0; i<8; i++) {
        for(int j = 0; j<8; j++) {
            mergeRGB[i][j] = r[i][j];
            mergeRGB[i][j] = (mergeRGB[i][j]<<8) + g[i][j];
            mergeRGB[i][j] = (mergeRGB[i][j]<<8) + b[i][j];
        }
    }
    return mergeRGB;
}

public BufferedImage getPixelBlock (int[][] mergeRGB) {
    BufferedImage bi = new BufferedImage( 8, 8, BufferedImage.TYPE_INT_RGB );
    final int[] a = ( (DataBufferInt) bi.getRaster().getDataBuffer() ).getData();
    System.arraycopy(mergeRGB, 0, a, 0, mergeRGB.length);
    return bi;
}

public BufferedImage joinImages (BufferedImage subImage) {


    return null;
}
}

我正在努力将 BufferedImage 子图像重新组合在一起。有什么想法吗?

非常感谢您抽出宝贵时间。

您似乎在尝试进行 JPEG 隐写术。编写 jpeg 编码器的标准非常复杂,因此更容易借用一个已经编写好的编码器并进行任何小的修改以将您的隐藏算法注入其中。

我有 一个类似的问题,我在其中简要总结了算法的要点并在 java 中展示了一个示例。

JPEG encoding 上的维基百科是了解该过程的良好开端。这应该可以回答您提出的所有问题,但我也会在这里解决它们。

What should I pass into getDTCTransformMatrix() function? Right now I am passing integer value of the pixel, which is wrong I think. I have seen some examples where guys were passing value from 0-255 so should I convert image into gray-scale? Or should I do it for each color (R,G,B)?

是的,您分别对每个颜色平面进行传递整数值。这可以是 RGB 或 YCrCb。它也可以从 0-255,或者以 0 为中心,即 -127,128。首选 YCrCb 的原因是因为一些通道可以压缩得更多,而不会因我们的眼睛工作方式而造成任何明显的质量损失。将数字的范围移动到以 0 为中心意味着生成的 DCT 系数将具有更小的值并且将需要更少的位来存储。

After getDTCTransformMatrix() is performed I get matrix of double. How can I edit LSB in double value? And is this a correct approach?

这是JPEG编码的要点。您应该使用特定的量化矩阵量化系数(将它们转换为整数)。虽然有默认的,但各种程序都选择使用自定义的。这个想法是低频系数不会受到太大影响,而大部分高频系数可能会变为 0,这有助于最终文件大小变小。这是一个有损过程,您应该在量化系数后嵌入您的信息,因为其余步骤都是无损的。

After I will change an LSB in double value what should I do next? How to make sure that information will be stored in the image.

您在 1D 中以之字形排列系数,以便低频系数排在第一位。然后结合使用 运行-length 和 Huffman 编码将该信息存储在文件中。文件的二进制数据与原始像素(显然)或 DCT 系数的值没有任何相似之处。它是系数的压缩数据。