Loading [MathJax]/extensions/TeX/AMSsymbols.js

2016年12月24日 星期六

[實作]Hiding data in images by simple LSB substitution

這篇是簡易版的,直接照老師上課講的方法實作,和論文實作應該有很大的出入
論文實作篇在這邊:https://iris123321.blogspot.com/2017/05/finding-optimal-least-significant-bit.html



Data hiding in image是指將secret image藏於cover image底下,就是將秘密藏於一張圖片底下,最貼近生活的例子如恐怖組織在傳遞訊息時,把機密藏在一張普通的照片底下,這樣就算被攔截也不會被發現,這時候,許多人就開始在想,那可以將一張圖片藏於一張圖片底下嗎?其實如果允許圖片失真的情況下,真的可以。

在電腦中,每一個像素(pixel)都是由RGB(red,green,blue)所組成,各占8bits,讓我們來看看如果拋棄幾個8bit中的後面幾個bit會發生什麼事




上面那張圖就可以發現,如果將R後四個bit給刪除,其實沒仔細看是看不太出來的,而刪除後面比較不重要的bit的方法稱為simple LSB (Least Significant Bit,最低有效位元),利用人眼對於色彩的敏感度:Green>Red>Blue,來實作data hiding,做法如下圖






要將Secret image藏於cover image中,得到stego image:



  • secret_R的前4個bits取代cover_R後4個bits得到stego_R,
  • secret_G的前5個bits取代cover_G後5個bits得到stego_G,
  • secret_R的前3個bits取代cover_B後3個bits得到stego_B,



解碼的部分就是將stego_R取後4 bits,再用0把8 bits填滿即可


來看一下demo的結果

測試圖片原圖來自google search


左上(cover image)、右上(secret image)、左下(stego image)、右下(decode image)

失真率還是蠻明顯的,但以data hiding來說已經算是成功了,接下來就是程式碼的部分,首先先準備兩張512x512 pixels的RAW檔圖:cover.raw 和 secret.raw作為測資,讀入後會輸出stego.raw


#include <iostream>
#include <fstream>
#include <cstdlib>
#include <stack>
#define size 512
using namespace std;
//=============input images=============//
unsigned char image_buf_R[size][size]; int image_R[size][size];
unsigned char image_buf_G[size][size]; int image_G[size][size];
unsigned char image_buf_B[size][size]; int image_B[size][size];
//=============input images2=============//
unsigned char image2_buf_R[size][size]; int image2_R[size][size];
unsigned char image2_buf_G[size][size]; int image2_G[size][size];
unsigned char image2_buf_B[size][size]; int image2_B[size][size];
//=============output1 random result======//
unsigned char result_buf_R[size][size]; int result_R[size][size];
unsigned char result_buf_G[size][size]; int result_G[size][size];
unsigned char result_buf_B[size][size]; int result_B[size][size];
//=============Input====================//
FILE *Fimage;
FILE *Fimage2;
//=============Output====================//
FILE *Fresult;
int main(void)
{
int n,i,j;
//Load Cover Image
cout <<"Load image.\n";
Fimage=fopen("cover.raw","rb");
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fread(&image_buf_R[i][j],1,1,Fimage);
fread(&image_buf_G[i][j],1,1,Fimage);
fread(&image_buf_B[i][j],1,1,Fimage);
image_R[i][j] = (int)image_buf_R[i][j];
image_G[i][j] = (int)image_buf_G[i][j];
image_B[i][j] = (int)image_buf_B[i][j];
}
}
fclose(Fimage);
//Load Secret Image
Fimage2=fopen("secret.raw","rb");
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fread(&image2_buf_R[i][j],1,1,Fimage2);
fread(&image2_buf_G[i][j],1,1,Fimage2);
fread(&image2_buf_B[i][j],1,1,Fimage2);
image2_R[i][j] = (int)image2_buf_R[i][j];
image2_G[i][j] = (int)image2_buf_G[i][j];
image2_B[i][j] = (int)image2_buf_B[i][j];
}
}
fclose(Fimage2);
//Do some operations
cout <<"Do some operations.\n";
//data hiding
int cover_r,secret_r,cover_g,secret_g,cover_b,secret_b;
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
cover_r = image_R[i][j]>>4<<4;
secret_r = image2_R[i][j]>>4;
cover_g = image_G[i][j]>>5<<5;
secret_g = image2_G[i][j]>>3;
cover_b = image_B[i][j]>>3<<3;
secret_b = image2_B[i][j]>>5;
result_R[i][j] = cover_r+secret_r;
result_G[i][j] = cover_g+secret_g;
result_B[i][j] = cover_b+secret_b;
}
}
//Write Image
cout <<"Write Image.\n";
Fresult=fopen("stego.raw","wb");
for (i=0;i<size;i++)
{
for(j=0;j<size;j++){
result_buf_R[i][j]=(unsigned char)result_R[i][j];
result_buf_G[i][j]=(unsigned char)result_G[i][j];
result_buf_B[i][j]=(unsigned char)result_B[i][j];
}
}
for (i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fwrite(&result_buf_R[i][j],1,1,Fresult);
fwrite(&result_buf_G[i][j],1,1,Fresult);
fwrite(&result_buf_B[i][j],1,1,Fresult);
}
}
fclose(Fresult);
cout <<"Finish.\n";
system("pause");
return 0;
}

