1

I am performing steganography on JPEG images. I am using DCT coefficients to hide data. I do the following steps:

  1. Read input image into BufferedImage
  2. Get an 8 x 8 block from BufferedImage
  3. Apply forward DCT to get DCT coefficients
  4. Embed message bits in DCT coefficients
  5. Apply inverse DCT and write block back to BufferedImage

Steps 2-5 are repeated until no message bits are remaining at which point I write BufferedImage to a JPEG file named output.jpg. It is worth mentioning that I do not apply DCT on the entire image but only on the block where I embed message bits. I am using this source for DCT and this for steganography

The Problem: I end up with random lines on ouput.jpg which signifies that pixel data changes dramatically. Following is code for image write:

static int[][] DCT_coefficients = new int[8][8];
static double[][] DCT_matrix = new double[8][8];
static int[][] Dequantized_m = new int[8][8];
static int[][] YBlock = new int[8][8];
static double[][] resultant = new double[8][8];
static int[][] red = new int[8][8];
static int[][] green = new int[8][8];
static int[][] blue = new int[8][8];
static int[][] Y = new int[8][8];
static int[][] Cb = new int[8][8];
static int[][] Cr = new int[8][8];
static int x = 0, y = 0;

public static void main(String args[]) {
  //I retrieve a pixel block from **BufferedImage** and convert it to YCbCr
  double temp1, temp2, temp3, temp4;
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      Color c = new Color(input_image.getRGB(y, x));
      red = c.getRed();
      green = c.getGreen();
      blue = c.getBlue();
      YCbCr = YCrCb_conversion(red, green, blue);
      YBlock[i][j] = YCbCr[0];
      Cb[i][j] = YCbCr[1];
      Cr[i][j] = YCbCr[2];
      y++;
    }
    x++;
  }
  //calculating DCT matrix
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      if (i == 0) {
        DCT_matrix[i][j] = 1 / Math.sqrt(8.0);
      }
      if (i > 0) {
        temp1 = 0.5;
        Apfloat Atemp1 = new Apfloat(temp1, 15);
        temp3 = ((2 * j) + 1) * i * java.lang.Math.PI;
        temp2 = temp3 / 16;
        Apfloat Atemp2 = new Apfloat(temp2, 15);
        temp4 = Math.cos(java.lang.Math.toRadians(temp2));
        Apfloat Atemp4 = ApfloatMath.cos(Atemp2);
        Apfloat Atemp = Atemp4.multiply(Atemp1);
        DCT_matrix[i][j] = Atemp.doubleValue();
      }
    }
  }
  //Leveling off Yblock
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      leveledoff[i][j] = pixel_block[i][j] - 128;
    }
  }
  // multiplying DCT matrix and leveled off YBlock
  resultant = matrix_multiply(DCT_matrix, leveledoff);
  // taking transpose of DCT matrix
  transpose = obj.transpose(DCT_matrix);
  //multiplying transpose with resultant to get DCT coefficient block
  DCT_Coefficients = matrix_multiply(resultant, trans);
  //quantizing DCT coefficients using 50 % quantization matrix
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      quantized_matrix[i][j] = (int) Math.round(DCT_Coefficients[i][j] / quantzation_matrix[i][j]);
    }
  }
  //Applying zigzag encoding
  ZigZag_encoded = zigzag.ZigzagEncode(quantised_m);
  //Encode data
  ZigZag_encoded = embed_data(ZigZag_encoded, data_buffer);
  //start IDCT
  IDCT(ZigZag_Encoded);
  //end main
}

//Method definitions
//Method to change LSB of integer value
public static int changeBit(int pixel, char bit) {
  String s = Integer.toBinaryString(pixel);
  char[] c = s.toCharArray();
  c[c.length - 1] = bit;
  // converting binary to integer
  String n = "";
  for (int y = 0; y < c.length; y++) {
    n += "" + c[y];
  }
  int j = 0;
  for (int i = 0; i < n.length(); i++) {
    if (n.charAt(i) == '1') {
      j = j + pow(2, n.length() - 1 - i);
    }
  }
  return j;
}

// method to embed data
public static int[] embed_data(int encoded[], char buffer[]) {
  for (int i = 0; i < buffer.length; i++) {
    int newVal = changeBit(encoded[i], buffer[i]);
    encoded[i] = newVal;
  }
  return encoded;
}

// Method to perform IDCT
public static void IDCT(ZigZag_encoded) {
  //converting 1D zigzag array to 2D array
  quantised_matrix = Zigzag_Decode(ZigZag_encoded);
  //Dequantizing 
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      Dequantized[i][j] = quantised_matrix[i][j] * quantzation_matrix[i][j];
    }
  }
  // multiplying transpose of DCT matrix with Dequantized matrix
  resultant = matrix_multiply(transpose, Dequantized);
  //multiplying resultant with DCT matrix
  resultant = matrix_multiply(resultant, dct_mat);
  //adding 128 to each resultant entry and rounding off
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      block[i][j] = (int) Math.round(resultant[i][j] + 128);
    }
  }
  //Converting Yblock to RGB block
  YCrCbToRGB(block, Cb, Cr);
  //writing RGB block to BufferedImage
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      newcolor = new Color(red[i][j], green[i][j], blue[i][j]);
      input_image.setRGB(y, x, newcolor.getRGB());
      y++;
    }
    x++;
  }
}

