How to Hide Data in Images Using Steganography
Have you ever wanted to send information in images like they do it in movies? Well, now you can with this cool script.
What is Steganography?
If one needs to define steganography, it can be simply said as a practice of hiding secret messages within or over something that is no secret. It is just a process of embedding a secret piece of text within a text, picture, or audio. The message could be a message or script within a document file or a picture file. A form of covert communication, the main purpose of steganography is concealing and deceiving. The message can be concealed through any medium. It differs from cryptography which is a science that enables privacy.
What is LSB (Least Significant Bit)?
LSB stands for Least Significant Bit. The idea behind LSB embedding is that if we change the last bit value of a pixel, there won’t be much visible change in the color. For example, 0 is black. Changing the value to 1 won’t make much of a difference since it is still black, just a lighter shade.
Python Implementation
Let's start by installing the required modules. The modules we will need are OpenCV, Numpy.
pip3 install opencv-python numpy
Now, we will import the modules.
import numpy as np
import cv2
Next, we will create a function that will take data and convert it to binary form.
def binary_convert(data):
"""This function converts our data into binary data."""
if isinstance(data, str):
return ''.join([format(ord(i), "08b") for i in data])
elif isinstance(data, bytes) or isinstance(data, np.ndarray):
return [format(i, "08b") for i in data]
elif isinstance(data, int) or isinstance(data, np.uint8):
return format(data, "08b")
else:
raise TypeError("This type is unsupported.")
The following code creates an encode() function. This function takes 2 arguments, one is the name/path of the image and the other is the data to encode. The encode function will then:
- Read the image using cv2.imread() function.
- It will then proceed to count the maximum bytes available to encode the data into.
- Check if we can encode all of the data in the given image.
- Adds a Stopping Criteria for the decoder to stop.
- Finally, modify the data of the image by just one bit and replace it by the data bit.
def encode(image_name, secret_data):
# Reads the image
image_rows = cv2.imread(image_name)
# Gives us the maximum no. of bytes to encode.
n_bytes = image_rows.shape[0] * image_rows.shape[1] * 3 // 8
print("Maximum bytes to encode are:", n_bytes)
if len(secret_data) > n_bytes:
raise ValueError(
"Insufficient bytes,we will need a bigger image or less data.")
print("Encoding Data to Image...")
# This adds a stopping criteria, which is used to guess the
secret_data += "====="
data_index = 0
# Converts the data to binary.
binary_secret_data = binary_convert(secret_data)
# Gets the size of the data to hide.
data_len = len(binary_secret_data)
for row in image_rows:
for pixel in row:
r, g, b = binary_convert(pixel)
if data_index < data_len:
pixel[0] = int(r[:-1] + binary_secret_data[data_index], 2)
data_index += 1
if data_index < data_len:
pixel[1] = int(g[:-1] + binary_secret_data[data_index], 2)
data_index += 1
if data_index < data_len:
pixel[2] = int(b[:-1] + binary_secret_data[data_index], 2)
data_index += 1
if data_index >= data_len:
break
return image_rows
The following code creates the decode()
function. This function takes only 1 argument, which is the name of the image to decode. The decode function simply reads the image, gets the last bits of every pixel of the image and then keep decoding until it detects the stopping criteria.
def decode(image_name):
print("Decoding data from Image...")
# Reading the Image
image_rows = cv2.imread(image_name)
binary_data = ""
for row in image_rows:
for pixel in row:
r, g, b = binary_convert(pixel)
binary_data += r[-1]
binary_data += g[-1]
binary_data += b[-1]
# Splitting the image by 8-bits
all_bytes = [binary_data[i: i+8] for i in range(0, len(binary_data), 8)]
# Now, we convert the bits to characters
decoded_data = ""
for byte in all_bytes:
decoded_data += chr(int(byte, 2))
if decoded_data[-5:] == "=====":
break
return decoded_data[:-5]
This snippet will assign the name of the input image in the input_img
variable and the name of the file to be outputted in the out_img
variable.
# We will assign variables as the name of the input and the output file.
input_img = "img.png" # Enter the name of your image here.
out_img = "img_out.png" # Also, update this accordingly.
This code calls the encode()
function and saves the output to a variable. Then, it will use the cv2.imwrite()
to save the data as an image file.
# Now we can just call the encode() function to encode the image and save the data to a variable.
enc_image = encode(input_img, "Hi Folks!") # Replace "Hi Folks!" with the data you want to encode.
cv2.imwrite(out_img, enc_image) #Using OpenCV to write the result to an actual image file.
The final step is to decode the image using the decode()
function and read the data from the image.
# We can decode the image by simply calling the decode() function.
decode(out_img)
Wrap-Up
Congratulations! You are now ready to communicate secretly with this age old trick. If you want to see the full code then head over at my gist.