# Image Steganography Tool

This utility allows you to hide secret text messages inside an image file (Steganography) and retrieve them later. It uses the **Least Significant Bit (LSB)** technique, which modifies the last bit of the pixel color values. These changes are so subtle that they are invisible to the human eye.

**Modules Used:**
*   [[programming/python/modules/pillow-module|Pillow]]: To manipulate image pixels.
*   [[programming/python/modules/argparse-module|argparse]]: To handle command-line arguments.

## Installation

```bash
pip install Pillow
```

## The Code

Save this as `stego.py`.

```python
from PIL import Image
import argparse
import os
import sys

DELIMITER = "#####"

def text_to_bin(text):
    """Convert text to a binary string."""
    return ''.join(format(ord(char), '08b') for char in text)

def encode(image_path, secret_text, output_path):
    print(f"Encoding message into '{image_path}'...")
    img = Image.open(image_path)
    img = img.convert("RGB") # Ensure 3 channels
    pixels = img.load()
    
    # Add delimiter so we know when to stop decoding
    full_text = secret_text + DELIMITER
    binary_secret = text_to_bin(full_text)
    data_len = len(binary_secret)
    
    width, height = img.size
    
    # Check if image is large enough
    if data_len > width * height * 3:
        raise ValueError(f"Text is too long for this image. Max bits: {width*height*3}")
    
    data_index = 0
    
    for y in range(height):
        for x in range(width):
            r, g, b = pixels[x, y]
            
            # Modify LSB of Red, Green, and Blue channels
            # (val & ~1) clears the last bit, | int(bit) sets it
            if data_index < data_len:
                r = (r & ~1) | int(binary_secret[data_index])
                data_index += 1
            if data_index < data_len:
                g = (g & ~1) | int(binary_secret[data_index])
                data_index += 1
            if data_index < data_len:
                b = (b & ~1) | int(binary_secret[data_index])
                data_index += 1
            
            pixels[x, y] = (r, g, b)
            
            if data_index >= data_len:
                break
        if data_index >= data_len:
            break
            
    img.save(output_path)
    print(f"Success! Encoded image saved to '{output_path}'")

def decode(image_path):
    print(f"Decoding message from '{image_path}'...")
    img = Image.open(image_path)
    img = img.convert("RGB")
    pixels = img.load()
    
    binary_data = ""
    width, height = img.size
    
    for y in range(height):
        for x in range(width):
            r, g, b = pixels[x, y]
            
            # Extract LSB
            binary_data += str(r & 1)
            binary_data += str(g & 1)
            binary_data += str(b & 1)

    # Convert binary to text and look for delimiter
    decoded_text = ""
    for i in range(0, len(binary_data), 8):
        byte = binary_data[i:i+8]
        if len(byte) < 8: break
        
        decoded_text += chr(int(byte, 2))
        
        if decoded_text.endswith(DELIMITER):
            print("\n--- Hidden Message ---")
            print(decoded_text[:-len(DELIMITER)])
            print("----------------------")
            return

    print("No hidden message found (delimiter missing).")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Image Steganography Tool")
    subparsers = parser.add_subparsers(dest="command", required=True)

    # Encode Command
    enc_parser = subparsers.add_parser("encode", help="Hide text in an image")
    enc_parser.add_argument("image", help="Input image path")
    enc_parser.add_argument("text", help="Secret text to hide")
    enc_parser.add_argument("-o", "--output", default="secret.png", help="Output image path (must be PNG)")

    # Decode Command
    dec_parser = subparsers.add_parser("decode", help="Reveal text from an image")
    dec_parser.add_argument("image", help="Image with hidden text")

    args = parser.parse_args()

    if args.command == "encode":
        # PNG is required because JPEG compression destroys LSB data
        if not args.output.lower().endswith(".png"):
            print("Error: Output file must be a .png to prevent data loss.")
        else:
            encode(args.image, args.text, args.output)
    elif args.command == "decode":
        decode(args.image)
```

## Usage

```bash
# Hide a message
python stego.py encode landscape.jpg "The treasure is buried under the palm tree." -o secret_image.png

# Reveal the message
python stego.py decode secret_image.png
```

[[programming/python/python]]