Introduction

A well known method of steganoanalysis is to search one coloured area of the image for variations. In diffuse areas where every pixel has a different color than its neighbors, variations by hidden bits are hard to detect, but most pictures also contain areas with smooth colors. Look at this one:

The blue sky in the upper middle should not contain hidden data, because there are nearly no natural variations in the colors.

The clouds have more shades of blue, but anything that's not blue would be easy to find. If any data has to be hidden in the clouds, not more than one bit per pixel should be changed.

It is the same with the trees on the right: changing the higher bits would produce light colors, but only dark pixels are allowed here, so the capacity of each pixel is reduced to one or two bits.

The boats and the beach on the left side are better. They contain red, yellow, white, blue, green... We can change up to 7 bits in this region, nobody will notice anything.

To escape from simple variations analysis, we'll hide our secret message only in these regions, and with adjusted "bit rates":

Edit and Store Regions

Just as in the preceding examples, we need a carrier bitmap, a secret message, and a key.

The new feature is a Region Editor which lets the user define regions and their capacities.

An easy way to draw a region is to click the points of a polygon. So, we let the user click on the image, and add every clicked point to a polygon. A polygon can be closed with a double click, then the next click starts a new polygon:

When a polygon is being closed by a double click, we have to make sure that it does not overlap one of the already existing polygons. If the new polygon intersects another polygon, we merge the two regions. If the pölygon stands alone, we create a new region and add it to the list. ctlRegions is a RegionInfoList, this control displays statistics and input fields for each region.

If another region has been drawn, we have to update the map and the statistics block. ReDrawImages paints the regions onto the source image and the map overview. (The map overview and the gradient brush are not necessary, they're just a nice visual effect.)

The map has to be stored in the first pixels of the image so that it can be extracted before the rest of the message. That means, we have to embed a header into the image. When extracting the hidden message, we first have to extract the header, read the region information from it, and then we can extract the actual message from those regions. The header can be spread over all pixels from 0/0 to the first pixel in the topmost region. We won't know where the first region begins before we've extracted the map, so we have to store the index of the first pixel that belongs to any region in the header itself. The coordinates of this pixel are not important, because we'll treat the pixels as one long stream, not as rows and columns. A complete header contains these information:

(Int32) Index (not coordinates!) of the topmost pixel in the first region.

(Int32) Length of the following region data.

For every region:

(Int32) Length (Region.GetRegionData().Data.Length)

(Int32) Capacity (Count of bytes to hide in this region)

(byte) Count of used bits per pixel

(byte[]) Region (Region.GetRegionData().Data)

The length of the header depends on the count and the complexity of the regions. When a new region is added in the Region Editor, we must check the new header's length and the position on the topmost region. If there are not enough pixels left between the image's first pixel and the first region, the header cannot be hidden. In that case, we'll display a warning and disable the "Next" button. The regions that are going to carry the actual message have to be big enough, we'll display another warning if the message does not fit into the regions:

If the regions are big enough, configured for enough capacity and leaves enough space for the header, UpdateSummary will enable the "Next" button. Now, the map and the message can be hidden.

Embed the Data

Until now, we have done nothing except receiving input data about the carrier image. Now, the interesting part begins! In the earlier articles, we used the key to locate pixels, and simply embedded the message. This won't work anymore. We have specific regions over which the data has to be distributed, and the header should be distributed evenly over the available pixels at the beginning of the image. That means, the bytes from the key stream cannot be used directly as the next offset, but we can use them to initialize a pseudo-random number generator. This number generator can choose the next offset:

When the message is extracted later on, we just have to initialize the System.Ramdom object with the same seed (a value from the key stream), and we'll get the same offsets again. But, we need two values to calculate the intervals: the length of the data we want to hide/extract, and the count of the remaining pixels. Let's hide these two Int32 values in the first 64 pixels so that we can read them easily.

The algorithm doesn't concat the sequence of bytes to hide, so many bits per pixel are unused, if you use a 7 bit encryption, for every two pixels you use 7 bits in first pixel and only one in the second, and just for this that the capacity calculation formula is wrong, you can change it with this:

Wie gehts ?
I was wondering why have you used such approach. I mean selecting the region manually and then hiding the data. Have you done the direct implementation of this, i mean like user doesn't have to select any region and it does it automatically like in most of the steganography software.
And also which method according to you is best the lsb approach that you used in this article or the key method that you used in all your previous methods.
And any other article use lsb approach or this is the only one ?

hello , nice work but can you make your program found good region to hide Automatic not by user and set the size of data to hide Automatic also
thx
for DCT Steganography i can help you in that , i write that program use DCT to fight MPEG video file compressed.

Hiya i am looking for a steganography application for .jpg n .mp3 carriers and also mpeg. You mentioned that you have done it already.Could you please help me out in this.
I would be greatful to you...
Thanks a lot
James

Easy to understand & practical. Thanks!
Can it be used for water marking? (will the message be reserved after some simple image processing processes such as: print & scan the photo again, resizing,...?)

It did create an region object polygionRG , but , how I get the points (that is 100,10 300,10 200,50 ) of this region polygionRG , use polygionRG.GetRegionData() ? If I must use GetRegionData method , can you tell me how ?

There is no official way to get the polygon from a Region.
The Region does not contain a path or coordinates anymore, it is an array of rectangles that covers an area.

The format of the data returned by Region.GetRegionData is not documented (or I have not found any documentation), but I think the method does nearly the same as the old GDI function GetRegionData. It returns a byte array with the coordinates of all rectangles that make up the region.

// Now, RgnData[0] to RgnData[7] are header information you probably
// don't need. RgnData[8] is a Left, RgnData[9] is a Top,
// RgnData[10] is a Right, and RgnData[11] is a Bottom for a single
// rectangle, and the rest of the array will continue that pattern.

hi... nice work on code... but could u pls make an article about on discret cosine transform jpeg steganography. I have to finish my final project so that i can get my graduate tittle. Is it possible to do like this... 1. Data (any data. .txt, .exe, .pdf, and soon) --> compress (huffman) -->encrypt it with 3des ---> steg. it on jpeg ... coz the plain data is not just only on .txt but also on others format pls replay me...

when the life is end then all we have is gone... so... why don't help each other when we still breath...

Sorry, but I cannot write an article about jpeg, because I don't have a clue about cosine trandforms and all that complicated stuff.
But there are a few open source solutions available. You should have a look at this article:About JPG steganography,
and theSteghide project.

Congratulations for your article, but I have only VC6++. Can you tell me how do I can convert all projects (Steganography 1-12). I tried to convert using Project Converter VC++7 to VC++6 made by Stephane Rodriguez, but for some reason I didn't get the project to open.

Yes, I'm back (holiday)... Thanx for your great articel and especially for your advanced image processing functions (methods)... So, I think it's very useful but i think it's not very recommend for large messages...

This is very impressive... both for the "neato" factor and for the fact that you're not selling it

I especially like the "pictograph" data storage method. Imagine the fun you could have using a satellite photo -- hide, say, the time and address of a party in areas that illustrate the roads you take. Very, very cool.