from PIL import Image import numpy as np from scipy.fftpack import dct, idct import io QUANTIZATION_FACTOR = 10 def convert_and_resize_image(image): gray_image = image.convert("L") w, h = gray_image.size w_crop = w - (w % 8) h_crop = h - (h % 8) if w_crop == 0 or h_crop == 0: return None return gray_image.crop((0, 0, w_crop, h_crop)) def message_to_binary(message): message += "_END_" return ''.join(format(ord(c), '08b') for c in message) def binary_to_message(binary_message): delimiter = ''.join(format(ord(c), '08b') for c in "_END_") if delimiter not in binary_message: return None try: end_index = binary_message.index(delimiter) clean_binary = binary_message[:end_index] if len(clean_binary) % 8 != 0: return None return ''.join(chr(int(clean_binary[i:i+8], 2)) for i in range(0, len(clean_binary), 8)) except: return None def apply_dct(image_array): h, w = image_array.shape dct_blocks = np.zeros_like(image_array, dtype=float) for i in range(0, h, 8): for j in range(0, w, 8): block = image_array[i:i+8, j:j+8].astype(float) dct_blocks[i:i+8, j:j+8] = dct(dct(block.T, norm='ortho').T, norm='ortho') return dct_blocks def apply_idct(dct_blocks): h, w = dct_blocks.shape image_blocks = np.zeros_like(dct_blocks, dtype=float) for i in range(0, h, 8): for j in range(0, w, 8): block = dct_blocks[i:i+8, j:j+8] image_blocks[i:i+8, j:j+8] = idct(idct(block.T, norm='ortho').T, norm='ortho') return np.clip(image_blocks, 0, 255).astype(np.uint8) def embed_message(image, secret_message): gray_cropped = convert_and_resize_image(image) if gray_cropped is None: return None img_array = np.array(gray_cropped) h, w = img_array.shape binary_message = message_to_binary(secret_message) msg_len = len(binary_message) dct_coeffs = apply_dct(img_array) bit_index = 0 for i in range(0, h, 8): for j in range(0, w, 8): if bit_index >= msg_len: break u, v = 4, 1 coeff = dct_coeffs[i + u, j + v] bit = int(binary_message[bit_index]) quantized_coeff = int(round(coeff / QUANTIZATION_FACTOR)) if quantized_coeff % 2 != bit: quantized_coeff -= 1 dct_coeffs[i + u, j + v] = quantized_coeff * QUANTIZATION_FACTOR bit_index += 1 if bit_index >= msg_len: break stego_array = apply_idct(dct_coeffs) return Image.fromarray(stego_array) def extract_message(image): gray_cropped = convert_and_resize_image(image) if gray_cropped is None: return None img_array = np.array(gray_cropped) h, w = img_array.shape dct_coeffs = apply_dct(img_array) binary_message = "" for i in range(0, h, 8): for j in range(0, w, 8): u, v = 4, 1 coeff = dct_coeffs[i + u, j + v] quantized_coeff = int(round(coeff / QUANTIZATION_FACTOR)) binary_message += str(quantized_coeff % 2) return binary_to_message(binary_message)