論文實作篇在這邊: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,
![]() |
左上(cover image)、右上(secret image)、左下(stego image)、右下(decode image) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
最後,祝大家有個美好的聖誕節~ 😄😄😄
作者已經移除這則留言。
回覆刪除作者您好,不好意思我是剛接觸的初學者,有些問題想請教您~
回覆刪除請問一定要用RAW的圖檔嗎?
我將您的程式執行後結合的stego.raw開不起來QQ
謝謝您
hi andrea,
刪除raw 是未經壓縮的圖檔,所以可以直接對pixel 做動作,不需要先解壓縮
但能支援這個圖檔的軟體很少,例如photoshop
如果其他格式圖檔ex JPG PNG,可以使用openCV或matlab來實作,有現成函數能做格式預處理