[C# Helper]
Index Books FAQ Contact About Rod
[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

[C# 24-Hour Trainer]

[C# 5.0 Programmer's Reference]

[MCSD Certification Toolkit (Exam 70-483): Programming in C#]

Title: Use steganography to hide multiple images with different sizes in C#

[Use steganography to hide multiple images with different sizes in C#]

The post Use steganography to hide multiple images in C# shows how you can hide four images inside another another image. Unfortunately it requires that the four hidden images be exactly half the width and half the height of the visible image. That method works well, but is annoyingly restrictive. This example removes the size requirement.

To remove the size restriction, this version simply resizes the four hidden images so they have the correct sizes. The following code shows how the program resizes its four images and encodes them.

// Hide the four hidden images inside bm_visible // and return the result. public Bitmap HideResizedTiledImages(Bitmap bm_visible, Bitmap hidden1, Bitmap hidden2, Bitmap hidden3, Bitmap hidden4, int hidden_bits) { // Resize the hidden images to fit. int wid = bm_visible.Width / 2; int hgt = bm_visible.Height / 2; Rectangle dest = new Rectangle(0, 0, wid, hgt); Bitmap bm1 = new Bitmap(wid, hgt); Rectangle source = new Rectangle(0, 0, hidden1.Width, hidden1.Height); using (Graphics gr = Graphics.FromImage(bm1)) { gr.DrawImage(hidden1, dest, source, GraphicsUnit.Pixel); } Bitmap bm2 = new Bitmap(wid, hgt); source = new Rectangle(0, 0, hidden2.Width, hidden2.Height); using (Graphics gr = Graphics.FromImage(bm2)) { gr.DrawImage(hidden2, dest, source, GraphicsUnit.Pixel); } Bitmap bm3 = new Bitmap(wid, hgt); source = new Rectangle(0, 0, hidden3.Width, hidden3.Height); using (Graphics gr = Graphics.FromImage(bm3)) { gr.DrawImage(hidden3, dest, source, GraphicsUnit.Pixel); } Bitmap bm4 = new Bitmap(wid, hgt); source = new Rectangle(0, 0, hidden4.Width, hidden4.Height); using (Graphics gr = Graphics.FromImage(bm4)) { gr.DrawImage(hidden4, dest, source, GraphicsUnit.Pixel); } // Hide the resized images. Bitmap combined = HideTiledImages(bm_visible, bm1, bm2, bm3, bm4, hidden_bits); // Hide the sizes of the original images in the result. int[] sizes = { hidden1.Width, hidden1.Height, hidden2.Width, hidden2.Height, hidden3.Width, hidden3.Height, hidden4.Width, hidden4.Height, }; byte[] bytes = IntArrayToByteArray(sizes); int x = 0, y = 0; EncodeBytes(ref x, ref y, combined, bytes); return combined; }

The code divides the width and height of the visible image by two. It creates a new bitmap with the same size as the visible image and then copies the four hidden images onto the bitmap.

[Use steganography to hide multiple images with different sizes in C#] When the previous example copied the hidden images, it kept them their original sizes. This example resizes the images so each fits in exactly 1/4 of the visible image's area. The picture on the right shows the combined result. Notice that all of the images are distorted.

The program then uses the HideTiledImages method to encode the image as before.

At this point you could retrieve the images as they are shown in Figure 1. To allow you to restore the images to their original sizes, the program saves the images' original sizes in the combined image. It saves the image sizes in an integer array, uses the IntArrayToByteArray method to convert the data into a byte array, and then uses the EncodeBytes method (described in earlier posts) to save the byte data into the combined image.

The following code shows the IntArrayToByteArray method.

// Convert an int[] into a byte[]. private byte[] IntArrayToByteArray(int[] ints) { byte[] result = new byte[ints.Length * sizeof(int)]; Buffer.BlockCopy(ints, 0, result, 0, result.Length); return result; }

This method converts an integer array and into a byte array. First it creates a byte array big enough to hold the data. It then calls Buffer.BlockCopy to copy the bytes of data from the integer array into the byte array. Finally it returns the byte array.

The following ByteArrayToIntArray method does the opposite. It converts a byte array into an integer array.

// Convert a byte[] into an int[]. private int[] ByteArrayToIntArray(byte[] bytes) { int[] result = new int[bytes.Length / sizeof(int)]; Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length); return result; }

To decode the combined image, the program uses the following RecoverResizedTiledImages method.

// Recover four resized tiled images. public void RecoverResizedTiledImages(Bitmap bm_combined, out Bitmap hidden1, out Bitmap hidden2, out Bitmap hidden3, out Bitmap hidden4, int hidden_bits) { // Get the image sizes. int x = 0, y = 0; byte[] bytes = DecodeBytes(ref x, ref y, 4 * 2 * sizeof(int), bm_combined); int[] sizes = ByteArrayToIntArray(bytes); // Recover the resized tiled images. Bitmap bm1, bm2, bm3, bm4; RecoverTiledImages(bm_combined, out bm1, out bm2, out bm3, out bm4, hidden_bits); // Restore the images' original sizes. Rectangle dest; int wid = bm_combined.Width / 2; int hgt = bm_combined.Height / 2; Rectangle source = new Rectangle(0, 0, wid, hgt); int index = 0; wid = sizes[index++]; hgt = sizes[index++]; dest = new Rectangle(0, 0, wid, hgt); hidden1 = new Bitmap(wid, hgt); using (Graphics gr = Graphics.FromImage(hidden1)) { gr.DrawImage(bm1, dest, source, GraphicsUnit.Pixel); } wid = sizes[index++]; hgt = sizes[index++]; dest = new Rectangle(0, 0, wid, hgt); hidden2 = new Bitmap(wid, hgt); using (Graphics gr = Graphics.FromImage(hidden2)) { gr.DrawImage(bm2, dest, source, GraphicsUnit.Pixel); } wid = sizes[index++]; hgt = sizes[index++]; dest = new Rectangle(0, 0, wid, hgt); hidden3 = new Bitmap(wid, hgt); using (Graphics gr = Graphics.FromImage(hidden3)) { gr.DrawImage(bm3, dest, source, GraphicsUnit.Pixel); } wid = sizes[index++]; hgt = sizes[index++]; dest = new Rectangle(0, 0, wid, hgt); hidden4 = new Bitmap(wid, hgt); using (Graphics gr = Graphics.FromImage(hidden4)) { gr.DrawImage(bm4, dest, source, GraphicsUnit.Pixel); } }

This method first recovers the bytes holding the original image sizes. Then for each hidden image, the program makes a bitmap of the correct size and copies that image's piece of the result image into the bitmap.

If the hidden images are originally small, resizing and then un-resizing them may not change them too much. If the images are relatively large, however, you may lose some data and the images may come out fuzzy. If you look closely at the picture at the top of this post, you'll see that the recovered image looks a bit fuzzy. The result looks a bit like the loss of information you get when you use JPEG compression.

In this example, the four hidden images are all bigger than the visible image so it's pretty remarkable that you can hide so much information and get anything back at all.

There are some improvements you could make to this technique. For example, you could reserve the first row of pixels in the combined image to hold the image size data so you don't need to encode that information in the combined image. That data takes up 8×4 = 32 bytes, so it messes up the first few pixels in the upper left corner of the combined image. That means it also messes up the first few pixels in the first recovered image.

You could also try resizing the hidden images by different amounts. For example, if two of the images are small and two are big, you might try to resize them all by roughly the same percentage and then tile them onto the combined image. That would create less distortion for the larger images but it would be a lot harder to do.

Download the example to experiment with it and to see additional details.

© 2009-2023 Rocky Mountain Computer Consulting, Inc. All rights reserved.