Simple test to hide Information in Image (I) - Source code.
- X.G. ZHANG
ATL CImage class is used in the following code, and it can only be built
in Windows environment, however the principle is quite simple. Some advance
technologies can be used actually, for example data encryption and decryption,
and information can be distributed to image more evenly.
Source code are as follows:
//=================================================================================================
// Hide Information in Image, a simple test, no right reserved by X.G. ZHANG
//
Nanjing, China
//=================================================================================================
// File2Image.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "File2Image.h"
#include "math.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define SW_ENCRYPT
#define MAGIC_HEADER_SIZE
12
// 12 = 4 + 4 + 4,
-------
magic data (4), data_size(4), randome_seed (4)
#define MAGIC_DATA
0xF0
#define MAGIC_DATA_IN_IMAGE
1234567
#define MY_ABS(a)
((a>0)?(a):(-a))
// turn a byte array to int, machine independent
#define INT_FROM_CHAR_ARRAY(iV,pByte) do{\
iV = \
(((int)(unsigned char)(pByte)[0]) +
\
(((int)(unsigned char)(pByte)[1]) << 8)
+
\
(((int)(unsigned char)(pByte)[2]) << 16) + \
(((int)(unsigned char)(pByte)[3]) << 24) );\
}while(0)
// turn an int to byte array, machine independent
#define CHAR_ARRAY_FROM_INT(pByte, iV) do{\
(pByte)[0] =
(byte)(iV & 0x000000FF);\
(pByte)[1] = (byte)((iV & 0x0000FF00) >> 8);\
(pByte)[2] = (byte)((iV & 0x00FF0000) >> 16);\
(pByte)[3] = (byte)((iV & 0xFF0000) >> 24);\
}while(0)
#include "atlimage.h"
#include "math.h"
class CMagicHeader
{
public:
CMagicHeader()
{
Init(0,0);
}
void Init(int data_size, int rand_seed)
{
m_magic_data = MAGIC_DATA_IN_IMAGE;
m_actual_data_size = data_size;
m_rand_seed = rand_seed;
}
public:
bool IsLegal()
{
return (MAGIC_DATA_IN_IMAGE == m_magic_data);
}
int
GetHeaderSize()
{
return 12;
}
public:
void ToByteArray(byte* bArray)
{
CHAR_ARRAY_FROM_INT(bArray,m_magic_data);
CHAR_ARRAY_FROM_INT(bArray+4,m_actual_data_size);
CHAR_ARRAY_FROM_INT(bArray+8,m_rand_seed);
}
void FromByteArray(byte* bArray)
{
INT_FROM_CHAR_ARRAY(m_magic_data,bArray);
INT_FROM_CHAR_ARRAY(m_actual_data_size,bArray+4);
INT_FROM_CHAR_ARRAY(m_rand_seed,bArray);
}
protected:
int m_magic_data;
int m_actual_data_size;
int m_rand_seed;
};
template <class T>
class CMemCleaner
{
protected:
T*&
m_Ptr;
BOOL
m_bIfArray;
CMemCleaner(){}
//Disable this function
public:
CMemCleaner(T*& pT,BOOL IfArray = FALSE):m_Ptr(pT)
{
m_bIfArray = IfArray;
}
virtual ~CMemCleaner()
{
Reset();
}
public:
T*& operator ->()
{
return m_Ptr;
}
public:
//only for array
T& operator [](int i)
{
return m_Ptr[i];
}
protected:
void
Reset()
{
if(m_Ptr)
{
if(m_bIfArray)
{
delete []m_Ptr;
}
else
{
delete m_Ptr;
}
m_Ptr = NULL;
}
}
};
virtual class IBitStream
{
public:
virtual void EnumInit() = 0;
virtual void EnumNext() = 0;
virtual bool IsEnd() = 0;
virtual byte GetCurrentBit() = 0;
virtual void SetCurrentBit(byte b) = 0; // b=1, means to set current bit to 1, otherwise set current bit = 0;
public:
virtual int
GetTotalBitNum() = 0;
};
//=================================================================================================
// iterate order (byte number, bit number)
//
(0,0) -( 0, 1) - (0,2) ... ( 0,7 ) ==> (1,0) - (1,1) - (1,2) - .. ( 1,7)
//
..................... (m_size-1, 0) - ( m_size -1, 1) ......... ( m_size -1, 7)
//
so, first bit ( 0,0), second bit ( 0.1) ... last bit ( m_size -1, 7)
//=================================================================================================
class CDataBitStream: IBitStream
{
public:
CDataBitStream(byte* array, int size)
{
m_array = array;
m_size = size;
m_cur_bit_pos = 0;
}
private:
CDataBitStream()
{
printf("Disabled!\r\n");
}
public:
virtual void EnumInit()
{
m_cur_bit_pos = 0;
}
virtual void EnumNext()
{
m_cur_bit_pos ++;
}
virtual bool IsEnd()
{
if ( (m_cur_bit_pos >> 3) == m_size)
// 8 bit --> size = 1, 16 bit -> size = 2
{
return TRUE;
}
else
{
return FALSE;
}
}
virtual int
GetTotalBitNum()
{
return m_size << 3;
}
public:
virtual byte GetCurrentBit()
{
int byte_pos = m_cur_bit_pos / 8;
int bit_pos = m_cur_bit_pos % 8;
byte b = m_array[byte_pos];
b = (byte)(b << (7 - bit_pos ));
return b >>
7;
}
virtual void SetCurrentBit(byte b)
{
int byte_pos = m_cur_bit_pos / 8;
int bit_pos = m_cur_bit_pos % 8;
//TRACE("byte_pos = %d, bit_pos = %d, old_value = %d ", byte_pos, bit_pos, m_array[byte_pos]);
if( b == 1)
{
m_array[byte_pos]
|= (byte)(0x01 << bit_pos);
}
else //
{
m_array[byte_pos]
&= (byte) \
(
(byte)( 0xFF >> (8 - bit_pos)) |
\
(byte)( 0xFF << (bit_pos +1 ))
\
);
}
//TRACE("new_value = %d \r\n",
m_array[byte_pos]);
}
public:
int GetBitNumWhenEncodeInIma
ge(int image_width, int image_height)
{
int valid_pitch = image_width * 3;
//valid byte number in each "line"
//-----------------------------------------------------------------------------------------
for (int i = 1; i < 16; i = i * 2)
{
if( i * valid_pitch * image_height >= (m_size * 8) )
{
break;
}
}
return (i < 9)? (i):0; // return 0, means too much information, cannot be hided, otherwise, i bits will be used for each bytes in bmp byte array
}
public:
virtual ~CDataBitStream()
{
// need do nothing
}
#ifdef _WIN32
public:
CImage*
CreateCompatiableImage()
{
int total_size = m_size + MAGIC_HEADER_SIZE;
// 4 for magic data, 4 for actual_useful_size, 4 for randome seed
int root = (int)sqrt((double)total_size/3)+1;
CImage* pImage = new CImage();
pImage->CreateEx(root,-root,24,BI_RGB,NULL);
// use -root, means up-bottom bitmaps
return pImage;
}
#endif
protected:
byte*
m_array;
int
m_size;
int
m_cur_bit_pos; // start from 0
High --> | 7 | 6 | 5 | ...| 3 | 2 | 1 | 0 | <-- LOW
( b7 b6 ... b0 )
private:
// only for code backup
virtual void SetCurrentBit_bad(byte b)
{
int byte_pos = m_cur_bit_pos / 8;
int bit_pos = m_cur_bit_pos % 8;
//TRACE("byte_pos = %d, bit_pos = %d, old_value = %d ", byte_pos, bit_pos, m_array[byte_pos]);
m_array[byte_pos]
&= ( (byte)(b << bit_pos) |
(byte)(0xFF << (bit_pos +1)) | (byte)(0xFF >>( 8-bit_pos))
) ;
//TRACE("new_value = %d \r\n",
m_array[byte_pos]);
}
};
//==========================================================================================================================
// The iterator order of Bit in bitmap is quite different from that of normal data
//
suppose ( x,y) means bit y in byte x, for example ( 3,2) means the second bit in byte 3 ( actually the forth byte)
//
//
----->
(0,0) - (1,0) - (2,0) ... ( m_width*3 -3,,0)
//
----->
(m_width*3,0)
..........(2*m_width*3-3,0)
//
...........
//
----->
...........................(m_width*m_height*3-3, 0)
## here, we have gotten every first bit in the bitmap array, we will try to get all second bits
//
//
----->
(0,1) - (1,1) ..............(m_width*-13, 1)
//
...........................(m_width*m_height*3-3,1)
## here, we have gotten every second bit in bitmap array,
//
//
.......................................................
//
//
----->
(0,7) - (1,7) ...............(m_width*3-3, 7)
//
......................................................
//
----->
(m_width*(m_height-1)*3,7) ... (m_width*m_height*3-3,7)
//
//
//
Special words: This class doesn't support multi-thread, please constructor Stream instance for each thread
//
Also, no lock mechnism is supported.
//==========================================================================================================================
class CBitmapBitStream: IBitStream
{
public:
// please use the two APIs to encode/decode information from Image
boolean HideInformation(byte* bArray, int iArraySize)
{
CDataBitStream dbs(bArray,iArraySize);
return HideInformation(&dbs);
}
boolean HideInformation(CDataBitStream * pSourceStream)
{
int iBitNum = pSourceStream->GetBitNumWhenEncodeInIma
ge(m_width,m_height);
if(iBitNum <=0 || iBitNum > 8)
{
return FALSE;
}
for(this->EnumInit(),pSourceStream->EnumInit(); !pSourceStream->IsEnd();pSourceStream->EnumNext(),this->EnumNext())
{
this->SetCurrentBit(pSourceStream->GetCurrentBit());
}
}
byte* ExtraceHideInformation() // Extract Hide information in the image
{
int iAll = m_width*3*m_height;
byte* bArray = new byte[iAll];
// allocated memory
if(bArray)
{
CDataBitStream dbs(bArray, iAll);
for(this->EnumInit(),dbs.EnumInit(); !dbs.IsEnd();dbs.EnumNext(),this->EnumNext())
{
dbs.SetCurrentBit(this->GetCurrentBit());
}
}
return bArray;
}
public:
// please remember, this class will not de-allocate data
CBitmapBitStream(byte* bmp_array, int pitch, int width, int height)
{
m_array = (byte*)bmp_array;
m_pitch = pitch;
m_height = height;
m_width = width;
m_x = m_y = m_bit_pos = 0;
ASSERT( MY_ABS(pitch) == ( (width* 3 % 4 == 0) ? (width*3 ):((width* 3 /4+1) * 4)) );
}
#ifdef _WIN32
CBitmapBitStream(CImage* pImg)
{
byte* bmp_array = (byte*)pImg->GetBits();
int pitch = pImg->GetPitch();
int width = pImg->GetWidth();
int height = pImg->GetHeight();
m_array = (byte*)bmp_array;
m_pitch = pitch;
m_height = height;
m_width = width;
m_x = m_y = m_bit_pos = 0;
ASSERT( MY_ABS(pitch) == ( (width* 3 % 4 == 0) ? (width*3 ):((width* 3 /4+1) * 4)) );
}
#endif
private:
CBitmapBitStream(){} // disable user directly create such an object
public:
virtual void EnumInit()
{
m_x = m_y = m_bit_pos = 0;
}
virtual void EnumNext()
{
m_x ++;
if( m_x == m_width * 3 ) // valid byte, padded byte will be clean by ATL Image lib automatically, so we will not use it
{
m_x = 0;
m_y ++;
if( m_y == m_height) // that means we will iterate higher bit in bitmap array
{
m_y = 0;
m_bit_pos ++;
}
}
}
virtual bool IsEnd()
{
return ( m_bit_pos == 9);
}
virtual int GetTotalBitNum()
{
return MY_ABS(m_width* 3 * m_height);
}
public:
virtual byte GetCurrentBit()
{
byte b = *(m_array + m_y * m_pitch + m_x );
return
(byte)(((byte)(b << (7-m_bit_pos))) >> 7);
}
virtual void SetCurrentBit(byte b) // bit is stored in the 0-th bit of "b"
{
byte* pB = m_array + m_y * m_pitch + m_x;
//TRACE("m_x= %d, m_y = %d, m_bit_pos = %d data=%d ",m_x,m_y,m_bit_pos,*pB);
if( b == 1)
{
(*pB) |= (byte)(0x01 << m_bit_pos);
}
else //
{
(*pB) &= (byte)\
( (byte)( 0xFF >> (8 - m_bit_pos)) | \
(byte)( 0xFF << (m_bit_pos +1)) \
);
}
//(*pB) &= ( (b << m_bit_pos) |
((byte)(0xFF))<< (m_bit_pos +1) | (byte)(((byte)(0xFF)) >> (8-m_bit_pos))
) ;
//TRACE(" new_data = %d\r\n", *pB);
}
public:
virtual ~CBitmapBitStream()
{
}
public:
CMagicHeader*
RetrieveMagicHeaderInBit
map()
{
int bit_num = GetTotalBitNum();
byte data[12];
if(bit_num < 12)
{
return NULL;
}
CDataBitStream dbs((byte*)data,12);
for(dbs.EnumInit(),EnumInit(); !this->IsEnd() && !dbs.IsEnd(); this->EnumNext(),dbs.EnumNext())
{
dbs.SetCurrentBit(this->GetCurrentBit());
}
CMagicHeader* pHeader = new CMagicHeader();
pHeader->FromByteArray(data);
#ifndef SW_ENCRYPT
//if(pHeader->IsLegal() == FALSE)
//{
//
delete pHeader;
//
pHeader = NULL;
//}
#endif
// re-initialize iterator
dbs.EnumInit();
this->EnumInit();
return pHeader;
}
protected:
byte*
m_array;
int
m_pitch;
// usually m_pitch is a multiplication of 4, so it not always equal to (width * 4) for a 24 color bmp
int
m_height;
int
m_width;
int
m_x,m_y;
// x,y is used to iterate bits in the bitmap array,
int
m_bit_pos;
};
ATL CImage class is used in the following code, and it can only be built
in Windows environment, however the principle is quite simple. Some advance
technologies can be used actually, for example data encryption and decryption,
and information can be distributed to image more evenly.
Source code are as follows:
//=================================================================================================
//
//
//=================================================================================================
// File2Image.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "File2Image.h"
#include "math.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define SW_ENCRYPT
#define MAGIC_HEADER_SIZE
#define MAGIC_DATA
#define MAGIC_DATA_IN_IMAGE
#define MY_ABS(a)
// turn a byte array to int, machine independent
#define INT_FROM_CHAR_ARRAY(iV,pByte)
// turn an int to byte array, machine independent
#define CHAR_ARRAY_FROM_INT(pByte, iV)
#include "atlimage.h"
#include "math.h"
class CMagicHeader
{
public:
public:
public:
protected:
};
template <class T>
class CMemCleaner
{
protected:
public:
public:
public:
protected:
};
virtual class IBitStream
{
public:
public:
};
//=================================================================================================
//
//
//
//
//=================================================================================================
class CDataBitStream: IBitStream
{
public:
private:
public:
public:
public:
public:
#ifdef _WIN32
public:
#endif
protected:
private:
};
//==========================================================================================================================
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//==========================================================================================================================
class CBitmapBitStream: IBitStream
{
public:
public:
#ifdef _WIN32
#endif
private:
public:
public:
public:
public:
#ifndef SW_ENCRYPT
#endif
protected:
};