Steganography Hiding Data in Images
{{REWRITTEN_CODE}} Steganography is the practice of concealing information within other non-secret data to avoid detection. In this article, we’ll explore how to hide data in images using Python, covering various techniques and tools with practical code examples and real-life use cases.
Introduction to Image Steganography
Image steganography involves hiding data within digital images. This process typically alters the least significant bits (LSB) of pixel values, making changes imperceptible to the human eye.
graph LR A[Original Image] --> B[Steganography Process] C[Secret Data] --> B B --> D[Stego Image] D --> E[Extraction Process] E --> F[Recovered Data]
Modern LSB Steganography
Let’s start with an optimized example of LSB steganography using Python and the numpy
library for improved performance.
import numpy as np
from PIL import Image
def encode_message(image_path, message, output_path):
# Load image and convert to numpy array
img = np.array(Image.open(image_path))
# Convert message to binary
binary_message = ''.join(format(ord(char), '08b') for char in message + '\0')
message_length = len(binary_message)
# Check if message can fit in the image
if message_length > img.size:
raise ValueError("Message too large for the image")
# Flatten the image array
flat_img = img.flatten()
# Create a binary array from the message
msg_array = np.array(list(binary_message), dtype=int)
# Modify the least significant bits
flat_img[:message_length] = (flat_img[:message_length] & 0xFE) | msg_array
# Reshape and save the image
stego_img = flat_img.reshape(img.shape)
Image.fromarray(stego_img.astype('uint8')).save(output_path)
print("Message encoded successfully")
def decode_message(image_path):
# Load image and convert to numpy array
img = np.array(Image.open(image_path))
# Extract the least significant bits
binary_message = ''.join(str(pixel & 1) for pixel in img.flatten())
# Convert binary to text
message = ''
for i in range(0, len(binary_message), 8):
byte = binary_message[i:i+8]
message += chr(int(byte, 2))
if message[-1] == '\0':
return message[:-1]
return message
# Usage
encode_message("original_image.png", "This is a secret message", "stego_image.png")
decoded_message = decode_message("stego_image.png")
print("Decoded message:", decoded_message)
This optimized version uses numpy
for faster array operations, significantly improving performance for large images.
Real-life Example: Watermarking Digital Art
Let’s consider a scenario where an artist wants to embed a watermark in their digital artwork to protect copyright. We’ll use a more advanced technique that combines LSB steganography with a simple encryption method.
import numpy as np
from PIL import Image
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
def encrypt_message(message, key):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(pad(message.encode(), AES.block_size))
def decrypt_message(ciphertext, key):
cipher = AES.new(key, AES.MODE_ECB)
return unpad(cipher.decrypt(ciphertext), AES.block_size).decode()
def embed_watermark(image_path, watermark, key, output_path):
# Load image
img = np.array(Image.open(image_path))
# Encrypt watermark
encrypted_watermark = encrypt_message(watermark, key)
binary_watermark = ''.join(format(byte, '08b') for byte in encrypted_watermark)
# Embed watermark
flat_img = img.flatten()
for i, bit in enumerate(binary_watermark):
if i >= flat_img.size:
break
flat_img[i] = (flat_img[i] & 0xFE) | int(bit)
# Save watermarked image
watermarked_img = flat_img.reshape(img.shape)
Image.fromarray(watermarked_img.astype('uint8')).save(output_path)
def extract_watermark(image_path, key, watermark_length):
# Load image
img = np.array(Image.open(image_path))
# Extract binary watermark
binary_watermark = ''.join(str(pixel & 1) for pixel in img.flatten()[:watermark_length*8])
# Convert to bytes and decrypt
encrypted_watermark = bytes(int(binary_watermark[i:i+8], 2) for i in range(0, len(binary_watermark), 8))
return decrypt_message(encrypted_watermark, key)
# Usage
key = os.urandom(16) # 128-bit key
watermark = "© 2024 Artist Name - All Rights Reserved"
embed_watermark("artwork.png", watermark, key, "watermarked_artwork.png")
extracted_watermark = extract_watermark("watermarked_artwork.png", key, len(encrypt_message(watermark, key)))
print("Extracted watermark:", extracted_watermark)
This example demonstrates a practical application of steganography for digital rights management. The watermark is encrypted before embedding, adding an extra layer of security.
Advanced Frequency Domain Steganography
For more robust steganography that can withstand some image processing, we can use frequency domain techniques. Here’s an improved version of the DCT (Discrete Cosine Transform) method using scipy
:
import numpy as np
from scipy.fftpack import dct, idct
from PIL import Image
def embed_dct(image_path, message, output_path):
# Load and prepare image
img = np.array(Image.open(image_path).convert('L')) / 255.0
# Apply DCT
dct_blocks = np.zeros(img.shape)
for i in range(0, img.shape[0], 8):
for j in range(0, img.shape[1], 8):
dct_blocks[i:i+8, j:j+8] = dct(dct(img[i:i+8, j:j+8].T, norm='ortho').T, norm='ortho')
# Prepare message
binary_message = ''.join(format(ord(char), '08b') for char in message + '\0')
# Embed message
embed_locations = [(1, 2), (2, 1), (2, 2), (3, 1)] # Mid-frequency coefficients
msg_index = 0
for i in range(0, dct_blocks.shape[0], 8):
for j in range(0, dct_blocks.shape[1], 8):
for loc in embed_locations:
if msg_index < len(binary_message):
dct_blocks[i+loc[0], j+loc[1]] = np.round(dct_blocks[i+loc[0], j+loc[1]] * 2) / 2
if binary_message[msg_index] == '1':
dct_blocks[i+loc[0], j+loc[1]] += 0.25
else:
dct_blocks[i+loc[0], j+loc[1]] -= 0.25
msg_index += 1
# Apply inverse DCT
stego_img = np.zeros(img.shape)
for i in range(0, img.shape[0], 8):
for j in range(0, img.shape[1], 8):
stego_img[i:i+8, j:j+8] = idct(idct(dct_blocks[i:i+8, j:j+8].T, norm='ortho').T, norm='ortho')
# Save stego image
Image.fromarray((stego_img * 255).clip(0, 255).astype('uint8')).save(output_path)
def extract_dct(image_path):
# Load stego image
stego_img = np.array(Image.open(image_path).convert('L')) / 255.0
# Apply DCT
dct_blocks = np.zeros(stego_img.shape)
for i in range(0, stego_img.shape[0], 8):
for j in range(0, stego_img.shape[1], 8):
dct_blocks[i:i+8, j:j+8] = dct(dct(stego_img[i:i+8, j:j+8].T, norm='ortho').T, norm='ortho')
# Extract message
embed_locations = [(1, 2), (2, 1), (2, 2), (3, 1)]
extracted_bits = []
for i in range(0, dct_blocks.shape[0], 8):
for j in range(0, dct_blocks.shape[1], 8):
for loc in embed_locations:
bit = '1' if (dct_blocks[i+loc[0], j+loc[1]] % 0.5) > 0.25 else '0'
extracted_bits.append(bit)
# Convert bits to message
extracted_message = ""
for i in range(0, len(extracted_bits), 8):
byte = ''.join(extracted_bits[i:i+8])
char = chr(int(byte, 2))
if char == '\0':
break
extracted_message += char
return extracted_message
# Usage
embed_dct("original_image.png", "Hidden with improved DCT", "dct_stego_image.png")
extracted_message = extract_dct("dct_stego_image.png")
print("Extracted message:", extracted_message)
This improved DCT method embeds the message in mid-frequency coefficients, making it more resistant to compression and some image processing operations.
Use Cases for Image Steganography
Digital Watermarking: As demonstrated in our real-life example, artists and photographers can use steganography to embed copyright information or ownership details into their work.
Secure Communication: Journalists or activists in restrictive environments can use image steganography to transmit sensitive information covertly.
Data Verification: Companies can embed checksums or digital signatures within product images for authenticity verification.
Medical Imaging: Patient information can be securely embedded within medical images, ensuring data privacy and easy access for authorized personnel.
Supply Chain Management: QR codes or tracking information can be hidden within product images for inventory and logistics purposes.
Conclusion
Image steganography offers powerful techniques for hiding data, with applications ranging from digital rights management to secure communication. The methods we’ve explored, from basic LSB to advanced DCT, demonstrate the versatility and potential of this field.
For further exploration, consider investigating:
- Error correction codes to improve robustness against image manipulation
- Adaptive steganography techniques that analyze image characteristics to determine optimal embedding locations
- Steganalysis methods to detect hidden data, and how to counter them
Remember, while steganography is a powerful tool, it must be used responsibly and ethically. Always ensure you have the necessary rights and permissions when working with images and data.
For more in-depth research on modern steganography techniques, refer to: