Image Steganography. What is Staganography?
By: Vaibhav Jagtap
Tags: Image Staganography, Image Processing, Staganography

1. By using LSB Insertion(Least Significant Bit Insertion)

Inorder to understand the algorithm we are go to take up a small task of hiding a image inside a original image using LSB algorithm. As images are nothing but pixels with rgb values, these rgb values can be converted into 8 bit binary numbers.

Each pixel has three value bits that are less significant. So, if we change the rightmost bits it will have a small visual impact on the final image. This is the steganography key to hide an image inside another. Change the less significant bits from an image and include the most significant bits from the other image.

An algorithm for hiding the image:

To hide an image inside another, the image which will be hidden needs to have at most the same size of the image which will hide it.

# Check the images dimensions

if img2.size[0] > img1.size[0] or img2.size[1] > img1.size[1]:

raise ValueError('Image 1 size is lower than image 2 size!')

2. We must create two loops to go through all rows and columns (actually each pixel) from the images.

# Get the pixel map of the two images

pixel_map1 = img1.load()

pixel_map2 = img2.load()



# Create a new image that will be outputted

new_image = Image.new(img1.mode, img1.size)

pixels_new = new_image.load()



for i in range(img1.size[0]):

for j in range(img1.size[1]):

3. Now we just have rgb tuple value in int, convert the int tuple to binary String tuple, write a function for it.

rgb1 = Steganography.__int_to_bin(pixel_map1[i, j])



# Use a black pixel as default

rgb2 = Steganography.__int_to_bin((0, 0, 0))



# Check if the pixel map position is valid for the second image

if i < img2.size[0] and j < img2.size[1]:

rgb2 = Steganography.__int_to_bin(pixel_map2[i, j]) @staticmethod

def __int_to_bin(rgb):

"""

Convert an integer tuple to a binary (string) tuple.

:param rgb: An integer tuple (e.g. (220, 110, 96))

:return: A string tuple (e.g. ("00101010", "11101011", "00010110"))

"""

r, g, b = rgb

return ('{0:08b}'.format(r),

'{0:08b}'.format(g),

'{0:08b}'.format(b))

4. Merge the highest significant bits of both the images as shown in the image above, we already have the binary strings from above stage so this would be easy.

# Merge the two pixels and convert it to a integer tuple

rgb = Steganography.__merge_rgb(rgb1, rgb2) @staticmethod

def __merge_rgb(rgb1, rgb2):

"""

Merge two RGB tuples.

:param rgb1: A string tuple (e.g. ("00101010", "11101011", "00010110"))

:param rgb2: Another string tuple (e.g. ("00101010", "11101011", "00010110"))

:return: An integer tuple with the two RGB values merged.

"""

r1, g1, b1 = rgb1

r2, g2, b2 = rgb2

rgb = (r1[:4] + r2[:4],

g1[:4] + g2[:4],

b1[:4] + b2[:4])

return rgb

5. Convert the binary string tuple back to int and here we have our encoded image.

pixels_new[i, j] = Steganography.__bin_to_int(rgb)

2. Masking and Filtering

It is well known that encryption provides secure channels for communicating entities.

However, due to lack of covertness on these channels, an eavesdropper can identify encrypted streams through statistical tests and capture them for further cryptanalysis.

Hence, the communicating entities can use steganography to achieve covertness

Masking and filtering techniques hide information by marking an image and is usually restricted to 24-bit and gray-scale images.

Digital watermarks include information such as copyright, ownership, or license.

While traditional steganography conceals information, watermarks extend information since it becomes an attribute of the cover image.

Instead of embedding a secret message into a multimedia object, as in traditional multimedia steganography, we process the entire secret message to make it appear statistically similar to a multimedia object itself.

Thereby we foil an eavesdropper who is primarily applying statistical tests to detect encrypted communication channels.

We show that our approach can potentially give a covert channel capacity which is an order of magnitude higher than traditional steganography

Masking techniques hide information in such a way that the hidden message is more integral to the cover image than simply hiding data in the “noise” level.

Masking adds redundancy to the hidden information.

This makes the masking technique more suitable than LSB with lossy JPEG images.

It may also help protect against some image processing such as cropping and rotating.

Advantages of Masking and filtering Techniques:

This method is much more robust than LSB replacement with respect to compression.

Disadvantages of Masking and filtering Techniques:

Techniques can be applied only to gray scale images and restricted to 24 bits.

3. Algorithms and Transformation

Algorithm and code for for merging ( hiding ) and unmerging the images .

Check the dimensions of the 2 images ( python .size function for matrix ) if they don’t match raise error Create a pixel map of the 2 images .the .load() function in python imaging library gets the pixel map which an be traversed as an array Create a third pixel map for the o/p image We can use the __int_to_bin method to convert a decimal value to a binary value for the pixel map Then, we create a new RGB value by concatenating only the 4 rightmost bits from the current pixel with zero values (to create a new 8-bit value) Finally, we convert the binary value to a decimal value and set it to the current pixel in the new image ( o/p image) If the hidden image is smaller than the image that was hiding it we can remove the black borders or crop the image .

Python function for merging 2 images — https://paste.ubuntu.com/p/kqPnYQnDvK/

Unmerging the images algorithm —

Load the pixel map of the image and load a empty pixel map which will represent the hidden image Get the RGB (as a string tuple) from the input image . Extract the last 4 bits (corresponding to the hidden image) Concatenate 4 zero bits because we are working with 8 bit Copy this array to the empty pixel map . Convert the pixel map to an integer tuple . Crop the image based on the ‘valid’ pixels . Valid pixels can be calculated using the extraction for loop to get the actual size of the image and remove unbounded pixels .

Function for unmerging the images i.e removing the hidden image .