解碼程式:

input:stego.raw
output:decode2.raw

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <stack>
#define size 512
using namespace std;
//=============input images=============//
unsigned char image_buf_R[size][size]; int image_R[size][size];
unsigned char image_buf_G[size][size]; int image_G[size][size];
unsigned char image_buf_B[size][size]; int image_B[size][size];
//=============output1 random result======//
unsigned char result_buf_R[size][size]; int result_R[size][size];
unsigned char result_buf_G[size][size]; int result_G[size][size];
unsigned char result_buf_B[size][size]; int result_B[size][size];
//=============Input====================//
FILE *Fimage;
//=============Output====================//
FILE *Fresult;
int main(void)
{
int n,i,j;
//Load Cover Image
cout <<"Load image.\n";
Fimage=fopen("stego.raw","rb");
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fread(&image_buf_R[i][j],1,1,Fimage);
fread(&image_buf_G[i][j],1,1,Fimage);
fread(&image_buf_B[i][j],1,1,Fimage);
image_R[i][j] = (int)image_buf_R[i][j];
image_G[i][j] = (int)image_buf_G[i][j];
image_B[i][j] = (int)image_buf_B[i][j];
}
}
fclose(Fimage);
//Do some operations
cout <<"Do some operations.\n";
//data hiding
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
result_R[i][j] = image_R[i][j]%16*16;
result_B[i][j] = image_B[i][j]%8*32;
result_G[i][j] = image_G[i][j]%32*8;
}
}
//Write Image
cout <<"Write Image.\n";
Fresult=fopen("decode2.raw","wb");
for (i=0;i<size;i++)
{
for(j=0;j<size;j++){
result_buf_R[i][j]=(unsigned char)result_R[i][j];
result_buf_G[i][j]=(unsigned char)result_G[i][j];
result_buf_B[i][j]=(unsigned char)result_B[i][j];
}
}
for (i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fwrite(&result_buf_R[i][j],1,1,Fresult);
fwrite(&result_buf_G[i][j],1,1,Fresult);
fwrite(&result_buf_B[i][j],1,1,Fresult);
}
}
fclose(Fresult);
cout <<"Finish.\n";
system("pause");
return 0;
}
view raw 001.cpp hosted with ❤ by GitHub


最後,祝大家有個美好的聖誕節~ 😄😄😄

3 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 作者您好,不好意思我是剛接觸的初學者,有些問題想請教您~
    請問一定要用RAW的圖檔嗎?
    我將您的程式執行後結合的stego.raw開不起來QQ
    謝謝您

    回覆刪除
    回覆
    1. hi andrea,
      raw 是未經壓縮的圖檔,所以可以直接對pixel 做動作,不需要先解壓縮
      但能支援這個圖檔的軟體很少,例如photoshop

      如果其他格式圖檔ex JPG PNG,可以使用openCV或matlab來實作,有現成函數能做格式預處理

      刪除

注意:只有此網誌的成員可以留言。