Skip to content
10 changes: 2 additions & 8 deletions src/EE/Stego/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,10 @@ class Steganography(utils.LibraryBase): # noqa: E501

def __init__(self):
super().__init__()
self.enc_input = [{"field": "Encryption Method", 'options': [{"name": "No encryption", "id": "none"},
{"name": "AES-256", "id": "aes256"}],
'type': 'select', 'id': 'en/dec'},
{"field": "Message", 'type': 'text_large', 'id': 'msg'},
self.enc_input = [{"field": "Message", 'type': 'text_large', 'id': 'msg'},
{"field": "Secret(Required to decrypt the image)", 'type': 'text_small', 'id': 'passcode'},
{"field": "Image", 'type': 'image_button', 'id': 'img'}]
self.dec_input = [{"field": "Encryption Method", 'options': [{"name": "No encryption", "id": "none"},
{"name": "AES-256", "id": "aes256"}],
'type': 'select', 'id': 'en/dec'},
{"field": "Secret(Required to decrypt the image)", 'type': 'text_small', 'id': 'passcode'},
self.dec_input = [{"field": "Secret(Required to decrypt the image)", 'type': 'text_small', 'id': 'passcode'},
{"field": "Image", 'type': 'image_button', 'id': 'img'}]
self.enc_output = [{"type": "image_button", "id": "img_down"}]
self.dec_output = [{"type": "msg", "id": "msg"}]
Expand Down
60 changes: 60 additions & 0 deletions src/EE/fourier/How_To_Use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# User Guide: How to Watermark Your Digital Art or Photography Using Fourier Transform

## Introduction

Hello, dear artist or photographer! Want to protect your magnificent creations with some tech magic? You're in the right place! This guide will walk you through watermarking your images using a technique called Fourier Transform. This method embeds the watermark into the frequency domain of the image, making it difficult to remove without specialized knowledge. In simpler terms, it's a robust way to safeguard your work without affecting its visual quality.

---

## Requirements

- Python installed on your computer
- The encoder and decoder scripts
- The image you want to watermark (in `.png`, `.jpg`, or `.tiff` formats)

---

## Step-by-Step Instructions

### Watermarking Your Image (Encoding)

1. **Open the Terminal or Command Prompt**
- Navigate to the folder where the encoder script is located.

2. **Run the Encoder Script**
- Type `python encoder.py` and press Enter.

3. **Enter the Watermark Text**
- You'll be prompted to enter the text you want to use as a watermark. This could be your name, brand, or any other identifying info.

4. **Select the Image File**
- You'll be prompted to provide the path to the image file you want to watermark. Enter the full path to the image.

5. **Check for Success**
- If everything goes well, you'll see messages indicating that the watermark has been successfully embedded. The watermarked image will be saved in the same folder with the suffix `_watermarked`.

### Verifying the Watermark (Decoding)

1. **Open the Terminal or Command Prompt**
- Navigate to the folder where the decoder script is located.

2. **Run the Decoder Script**
- Type `python decoder.py` and press Enter.

3. **Enter the Original Watermark Text**
- You'll be prompted to enter the original text that was used for watermarking. Make sure to enter it exactly as you did during the encoding process.

4. **Select the Watermarked Image File**
- You'll be prompted to provide the path to the watermarked image file. Enter the full path to the image.

5. **Check for Success**
- If all goes well, the script will display the watermark text extracted from the image. It should match the text you originally entered.

---

## Final Thoughts

And there you have it! You've successfully watermarked your artwork using Fourier Transform methods. While this watermark is not visible, it's embedded in a technically complex way that makes it difficult to remove, providing an extra layer of security for your creations.

Remember, this is not a 100% foolproof method, but it adds a layer of protection that can deter unauthorized use of your work.

42 changes: 42 additions & 0 deletions src/EE/fourier/Image_Watermarking_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Image Watermarking using Fourier Transform

## Encoder

### Overview
The encoder program takes an image and a watermark text from the user. It then performs the following steps:
1. Converts the image to the frequency domain using Fourier Transform for each color channel (red, green, blue).
2. Embeds the watermark into the magnitude of the frequency domain.
3. Transforms the image back to the spatial domain and saves it.

### How to Use
1. Run the program.
2. Enter the watermark text when prompted.
3. Enter the path to the image file you want to watermark.

### Code Explanation
- `get_watermark_text()`: Gets watermark text from the user.
- `load_image()`: Loads an image from a file path given by the user.
- `fourier_transform_color()`: Performs Fourier Transform on each color channel.
- `embed_watermark_to_color_channel()`: Embeds the watermark into the magnitude of the frequency domain.
- `inverse_fourier_transform_color()`: Performs inverse Fourier Transform to get the image back to the spatial domain.
- `save_image()`: Saves the watermarked image.

---

## Decoder

### Overview
The decoder program extracts the watermark text from a watermarked image. It uses the frequency domain data saved during the encoding process.

### How to Use
1. Run the program.
2. Enter the watermark text that was used for encoding when prompted.
3. Enter the path to the watermarked image file.

### Code Explanation
- `load_image()`: Loads the watermarked image.
- `fourier_transform_color()`: Performs Fourier Transform on each color channel of the image.
- `extract_watermark()`: Extracts the watermark from the magnitude of the frequency domain.
- `main()`: Orchestrates the watermark extraction process.

The decoder does not read the watermark directly from the watermarked image. Instead, it loads the frequency domain data from a `.npy` file saved during the encoding process.
50 changes: 50 additions & 0 deletions src/EE/fourier/fourier_transform_decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import numpy as np
from PIL import Image


def load_image():
image_path = input("Enter the path to the watermarked image file: ")
try:
return Image.open(image_path)
except FileNotFoundError:
print("Image not found. Exiting.")
exit()


def fourier_transform_color(image):
image_array = np.array(image)
red, green, blue = image_array[:, :, 0], image_array[:, :, 1], image_array[:, :, 2]
red_freq = np.fft.fft2(red)
green_freq = np.fft.fft2(green)
blue_freq = np.fft.fft2(blue)
return red_freq, green_freq, blue_freq


def extract_watermark(frequency_domain, watermark_length):
magnitude = np.abs(frequency_domain)
watermark_data = magnitude[0, 0:watermark_length]
print(f"Debug: Extracted data from magnitude: {watermark_data}")
watermark_text = "".join([chr(int(round(x))) for x in watermark_data])
return watermark_text


def main():
watermark_text = input("Enter the watermark text that was used for encoding: ")
image = load_image()
watermark_length = len(watermark_text)

try:
loaded_red_freq_watermarked = np.load(
f"red_freq_watermarked_{watermark_text}.npy"
)
watermark_text_red = extract_watermark(
loaded_red_freq_watermarked, watermark_length
)
print(f"Extracted watermark text from the red channel is: {watermark_text_red}")
except FileNotFoundError:
print("Frequency domain data not found. Exiting.")
exit()


if __name__ == "__main__":
main()
168 changes: 168 additions & 0 deletions src/EE/fourier/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import asyncio
import random

import numpy as np
import PIL.Image
from PIL import Image

try:
from .. import utils
except ImportError:
from EE import utils


def get_watermark_text():
"""Basic CLI prompt to enter text, Deprecated."""
return input("Enter the watermark text: ")


def load_image():
"""Load Image object, Deprecated."""
image_path = input("Enter the path to the image file: ")
try:
return Image.open(image_path)
except FileNotFoundError:
print("Image not found. Please check the path and try again.")
return None


def shift_to_center(frequency_domain): # noqa: D103
return np.fft.fftshift(frequency_domain)


def shift_to_corners(frequency_domain): # noqa: D103
return np.fft.ifftshift(frequency_domain)


def fourier_transform_color(image): # noqa: D103
image_array = np.array(image)
red, green, blue = image_array[:, :, 0], image_array[:, :, 1], image_array[:, :, 2]
red_freq = shift_to_center(np.fft.fft2(red))
green_freq = shift_to_center(np.fft.fft2(green))
blue_freq = shift_to_center(np.fft.fft2(blue))
return red_freq, green_freq, blue_freq


def embed_watermark_to_color_channel(magnitude, phase, watermark_array):
"""Encode watermark by colour channel."""
# Embed watermark into magnitude
magnitude[0:1, 0: watermark_array.shape[1]] = watermark_array

# Print debug information
# print("Debug: Portion of magnitude where watermark is embedded:")
# print(magnitude[0:1, 0:watermark_array.shape[1]])
print(
f"Debug: Portion of magnitude where watermark is embedded:\n{magnitude[0:1, 0:watermark_array.shape[1]]}"
)
frequency_domain_watermarked = magnitude * np.exp(1j * phase)

return frequency_domain_watermarked


def inverse_fourier_transform_color(red_freq, green_freq, blue_freq): # noqa: D103
red = np.fft.ifft2(shift_to_corners(red_freq))
green = np.fft.ifft2(shift_to_corners(green_freq))
blue = np.fft.ifft2(shift_to_corners(blue_freq))
color_image_array = np.stack([np.real(red), np.real(green), np.real(blue)], axis=2)
color_image_array = np.uint8(color_image_array)
return Image.fromarray(color_image_array, "RGB")


def save_image(image, save_path):
"""Save PIL.Image.Image by path."""
try:
image.save(save_path)
print(f"Watermarked image saved as '{save_path}'")

except Exception as e:
print(f"Failed to save image: {e}")


def encode_fourier(watermark_text, img_path, save_path):
"""Code to dncode watermark to the image."""
if not isinstance(img_path, PIL.Image.Image):
image = Image.open(img_path)
else:
image = img_path

if image:
print(f"Successfully loaded image. Watermark text is: {watermark_text}")

red_freq, green_freq, blue_freq = fourier_transform_color(image)
print("Successfully loaded image to frequency domain.")

watermark_array = np.array([[ord(char) for char in watermark_text]])

# Separate magnitude and phase for each color channel
red_magnitude, red_phase = np.abs(red_freq), np.angle(red_freq)
green_magnitude, green_phase = np.abs(green_freq), np.angle(green_freq)
blue_magnitude, blue_phase = np.abs(blue_freq), np.angle(blue_freq)

# Embed the watermark and get the watermarked frequency domain
red_freq_watermarked = embed_watermark_to_color_channel(
red_magnitude, red_phase, watermark_array
)
green_freq_watermarked = embed_watermark_to_color_channel(
green_magnitude, green_phase, watermark_array
)
blue_freq_watermarked = embed_watermark_to_color_channel(
blue_magnitude, blue_phase, watermark_array
)
# Save the frequency domain data to disk
np.save(f"red_freq_watermarked_{watermark_text}.npy", red_freq_watermarked)

print("Successfully embedded watermark into frequency domain.")
print(
f"Debug: Red frequency domain after watermark: {np.abs(red_freq_watermarked)[0, 0:10]}"
)

watermarked_image = inverse_fourier_transform_color(
red_freq_watermarked, green_freq_watermarked, blue_freq_watermarked
)
print("Successfully transformed image back to spatial domain.")

save_image(watermarked_image, save_path)

loaded_image = Image.open(save_path)
loaded_red_freq, _, _ = fourier_transform_color(loaded_image)
print(f"Debug: Loaded red frequency domain: {np.abs(loaded_red_freq)[0, 0:10]}")
else:
print("Failed to load image. Exiting.")


class Fourier(utils.LibraryBase):
"""Class to run Fourier Library"""

def __init__(self):
super().__init__()
self.enc_input = [
{"field": "Watermark content", 'type': 'text_large', 'id': 'msg'},
{"field": "Image", 'type': 'image_button', 'id': 'img'}]
self.dec_input = [
{"field": "Image", 'type': 'image_button', 'id': 'img'},
{"field": "Length of watermark content", 'type': 'text_small', 'id': 'len'}]
self.enc_output = [{"type": "image_button", "id": "img_down"}, {'type': 'int', 'id': 'len'}]
self.dec_output = [{"type": "msg", "id": "msg"}]

async def routine(self, func_mode: utils.mode, data_input: dict):
"""Routine of running Fourier watermark."""
# If both code have something in common, the proprtion of those code is here
if func_mode == utils.MODE_ENCRYPTION:
data = data_input['msg']
pathname_gen = ''.join(random.choices('0123456789abcdef', k=32)) + '.tiff'
encode_fourier(data, data_input['img'], pathname_gen)
return {'img_down': pathname_gen, 'len': len(data)}

if func_mode == utils.MODE_DECRYPTION: # {'len':'4', 'img;<object: PIL.Image.Image>} -> {'msg':'test'}
# data = fourier_transform_decoder.main(data_input['msg'],data_input["img"] )
# return {'img_down': name}
# disconnected code
pass


if __name__ == "__main__":
lib = Fourier()
out_data = asyncio.run(lib.routine(utils.MODE_ENCRYPTION, {'msg': 'test', 'img': PIL.Image.open('img.png')}))
out_data_2 = asyncio.run(
lib.routine(utils.MODE_DECRYPTION, {'img': PIL.Image.open(out_data['img_down']), 'len': str(out_data['len'])}))
print(out_data_2)
35 changes: 35 additions & 0 deletions src/EE/fourier/watermark_decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import numpy as np


def extract_watermark(watermarked_image, original_image, watermark_length):
watermarked_image = np.array(watermarked_image)
original_image = np.array(original_image)
if watermarked_image is None or original_image is None:
print("Could not open one of the images. Check the paths.")
return None

extracted_data = []
for x in range(watermarked_image.shape[0]):
for y in range(watermarked_image.shape[1]):
pixel = list(watermarked_image[x, y])
for n in range(3):
extracted_data.append(bin(pixel[n])[-1])

# Validate and convert the binary string to characters
extracted_watermark = ''.join(
[chr(int(''.join([str(bit) for bit in extracted_data[i:i + 8]]), 2))
for i in range(0, watermark_length * 8, 8) if ''.join(extracted_data[i:i + 8]).isdigit()]
)

return extracted_watermark

def main():
watermarked_image_path = input("Enter the path to the watermarked image file: ")
original_image_path = input("Enter the path to the original image file: ")
watermark_length = int(input("Enter the length of the watermark text: "))

extracted_watermark = extract_watermark(watermarked_image_path, original_image_path, watermark_length)
print(f"Extracted watermark is: {extracted_watermark}")

if __name__ == "__main__":
main()
Loading