//method to convert RGB block to YCbCr
public int[] YCrCb_conversion(int red, int green, int blue) {
  int[] YCbCr = new int[3];
  //Y = 0.257R+ 0.504G + 0.098B + 16
  YCbCr[0] = (int)(0.257 * red + 0.504 * green + 0.098 * blue) + 16;
  //Cb=–0.148R – 0.291G+ 0.439B + 128
  YCbCr[1] = (int)((-0.148 * red - 0.291 * green + 0.439 * blue) + 128);
  //Cr = 0.439R – 0.368G – 0.071B + 128
  YCbCr[2] = (int)((0.539 * red - 0.368 * green - 0.071 * blue) + 128);
  return value
  // index 0 holds values of Y 1 holds Cb values and 2 holds Cr vlaues
  return YCbCr;
}

//Method to convert YCbCr block to RGB block
public static void YCrCbToRGB(int y[][], int Cb[][], int Cr[][]) {
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      //R = 1.164(Y – 16) + 1.596(Cr – 128)
      red[i][j] = (int)(1.164 * (y[i][j] - 16) + (1.596 * (Cr[i][j] - 128)));
      //G = 1.164(Y – 16) – 0.813(Cr – 128) – 0.391(Cb – 128)
      green[i][j] = (int)(1.164 * (y[i][j] - 16) - (0.813 * (Cr[i][j] - 128)) - (0.391 * (Cb[i][j] - 128)));
      //B = 1.164(Y – 16) + 2.018(Cb – 128)
      blue[i][j] = (int)(1.164 * (y[i][j] - 16) + (2.018 * (Cb[i][j] - 128)));
    }
  }
}

Sample input and output:

 pixel block in Y space as input to DCT
 223  224  226  227  229  229  230  230 
 226  227  227  227  228  229  229  230 
 228  228  228  228  228  229  230  231 
 228  228  228  228  229  231  233  234 
 227  228  229  230  231  233  234  234 
 226  227  229  230  232  233  233  233 
 227  228  230  232  232  231  229  228 
 228  230  232  234  232  229  226  223 

DCT coefficient matrix after quantization and before embedding data:

 51  -1  0  0  0  0  0  0 
 -1  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 

Quantized DCT coefficients after embedding data:

50  -1  0  1  0  0  0  0 
 -1  0  1  0  0  0  0  0 
 1  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0 

Pixels block in Y space after embedding data and applying IDCT:

 232  227  223  223  227  231  232  231 
 230  226  222  223  226  230  230  229 
 228  224  221  222  226  229  229  227 
 226  223  221  223  227  229  228  225 
 226  224  223  225  229  230  228  225 
 227  225  225  228  232  233  230  226 
 228  227  228  231  235  236  232  228 
 230  229  230  234  237  238  233  229 

input.jpg]

output.jpg]

I thought that it was due to lossy nature of JPEG so I tried PNG but still got the same problem.

TT.
  • 15,774
  • 6
  • 47
  • 88
saim2025
  • 280
  • 2
  • 5
  • 14
  • 1
    You've probably misunderstood what jpeg steganography is. It isn't DCT -> embed secret -> IDCT, but more along the lines of following the whole jpeg encoding process and modifying the coefficients in an intermediate step, such as [here](https://stackoverflow.com/questions/29677726/steganography-in-lossy-compression-java) and [here](https://stackoverflow.com/questions/35396977/lsb-dct-based-image-steganography). As for the dots in your output image, it's probably a result of how you apply steps 2-5, for which you haven't shown us the code. – Reti43 Dec 03 '16 at 15:46
  • I have not performed step 4 and 8 mentioned at [JPEG steganography](https://stackoverflow.com/questions/35396977/lsb-dct-based-image-steganography). I though huffman encoding was not required so i go straight to IDCT after embedding data after step 7. – saim2025 Dec 03 '16 at 17:14
  • I am also foggy on pixel value re centering – saim2025 Dec 03 '16 at 17:15
  • Code was long that is why i did not share. Would code snippets of each step work. – saim2025 Dec 03 '16 at 17:17
  • 1
    Show us the [minimal](http://sscce.org/) code required to replicate your problem. In your case, that should be starting with 64 hard-coded pixels, DCT, modifying some coefficients manually and IDCT. If you're basing your algorithm of a publication or something, share the source with us. – Reti43 Dec 03 '16 at 17:30
  • I have made changes you asked for – saim2025 Dec 04 '16 at 08:59
  • The code isn't complete. There are some syntax errors, some methods and objects which aren't defined and some inconsistent variable naming, e.g., `YBlock` and `pixel_block`. However, testing with other software, for the example pixel block you provided you get the correct DCT coefficients and vice versa. The colour transform seems fine as well. – Reti43 Dec 04 '16 at 13:31
  • As i said i skipped some method declarations. Original code is about 600 lines. I could share the entire program – saim2025 Dec 04 '16 at 18:16

0 Answers0