Upload 5 files
Browse files- app.py +27 -0
- detection.py +75 -0
- picchange.py +163 -0
- stegnography.py +104 -0
- watermark.py +112 -0
app.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from picchange import img_cryptography
|
| 3 |
+
from stegnography import steganography_function
|
| 4 |
+
from detection import steganography_detection
|
| 5 |
+
|
| 6 |
+
# Sidebar Title
|
| 7 |
+
st.sidebar.title("IMAGENIAS")
|
| 8 |
+
st.sidebar.write("Choose a feature to proceed:")
|
| 9 |
+
|
| 10 |
+
# Add buttons in rows within the sidebar
|
| 11 |
+
crypto_button = st.sidebar.button("Crypto")
|
| 12 |
+
stego_button = st.sidebar.button("Steganography")
|
| 13 |
+
detect_button = st.sidebar.button("Detection")
|
| 14 |
+
|
| 15 |
+
# Main App Title
|
| 16 |
+
st.title("IMAGNIAS: Image Processing Toolkit")
|
| 17 |
+
|
| 18 |
+
# Trigger the corresponding function based on the clicked button
|
| 19 |
+
if crypto_button:
|
| 20 |
+
st.write("### Crypto Functionality")
|
| 21 |
+
img_cryptography()
|
| 22 |
+
elif stego_button:
|
| 23 |
+
st.write("### Steganography Functionality")
|
| 24 |
+
steganography_function()
|
| 25 |
+
else:
|
| 26 |
+
steganography_detection()
|
| 27 |
+
|
detection.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
def steganography_detection():
|
| 6 |
+
def lsb_analysis(image):
|
| 7 |
+
"""
|
| 8 |
+
Analyzes the Least Significant Bits (LSB) of the image pixels
|
| 9 |
+
and checks for irregularities that might indicate hidden data.
|
| 10 |
+
"""
|
| 11 |
+
img = Image.open(image)
|
| 12 |
+
img = img.convert('RGB') # Ensure RGB mode
|
| 13 |
+
pixels = np.array(img) # Convert image to numpy array
|
| 14 |
+
|
| 15 |
+
if len(pixels.shape) != 3 or pixels.shape[2] != 3:
|
| 16 |
+
raise ValueError("Unsupported image format. Please use RGB images.")
|
| 17 |
+
|
| 18 |
+
height, width, _ = pixels.shape
|
| 19 |
+
|
| 20 |
+
lsb_count = [0, 0] # Count of 0s and 1s in the LSB
|
| 21 |
+
lsb_distribution = []
|
| 22 |
+
|
| 23 |
+
# Loop through each pixel and channel
|
| 24 |
+
for y in range(height):
|
| 25 |
+
for x in range(width):
|
| 26 |
+
pixel = pixels[y, x]
|
| 27 |
+
for channel in range(3): # Process R, G, B channels
|
| 28 |
+
lsb = pixel[channel] & 1
|
| 29 |
+
lsb_count[lsb] += 1
|
| 30 |
+
lsb_distribution.append(lsb)
|
| 31 |
+
|
| 32 |
+
total_lsb = sum(lsb_count)
|
| 33 |
+
if total_lsb == 0:
|
| 34 |
+
return None, None
|
| 35 |
+
|
| 36 |
+
# Compute percentage distribution
|
| 37 |
+
lsb_percentage = [count / total_lsb * 100 for count in lsb_count]
|
| 38 |
+
return lsb_percentage, lsb_distribution
|
| 39 |
+
|
| 40 |
+
st.title("Steganography Detection Tool")
|
| 41 |
+
st.subheader("Analyze images for hidden messages based on LSB irregularities")
|
| 42 |
+
|
| 43 |
+
uploaded_image = st.file_uploader("Upload an image to analyze", type=["png", "jpg", "jpeg"])
|
| 44 |
+
analyze_button = st.button("Analyze Image")
|
| 45 |
+
|
| 46 |
+
if analyze_button:
|
| 47 |
+
if uploaded_image:
|
| 48 |
+
try:
|
| 49 |
+
st.image(uploaded_image, caption="Uploaded Image", use_column_width=True)
|
| 50 |
+
lsb_percentage, lsb_distribution = lsb_analysis(uploaded_image)
|
| 51 |
+
|
| 52 |
+
if lsb_percentage is None:
|
| 53 |
+
st.error("Unable to analyze the image.")
|
| 54 |
+
else:
|
| 55 |
+
st.write("### LSB Distribution Analysis")
|
| 56 |
+
st.write(f"**Percentage of LSBs:**\n- 0s: {lsb_percentage[0]:.2f}%\n- 1s: {lsb_percentage[1]:.2f}%")
|
| 57 |
+
|
| 58 |
+
# Visualization
|
| 59 |
+
st.write("### Visualization of LSB Distribution")
|
| 60 |
+
st.bar_chart({
|
| 61 |
+
"LSB Values": ["0s", "1s"],
|
| 62 |
+
"Percentage": lsb_percentage
|
| 63 |
+
})
|
| 64 |
+
|
| 65 |
+
# Detection threshold
|
| 66 |
+
threshold = 50 # Typically, natural images have an approximately equal distribution of 0s and 1s
|
| 67 |
+
if abs(lsb_percentage[0] - lsb_percentage[1]) > 5:
|
| 68 |
+
st.warning("The image shows irregular LSB distribution, suggesting possible steganographic modification.")
|
| 69 |
+
else:
|
| 70 |
+
st.success("The LSB distribution appears normal, with no clear indication of hidden data.")
|
| 71 |
+
|
| 72 |
+
except Exception as e:
|
| 73 |
+
st.error(f"An error occurred while analyzing the image: {e}")
|
| 74 |
+
else:
|
| 75 |
+
st.error("Please upload an image for analysis.")
|
picchange.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import numpy as np
|
| 4 |
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
| 5 |
+
from cryptography.hazmat.backends import default_backend
|
| 6 |
+
from cryptography.hazmat.primitives import padding
|
| 7 |
+
from io import BytesIO
|
| 8 |
+
def img_cryptography():
|
| 9 |
+
|
| 10 |
+
# Function to add padding to the image bytes
|
| 11 |
+
def pad_data(data):
|
| 12 |
+
try:
|
| 13 |
+
padder = padding.PKCS7(128).padder() # AES block size is 128 bits (16 bytes)
|
| 14 |
+
padded_data = padder.update(data) + padder.finalize()
|
| 15 |
+
return padded_data
|
| 16 |
+
except Exception as e:
|
| 17 |
+
raise ValueError(f"Padding Error: {e}")
|
| 18 |
+
|
| 19 |
+
# Function to remove padding after decryption
|
| 20 |
+
def unpad_data(data):
|
| 21 |
+
try:
|
| 22 |
+
unpadder = padding.PKCS7(128).unpadder()
|
| 23 |
+
unpadded_data = unpadder.update(data) + unpadder.finalize()
|
| 24 |
+
return unpadded_data
|
| 25 |
+
except Exception as e:
|
| 26 |
+
raise ValueError(f"Unpadding Error: {e}")
|
| 27 |
+
|
| 28 |
+
# Function to encrypt image using AES with a user-provided key
|
| 29 |
+
def encrypt_image(image_bytes, key):
|
| 30 |
+
try:
|
| 31 |
+
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
|
| 32 |
+
encryptor = cipher.encryptor()
|
| 33 |
+
|
| 34 |
+
# Pad the image bytes before encryption
|
| 35 |
+
padded_bytes = pad_data(image_bytes)
|
| 36 |
+
encrypted_bytes = encryptor.update(padded_bytes) + encryptor.finalize()
|
| 37 |
+
return encrypted_bytes
|
| 38 |
+
except Exception as e:
|
| 39 |
+
raise ValueError(f"Encryption Error: {e}")
|
| 40 |
+
|
| 41 |
+
# Function to decrypt image using AES with a user-provided key
|
| 42 |
+
def decrypt_image(encrypted_bytes, key):
|
| 43 |
+
try:
|
| 44 |
+
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
|
| 45 |
+
decryptor = cipher.decryptor()
|
| 46 |
+
decrypted_bytes = decryptor.update(encrypted_bytes) + decryptor.finalize()
|
| 47 |
+
|
| 48 |
+
# Remove padding after decryption
|
| 49 |
+
unpadded_bytes = unpad_data(decrypted_bytes)
|
| 50 |
+
return unpadded_bytes
|
| 51 |
+
except ValueError as e:
|
| 52 |
+
raise ValueError(f"Decryption Error: Invalid padding or incorrect key - {e}")
|
| 53 |
+
except Exception as e:
|
| 54 |
+
raise ValueError(f"Decryption Error: {e}")
|
| 55 |
+
|
| 56 |
+
# Streamlit user interface
|
| 57 |
+
st.title("Image Encryption and Decryption using AES")
|
| 58 |
+
|
| 59 |
+
# Option for selecting key length
|
| 60 |
+
key_length = st.selectbox("Choose AES key length:", [16, 24, 32])
|
| 61 |
+
|
| 62 |
+
# Function to generate the AES key based on user input
|
| 63 |
+
def generate_key(key_input, length):
|
| 64 |
+
try:
|
| 65 |
+
key = key_input.encode('utf-8')
|
| 66 |
+
if len(key) < length:
|
| 67 |
+
# Pad the key with null bytes if it's shorter than the selected length
|
| 68 |
+
key = key.ljust(length, b'\0')
|
| 69 |
+
elif len(key) > length:
|
| 70 |
+
# Truncate the key if it's longer than the selected length
|
| 71 |
+
key = key[:length]
|
| 72 |
+
return key
|
| 73 |
+
except Exception as e:
|
| 74 |
+
raise ValueError(f"Key Generation Error: {e}")
|
| 75 |
+
|
| 76 |
+
# Input for AES Key
|
| 77 |
+
key_input = st.text_input(f"Enter AES encryption key (up to {key_length} characters):", type="password")
|
| 78 |
+
|
| 79 |
+
# Ensure the key is of the correct length after processing
|
| 80 |
+
if key_input and key_length:
|
| 81 |
+
key = generate_key(key_input, key_length)
|
| 82 |
+
else:
|
| 83 |
+
st.warning(f"Please enter a key of length {key_length} characters for encryption and decryption.")
|
| 84 |
+
key = None
|
| 85 |
+
|
| 86 |
+
# Create two columns for encryption and decryption sections
|
| 87 |
+
col1, col2 = st.tabs(["Encryption","Decryption"])
|
| 88 |
+
|
| 89 |
+
# ENCRYPTION SECTION (Left Column)
|
| 90 |
+
with col1:
|
| 91 |
+
st.header("Encryption")
|
| 92 |
+
|
| 93 |
+
# Option to upload image for encryption
|
| 94 |
+
uploaded_file = st.file_uploader("Choose an image to encrypt...", type=["jpg", "jpeg", "png"])
|
| 95 |
+
|
| 96 |
+
if uploaded_file is not None and key:
|
| 97 |
+
# Display the original image
|
| 98 |
+
image = Image.open(uploaded_file)
|
| 99 |
+
st.image(image, caption="Original Image", use_column_width=True)
|
| 100 |
+
|
| 101 |
+
if st.button("Encrypt"):
|
| 102 |
+
# Convert image to bytes for encryption
|
| 103 |
+
image_bytes = BytesIO()
|
| 104 |
+
image.save(image_bytes, format="PNG")
|
| 105 |
+
image_bytes = image_bytes.getvalue()
|
| 106 |
+
|
| 107 |
+
# Encrypt the image bytes
|
| 108 |
+
encrypted_bytes = encrypt_image(image_bytes, key)
|
| 109 |
+
|
| 110 |
+
# Save the encrypted bytes as a .bin file
|
| 111 |
+
encrypted_bin_file = "encrypted_image.bin"
|
| 112 |
+
with open(encrypted_bin_file, "wb") as f:
|
| 113 |
+
f.write(encrypted_bytes)
|
| 114 |
+
|
| 115 |
+
# To visualize encrypted data, truncate or pad it to match the original size
|
| 116 |
+
# Use numpy to create a grayscale view of the encrypted data
|
| 117 |
+
encrypted_array = np.frombuffer(encrypted_bytes, dtype=np.uint8)
|
| 118 |
+
padded_array = np.zeros(image.size[0] * image.size[1], dtype=np.uint8)
|
| 119 |
+
padded_array[:len(encrypted_array)] = encrypted_array[:len(padded_array)]
|
| 120 |
+
|
| 121 |
+
# Reshape to match image dimensions and visualize as a grayscale image
|
| 122 |
+
encrypted_image_array = padded_array.reshape(image.size[1], image.size[0])
|
| 123 |
+
encrypted_image = Image.fromarray(encrypted_image_array, mode="L")
|
| 124 |
+
encrypted_image_file = "encrypted_image.png"
|
| 125 |
+
encrypted_image.save(encrypted_image_file, format="PNG")
|
| 126 |
+
|
| 127 |
+
# Success message and download buttons
|
| 128 |
+
st.success("Image Encrypted Successfully!")
|
| 129 |
+
st.download_button(label="Download Encrypted BIN File", data=encrypted_bytes, file_name="encrypted_image.bin")
|
| 130 |
+
st.download_button(label="Download Encrypted PNG File", data=open(encrypted_image_file, "rb").read(), file_name="encrypted_image.png")
|
| 131 |
+
st.image(encrypted_image, caption="Encrypted Image (PNG)", use_column_width=True)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
# DECRYPTION SECTION (Right Column)
|
| 135 |
+
with col2:
|
| 136 |
+
st.header("Decryption")
|
| 137 |
+
|
| 138 |
+
# Option to upload encrypted file for decryption (.bin only)
|
| 139 |
+
encrypted_file = st.file_uploader("Upload an encrypted file to decrypt (.bin only)...", type=["bin"])
|
| 140 |
+
|
| 141 |
+
if encrypted_file is not None and key:
|
| 142 |
+
try:
|
| 143 |
+
# Read encrypted binary data
|
| 144 |
+
encrypted_bytes = encrypted_file.read()
|
| 145 |
+
|
| 146 |
+
# Decrypt the image
|
| 147 |
+
decrypted_bytes = decrypt_image(encrypted_bytes, key)
|
| 148 |
+
|
| 149 |
+
# Convert decrypted bytes back to image
|
| 150 |
+
decrypted_image = Image.open(BytesIO(decrypted_bytes))
|
| 151 |
+
st.image(decrypted_image, caption="Decrypted Image", use_column_width=True)
|
| 152 |
+
|
| 153 |
+
# Save decrypted image as JPG
|
| 154 |
+
decrypted_image_file = "decrypted_image.jpg"
|
| 155 |
+
decrypted_image.save(decrypted_image_file, format="JPEG")
|
| 156 |
+
|
| 157 |
+
st.success("Image Decrypted Successfully!")
|
| 158 |
+
st.download_button(label="Download Decrypted Image (JPG)", data=open(decrypted_image_file, "rb").read(), file_name="decrypted_image.jpg")
|
| 159 |
+
|
| 160 |
+
except ValueError as e:
|
| 161 |
+
st.error(f"Error in decryption: {e}. This may indicate mismatched padding or incorrect decryption keys.")
|
| 162 |
+
except Exception as e:
|
| 163 |
+
st.error(f"Unexpected error: {e}")
|
stegnography.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import io
|
| 4 |
+
def steganography_function():
|
| 5 |
+
# Steganography functions
|
| 6 |
+
def encode_image(input_image, secret_message):
|
| 7 |
+
"""Encodes a secret message into an image."""
|
| 8 |
+
img = Image.open(input_image)
|
| 9 |
+
encoded = img.copy()
|
| 10 |
+
width, height = img.size
|
| 11 |
+
pixels = encoded.load()
|
| 12 |
+
|
| 13 |
+
# Convert the message into binary and add a delimiter
|
| 14 |
+
binary_message = ''.join(format(ord(char), '08b') for char in secret_message) + '1111111111111110'
|
| 15 |
+
data_index = 0
|
| 16 |
+
|
| 17 |
+
for y in range(height):
|
| 18 |
+
for x in range(width):
|
| 19 |
+
if data_index < len(binary_message):
|
| 20 |
+
pixel = list(pixels[x, y])
|
| 21 |
+
# Modify the LSB of each RGB value
|
| 22 |
+
for i in range(3): # Process R, G, B channels
|
| 23 |
+
if data_index < len(binary_message):
|
| 24 |
+
pixel[i] = (pixel[i] & ~1) | int(binary_message[data_index])
|
| 25 |
+
data_index += 1
|
| 26 |
+
pixels[x, y] = tuple(pixel)
|
| 27 |
+
|
| 28 |
+
output = io.BytesIO()
|
| 29 |
+
encoded.save(output, format='PNG')
|
| 30 |
+
output.seek(0)
|
| 31 |
+
return output
|
| 32 |
+
|
| 33 |
+
def decode_image(encoded_image):
|
| 34 |
+
"""Decodes a secret message from an image."""
|
| 35 |
+
img = Image.open(encoded_image)
|
| 36 |
+
width, height = img.size
|
| 37 |
+
pixels = img.load()
|
| 38 |
+
|
| 39 |
+
binary_message = ""
|
| 40 |
+
for y in range(height):
|
| 41 |
+
for x in range(width):
|
| 42 |
+
pixel = pixels[x, y]
|
| 43 |
+
for i in range(3): # Process R, G, B channels
|
| 44 |
+
binary_message += str(pixel[i] & 1)
|
| 45 |
+
|
| 46 |
+
# Split binary string into 8-bit chunks
|
| 47 |
+
message = ""
|
| 48 |
+
for i in range(0, len(binary_message), 8):
|
| 49 |
+
byte = binary_message[i:i+8]
|
| 50 |
+
if byte == '11111110': # Stop at the delimiter
|
| 51 |
+
break
|
| 52 |
+
message += chr(int(byte, 2))
|
| 53 |
+
|
| 54 |
+
return message
|
| 55 |
+
|
| 56 |
+
# Streamlit App
|
| 57 |
+
st.title("Steganography Tool")
|
| 58 |
+
st.subheader("Hide and retrieve secret messages in images")
|
| 59 |
+
|
| 60 |
+
# Tabs for Encode and Decode
|
| 61 |
+
tab1, tab2 = st.tabs(["🔒 Encode Message", "🔓 Decode Message"])
|
| 62 |
+
|
| 63 |
+
with tab1:
|
| 64 |
+
st.header("Encode a Secret Message")
|
| 65 |
+
uploaded_image = st.file_uploader("Upload an image to encode", type=["png", "jpg", "jpeg"])
|
| 66 |
+
if uploaded_image:
|
| 67 |
+
st.image(uploaded_image, caption="Selected Image for Encoding", use_column_width=True)
|
| 68 |
+
secret_message = st.text_area("Enter the secret message to hide")
|
| 69 |
+
encode_button = st.button("Apply Encoding")
|
| 70 |
+
|
| 71 |
+
if encode_button:
|
| 72 |
+
if uploaded_image and secret_message:
|
| 73 |
+
try:
|
| 74 |
+
encoded_image = encode_image(uploaded_image, secret_message)
|
| 75 |
+
st.success("Message encoded successfully!")
|
| 76 |
+
st.image(encoded_image, caption="Encoded Image", use_column_width=True)
|
| 77 |
+
st.download_button(
|
| 78 |
+
label="Download Encoded Image",
|
| 79 |
+
data=encoded_image,
|
| 80 |
+
file_name="encoded_image.png",
|
| 81 |
+
mime="image/png"
|
| 82 |
+
)
|
| 83 |
+
except Exception as e:
|
| 84 |
+
st.error(f"An error occurred: {e}")
|
| 85 |
+
else:
|
| 86 |
+
st.error("Please upload an image and enter a secret message.")
|
| 87 |
+
|
| 88 |
+
with tab2:
|
| 89 |
+
st.header("Decode a Secret Message")
|
| 90 |
+
encoded_image = st.file_uploader("Upload an image to decode", type=["png", "jpg", "jpeg"], key="decode")
|
| 91 |
+
if encoded_image:
|
| 92 |
+
st.image(encoded_image, caption="Selected Image for Decoding", use_column_width=True)
|
| 93 |
+
decode_button = st.button("Apply Decoding")
|
| 94 |
+
|
| 95 |
+
if decode_button:
|
| 96 |
+
if encoded_image:
|
| 97 |
+
try:
|
| 98 |
+
decoded_message = decode_image(encoded_image)
|
| 99 |
+
st.success("Message decoded successfully!")
|
| 100 |
+
st.text_area("Decoded Message", decoded_message, height=100)
|
| 101 |
+
except Exception as e:
|
| 102 |
+
st.error(f"An error occurred: {e}")
|
| 103 |
+
else:
|
| 104 |
+
st.error("Please upload an image to decode.")
|
watermark.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 3 |
+
import io
|
| 4 |
+
import numpy as np
|
| 5 |
+
def watermark_():
|
| 6 |
+
# Function to create a visible watermark on an image
|
| 7 |
+
def add_visible_watermark(image, watermark_text, size, opacity):
|
| 8 |
+
width, height = image.size
|
| 9 |
+
font_size = int(size)
|
| 10 |
+
try:
|
| 11 |
+
font = ImageFont.truetype("arial.ttf", font_size)
|
| 12 |
+
except IOError:
|
| 13 |
+
font = ImageFont.load_default()
|
| 14 |
+
|
| 15 |
+
# Create watermark layer
|
| 16 |
+
# Create watermark layer
|
| 17 |
+
watermark_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))
|
| 18 |
+
watermark_draw = ImageDraw.Draw(watermark_layer)
|
| 19 |
+
|
| 20 |
+
# Calculate center position
|
| 21 |
+
text_width, text_height = watermark_draw.textbbox((0, 0), watermark_text, font=font)[2:]
|
| 22 |
+
x = (width - text_width) / 2
|
| 23 |
+
y = (height - text_height) / 2
|
| 24 |
+
|
| 25 |
+
# Add shadow
|
| 26 |
+
shadow_color = (0, 0, 0, int(opacity * 0.3))
|
| 27 |
+
watermark_draw.text((x + 3, y + 3), watermark_text, font=font, fill=shadow_color)
|
| 28 |
+
|
| 29 |
+
# Add main watermark in a slant line
|
| 30 |
+
watermark_color = (255, 255, 255, int(opacity))
|
| 31 |
+
for i in range(-width, width, text_width + 20):
|
| 32 |
+
watermark_draw.text((x + i, y + i), watermark_text, font=font, fill=watermark_color)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# Combine the watermark layer with the original image
|
| 36 |
+
combined_image = Image.alpha_composite(image.convert("RGBA"), watermark_layer).convert("RGB")
|
| 37 |
+
return combined_image
|
| 38 |
+
|
| 39 |
+
# Function to embed an invisible watermark in an image
|
| 40 |
+
def add_invisible_watermark(image, watermark_text):
|
| 41 |
+
binary_watermark = ''.join(format(ord(char), '08b') for char in watermark_text)
|
| 42 |
+
img_array = np.array(image)
|
| 43 |
+
flat_image = img_array.flatten()
|
| 44 |
+
|
| 45 |
+
for i in range(len(binary_watermark)):
|
| 46 |
+
flat_image[i] = flat_image[i] & ~1 | int(binary_watermark[i])
|
| 47 |
+
|
| 48 |
+
watermarked_image = flat_image.reshape(img_array.shape)
|
| 49 |
+
return Image.fromarray(watermarked_image.astype(np.uint8))
|
| 50 |
+
|
| 51 |
+
# Function to detect an invisible watermark in an image
|
| 52 |
+
def detect_invisible_watermark(image, watermark_length):
|
| 53 |
+
img_array = np.array(image)
|
| 54 |
+
flat_image = img_array.flatten()
|
| 55 |
+
binary_watermark = ''.join(str(flat_image[i] & 1) for i in range(watermark_length * 8))
|
| 56 |
+
watermark = ''.join(chr(int(binary_watermark[i:i + 8], 2)) for i in range(0, len(binary_watermark), 8))
|
| 57 |
+
return watermark
|
| 58 |
+
|
| 59 |
+
# Streamlit UI
|
| 60 |
+
def main():
|
| 61 |
+
st.title("Visible and Invisible Watermarking Tool")
|
| 62 |
+
|
| 63 |
+
col1, col2 = st.tabs(["Visible","Invisible"])
|
| 64 |
+
|
| 65 |
+
with col1:
|
| 66 |
+
st.header("Visible Watermark")
|
| 67 |
+
watermark_text = st.text_input("Enter Watermark Text", key="visible_text")
|
| 68 |
+
|
| 69 |
+
uploaded_file = st.file_uploader("Upload an Image", type=["png", "jpg", "jpeg"], key="visible_upload")
|
| 70 |
+
|
| 71 |
+
if uploaded_file and (watermark_text):
|
| 72 |
+
image = Image.open(uploaded_file).convert("RGB")
|
| 73 |
+
|
| 74 |
+
size = st.slider("Select Font Size", 10, 200, 50, key="font_size")
|
| 75 |
+
opacity = st.slider("Select Opacity", 0, 255, 128, key="opacity")
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
watermarked_image = add_visible_watermark(
|
| 79 |
+
image, watermark_text, size, opacity
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
st.image(watermarked_image, caption="Watermarked Image")
|
| 83 |
+
buffer = io.BytesIO()
|
| 84 |
+
watermarked_image.save(buffer, format="PNG")
|
| 85 |
+
st.download_button("Download Watermarked Image", buffer.getvalue(), "visible_watermarked_image.png")
|
| 86 |
+
|
| 87 |
+
with col2:
|
| 88 |
+
st.header("Invisible Watermark")
|
| 89 |
+
|
| 90 |
+
watermark_text = st.text_input("Enter Invisible Watermark Text", key="invisible_text")
|
| 91 |
+
uploaded_file = st.file_uploader("Upload an Image", type=["png", "jpg", "jpeg"], key="invisible_upload")
|
| 92 |
+
|
| 93 |
+
if uploaded_file and watermark_text:
|
| 94 |
+
image = Image.open(uploaded_file).convert("RGB")
|
| 95 |
+
watermarked_image = add_invisible_watermark(image, watermark_text)
|
| 96 |
+
|
| 97 |
+
st.image(watermarked_image, caption="Image with Invisible Watermark")
|
| 98 |
+
buffer = io.BytesIO()
|
| 99 |
+
watermarked_image.save(buffer, format="PNG")
|
| 100 |
+
st.download_button("Download Image with Invisible Watermark", buffer.getvalue(), "invisible_watermarked_image.png")
|
| 101 |
+
|
| 102 |
+
if st.checkbox("Check Invisible Watermark"):
|
| 103 |
+
uploaded_file = st.file_uploader("Upload an Image to Check Watermark", type=["png", "jpg", "jpeg"], key="check_upload")
|
| 104 |
+
watermark_length = st.number_input("Enter Watermark Length", min_value=1, step=1, key="watermark_length")
|
| 105 |
+
|
| 106 |
+
if uploaded_file and watermark_length:
|
| 107 |
+
image = Image.open(uploaded_file).convert("RGB")
|
| 108 |
+
extracted_watermark = detect_invisible_watermark(image, watermark_length)
|
| 109 |
+
st.write("Extracted Watermark:", extracted_watermark)
|
| 110 |
+
|
| 111 |
+
if __name__ == "__main__":
|
| 112 |
+
main()
|