diff --git a/src/EE/Stego/main.py b/src/EE/Stego/main.py index 2d2de09..038af18 100644 --- a/src/EE/Stego/main.py +++ b/src/EE/Stego/main.py @@ -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"}] diff --git a/src/EE/fourier/How_To_Use.md b/src/EE/fourier/How_To_Use.md new file mode 100644 index 0000000..6451f44 --- /dev/null +++ b/src/EE/fourier/How_To_Use.md @@ -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. + diff --git a/src/EE/fourier/Image_Watermarking_README.md b/src/EE/fourier/Image_Watermarking_README.md new file mode 100644 index 0000000..fa63ac4 --- /dev/null +++ b/src/EE/fourier/Image_Watermarking_README.md @@ -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. diff --git a/src/EE/fourier/fourier_transform_decoder.py b/src/EE/fourier/fourier_transform_decoder.py new file mode 100644 index 0000000..edc9cd8 --- /dev/null +++ b/src/EE/fourier/fourier_transform_decoder.py @@ -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() diff --git a/src/EE/fourier/main.py b/src/EE/fourier/main.py new file mode 100644 index 0000000..7ca7d41 --- /dev/null +++ b/src/EE/fourier/main.py @@ -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;} -> {'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) diff --git a/src/EE/fourier/watermark_decoder.py b/src/EE/fourier/watermark_decoder.py new file mode 100644 index 0000000..db3eb04 --- /dev/null +++ b/src/EE/fourier/watermark_decoder.py @@ -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() diff --git a/src/EE/fourier/watermark_encoder.py b/src/EE/fourier/watermark_encoder.py new file mode 100644 index 0000000..523c3a1 --- /dev/null +++ b/src/EE/fourier/watermark_encoder.py @@ -0,0 +1,38 @@ +import PIL.Image +import numpy as np +from PIL import Image + + +def embed_watermark(image_in, watermark_text): + imgarr = np.array(image_in) + watermark_data = ''.join(format(ord(char), '08b') + for char in watermark_text) + i = 0 + for x in range(imgarr.shape[0]): + for y in range(imgarr.shape[1]): + pixel = list(imgarr[x, y]) + for n in range(3): + if i < len(watermark_data): + pixel[n] = int(format(pixel[n], '08b')[ + :-1] + watermark_data[i], 2) + i += 1 + imgarr[x, y] = tuple(pixel) + # cv2.imwrite("watermarked_image.png", image) + return Image.fromarray(imgarr) + + # print("Watermark embedded and saved to watermarked_image.png.") + + +def main(image, watermark_text): + out_image = embed_watermark(image, watermark_text) + return out_image + +if __name__ == "__main__": + image_path = input('Image path for the original image: ') + text = input('Input text that you want to watermark: ') + image_obj = main(PIL.Image.open(image_path), text) + out_path = input('The output path for the image:') + image_obj.save(out_path) + print('For reminder, You will require the original image, the watermarked image '+\ + 'and the length of the watermarked text to get the text you watermarked.') + print(f"Your length of watermark is:{len(text)}")