JPEG画像でステガノグラフィーを実行しています。DCT
係数を使用してデータを非表示にしています。次の手順を実行します。
- 入力画像を読み込む
BufferedImage
- から 8 x 8 ブロックを取得します。
BufferedImage
- 順方向 DCT を適用して DCT 係数を取得する
- メッセージ ビットを DCT 係数に埋め込む
- 逆 DCT を適用し、ブロックを書き戻す
BufferedImage
メッセージ ビットがなくなるまで手順 2 ~ 5 を繰り返しBufferedImage
、output.jpg
. 画像全体に適用するDCT
のではなく、メッセージ ビットを埋め込むブロックにのみ適用することに注意してください。このソースをDCTに使用し、これをステガノグラフィに使用しています
問題:ouput.jpg
ピクセル データが劇的に変化することを示すランダムな線になってしまいます。以下は、イメージ書き込みのコードです。
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)));
}
}
}
入力と出力の例:
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 係数行列:
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
データを埋め込んだ後の量子化された DCT 係数:
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
データを埋め込んで IDCT を適用した後、ピクセルは Y 空間でブロックされます。
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
JPEGの非可逆性が原因だと思ったので、PNGを試しましたが、それでも同じ問題が発生しました。