{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "id": "siM8xBq5kDja" }, "outputs": [], "source": [ "from PIL import Image\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "QeDEw-sukFj9" }, "outputs": [], "source": [ "def texte_vers_binaire(texte):\n", " \"\"\"Convertit un string en une chaîne de caractères binaires (8 bits par caractère).\"\"\"\n", " return \"\".join(format(ord(c), \"08b\") for c in texte)\n", "\n", "def binaire_vers_texte(chaine_binaire):\n", " \"\"\"Convertit une chaîne de caractères binaires en string.\"\"\"\n", " octets = [chaine_binaire[i:i+8] for i in range(0, len(chaine_binaire), 8)]\n", " return \"\".join(chr(int(octet, 2)) for octet in octets if len(octet) == 8)\n", "\n", "DELIMITEUR = \"#####\"\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "ULmRSCIExZ2j" }, "outputs": [], "source": [ "def cacher_message(chemin_image_entree, message_secret, chemin_image_sortie):\n", " # On ouvre l'image et on s'assure qu'elle est en 8-bits (Niveaux de gris = mode 'L')\n", " img = Image.open(chemin_image_entree).convert(\"L\")\n", " pixels = np.array(img)\n", "\n", " # On ajoute le délimiteur à la fin du message et on convertit tout en binaire\n", " message_complet = message_secret + DELIMITEUR\n", " message_binaire = texte_vers_binaire(message_complet)\n", "\n", " # On aplatit le tableau de pixels en 1D pour faciliter le parcours\n", " pixels_plats = pixels.flatten()\n", "\n", " # Vérification de la capacité de l'image\n", " if len(message_binaire) > len(pixels_plats):\n", " raise ValueError(\"Message is too long for the image.\")\n", "\n", " # Remplacement du bit de poids faible (LSB)\n", " for i in range(len(message_binaire)):\n", " bit_a_cacher = int(message_binaire[i])\n", " # L'opération bit à bit '& 254' force le dernier bit à 0, puis on fait un 'OU' avec notre bit\n", " pixels_plats[i] = (pixels_plats[i] & 254) | bit_a_cacher\n", "\n", " # On redonne à l'image sa forme originale et on sauvegarde\n", " pixels_modifies = pixels_plats.reshape(pixels.shape)\n", " img_modifiee = Image.fromarray(pixels_modifies, mode=\"L\")\n", " img_modifiee.save(chemin_image_sortie, format=\"BMP\")\n", " print(f\"Message succesfully hidden in : {chemin_image_sortie}\")\n", "\n", "# --- 3. FONCTION POUR EXTRAIRE LE MESSAGE (DÉCODAGE LSB) ---\n", "\n", "def extraire_message(chemin_image):\n", " # On ouvre l'image modifiée\n", " img = Image.open(chemin_image).convert(\"L\")\n", " pixels_plats = np.array(img).flatten()\n", "\n", " bits_extraits = []\n", "\n", " # On récupère le dernier bit de chaque pixel\n", " for pixel in pixels_plats:\n", " bits_extraits.append(str(pixel & 1))\n", "\n", " chaine_binaire_complete = \"\".join(bits_extraits)\n", "\n", " # On lit le binaire par paquets de 8 bits (1 octet = 1 caractère)\n", " message_decode = \"\"\n", " for i in range(0, len(chaine_binaire_complete), 8):\n", " octet = chaine_binaire_complete[i:i+8]\n", " if len(octet) < 8:\n", " break\n", " caractere = chr(int(octet, 2))\n", " message_decode += caractere\n", "\n", " # On s'arrête si on détecte le délimiteur\n", " if message_decode.endswith(DELIMITEUR):\n", " # On retourne le message sans le délimiteur\n", " return message_decode[:-len(DELIMITEUR)]\n", "\n", " return \"No hidden message found (or delimiter not found).\"\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "g_e4U9O4lIOb" }, "outputs": [], "source": [ "nom_fichier_sortie = \"image_secrete.bmp\"\n", "mon_message = \"Decode this!\"\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "oLpz4m1vk-zF", "outputId": "1cdb06cb-9c4e-4131-b7a3-d76be6930c11" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Image 'image_test_8bits.bmp' created successfully!\n" ] } ], "source": [ "# 1. On définit la taille de l'image (par exemple 256 pixels par 256 pixels)\n", "largeur, hauteur = 256, 256\n", "\n", "# 2. On crée un tableau rempli de zéros (qui correspondra à du noir)\n", "pixels = np.zeros((hauteur, largeur), dtype=np.uint8)\n", "\n", "# 3. On crée un dégradé horizontal\n", "# Chaque colonne prend la valeur de son index (de 0 à 255)\n", "for x in range(largeur):\n", " pixels[:, x] = x\n", "\n", "# 4. On convertit le tableau NumPy en image PIL (mode 'L' = 8 bits niveaux de gris)\n", "image_test = Image.fromarray(pixels, mode=\"L\")\n", "\n", "# 5. On sauvegarde le fichier au format BMP\n", "nom_fichier = \"image_test_8bits.bmp\"\n", "image_test.save(nom_fichier, format=\"BMP\")\n", "\n", "print(f\"Image '{nom_fichier}' created successfully!\")\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 602 }, "id": "s9NtmhpUkU8T", "outputId": "699b8055-5e19-4fde-94fc-44464672a291" }, "outputs": [ { "data": { "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAEAAQABAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APAkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSv/2Q==", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAAAAAB5Gfe6AAACuUlEQVR4AWJkYBzZgIlhhIPRABjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoCgBshCcABgCdkAL/fkEcHQAAAABJRU5ErkJggg==", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Message succesfully hidden in : image_secrete.bmp\n" ] }, { "data": { "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAEAAQABAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APAkqwlWEqylTpVhKsJVhKsJVhKspU6VYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK//2Q==", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAAAAAB5Gfe6AAAEwElEQVR4AWJkYGRkYAJjpv9MjCDA9J+JgZmBkZHpPzMDE5hGkWdg/s/EwMTAxAiSA+ll+g8WAakHm8DEwAjSD1HHyAimmUFiEHNx8UFWDwBmYmBgYITg/yCagYHhPyMD438w/Z8BQqPIM4LkQXpAcmAaIsLwH0SD9DGA9YN4IBPB9H+QGMRcXHyGgQGgABgYmweJraMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEotHA2CQRMSAOWM0AAYs6AeJxaMBMEgiYsCcMRoAAxb0g8Ti0QAYJBExYM4YDYABC/pBYvFoAAySiBgwZ4wGwIAF/SCxeDQABklEDJgzRgNgwIJ+kFg8GgCDJCIGzBmjATBgQT9ILB4NgEESEQPmjNEAGLCgHyQWjwbAIImIAXPGaAAMWNAPEosBG/EBAACB7C4KJpffiQAAAABJRU5ErkJggg==", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Message : Decode this!\n" ] } ], "source": [ "display(Image.open(nom_fichier))\n", "# 2. Cacher le message\n", "cacher_message(nom_fichier, mon_message, nom_fichier_sortie)\n", "\n", "#display the image\n", "display(Image.open(nom_fichier_sortie))\n", "\n", "# 3. Extraire le message\n", "message_trouve = extraire_message(nom_fichier_sortie)\n", "print(f\"Message : {message_trouve}\")\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "HUvxyPw-knd9" }, "outputs": [], "source": [ "image_sortie = Image.open(nom_fichier_sortie)\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 562 }, "id": "YW7UFBzMn7I7", "outputId": "ddfdd830-f1ff-465a-d2e5-e45767510400" }, "outputs": [ { "ename": "ValueError", "evalue": "Message is too long for the image.", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m 5\u001b[39m image_stego = \u001b[33m\"\u001b[39m\u001b[33mimage_secrete.bmp\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 7\u001b[39m \u001b[38;5;66;03m# On cache ce long message dans notre image de test\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m \u001b[43mcacher_message\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimage_originale\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlong_message\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mimage_stego\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[38;5;66;03m# 2. Fonction pour extraire tous les LSB d'une image\u001b[39;00m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mextraire_tous_les_lsb\u001b[39m(chemin_image):\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 15\u001b[39m, in \u001b[36mcacher_message\u001b[39m\u001b[34m(chemin_image_entree, message_secret, chemin_image_sortie)\u001b[39m\n\u001b[32m 13\u001b[39m \u001b[38;5;66;03m# Vérification de la capacité de l'image\u001b[39;00m\n\u001b[32m 14\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(message_binaire) > \u001b[38;5;28mlen\u001b[39m(pixels_plats):\n\u001b[32m---> \u001b[39m\u001b[32m15\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mMessage is too long for the image.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 17\u001b[39m \u001b[38;5;66;03m# Remplacement du bit de poids faible (LSB)\u001b[39;00m\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;28mlen\u001b[39m(message_binaire)):\n", "\u001b[31mValueError\u001b[39m: Message is too long for the image." ] } ], "source": [ "# 1. Préparation d'un LONG message pour que l'effet stégo soit visible\n", "# On crée un faux message assez long pour remplir une bonne partie de l'image (256x256 = 65536 pixels)\n", "long_message = \"Check if we can decode everything, like long messages\" * 200\n", "image_originale = \"image_test_8bits.bmp\"\n", "image_stego = \"image_secrete.bmp\"\n", "\n", "# On cache ce long message dans notre image de test\n", "cacher_message(image_originale, long_message, image_stego)\n", "\n", "# 2. Fonction pour extraire tous les LSB d'une image\n", "def extraire_tous_les_lsb(chemin_image):\n", " img = Image.open(chemin_image).convert(\"L\")\n", " pixels = np.array(img).flatten()\n", " # On récupère uniquement le dernier bit avec l'opérateur '& 1'\n", " return pixels & 1\n", "\n", "# On extrait les LSB des deux images\n", "lsb_orig = extraire_tous_les_lsb(image_originale)\n", "lsb_stego = extraire_tous_les_lsb(image_stego)\n", "\n", "# 3. Création des graphiques avec Matplotlib\n", "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) # 1 ligne, 2 colonnes\n", "\n", "# --- Graphique de gauche : Image Stego ---\n", "ax1.hist(lsb_stego, bins=[-0.5, 0.5, 1.5], edgecolor=\"black\", alpha=0.7)\n", "ax1.set_title(\"Histogramme des LSB - Image Stego\")\n", "ax1.set_xticks([0, 1])\n", "ax1.set_xticklabels([\"LSB=0\", \"LSB=1\"])\n", "ax1.set_ylabel(\"Nombre d'occurrences\")\n", "\n", "# --- Graphique de droite : Image Originale ---\n", "ax2.hist(lsb_orig, bins=[-0.5, 0.5, 1.5], edgecolor=\"black\", alpha=0.7)\n", "ax2.set_title(\"Histogramme des LSB - Image Originale\")\n", "ax2.set_xticks([0, 1])\n", "ax2.set_xticklabels([\"LSB=0\", \"LSB=1\"])\n", "ax2.set_ylabel(\"Nombre d'occurrences\")\n", "\n", "# Affichage du résultat\n", "plt.tight_layout()\n", "plt.show()\n" ] }, { "cell_type": "markdown", "metadata": { "id": "0ZLEleEjmyyZ" }, "source": [ "GSB" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Y4uXeBJ_m7D7" }, "outputs": [], "source": [ "def cacher_messageGSB(chemin_image_entree, message_secret, chemin_image_sortie):\n", " # On ouvre l'image et on s'assure qu'elle est en 8-bits (Niveaux de gris = mode 'L')\n", " img = Image.open(chemin_image_entree).convert(\"L\")\n", " pixels = np.array(img)\n", "\n", " # On ajoute le délimiteur à la fin du message et on convertit tout en binaire\n", " message_complet = message_secret + DELIMITEUR\n", " message_binaire = texte_vers_binaire(message_complet)\n", "\n", " # On aplatit le tableau de pixels en 1D pour faciliter le parcours\n", " pixels_plats = pixels.flatten()\n", "\n", " # Vérification de la capacité de l'image\n", " if len(message_binaire) > len(pixels_plats):\n", " raise ValueError(\"The image is too small.\")\n", "\n", " # Remplacement du bit de poids faible (LSB)\n", " for i in range(len(message_binaire)):\n", " bit_a_cacher = int(message_binaire[i])\n", " # bit_a_cacher << 7 décale le bit de 7 crans vers la gauche\n", " pixels_plats[i] = (pixels_plats[i] & 127) | (bit_a_cacher << 7)\n", "\n", " # On redonne à l'image sa forme originale et on sauvegarde\n", " pixels_modifies = pixels_plats.reshape(pixels.shape)\n", " img_modifiee = Image.fromarray(pixels_modifies, mode=\"L\")\n", " img_modifiee.save(chemin_image_sortie, format=\"BMP\")\n", " print(f\"Message well hidden within : {chemin_image_sortie}\")\n", "\n", "# --- 3. FONCTION POUR EXTRAIRE LE MESSAGE (DÉCODAGE LSB) ---\n", "\n", "def extraire_messageGSB(chemin_image):\n", " # On ouvre l'image modifiée\n", " img = Image.open(chemin_image).convert(\"L\")\n", " pixels_plats = np.array(img).flatten()\n", "\n", " bits_extraits = []\n", "\n", " # On récupère le dernier bit de chaque pixel\n", " for pixel in pixels_plats:\n", " # On isole le bit de poids fort, puis on le ramène tout à droite\n", " bits_extraits.append(str((pixel & 128) >> 7))\n", "\n", " chaine_binaire_complete = \"\".join(bits_extraits)\n", "\n", " # On lit le binaire par paquets de 8 bits (1 octet = 1 caractère)\n", " message_decode = \"\"\n", " for i in range(0, len(chaine_binaire_complete), 8):\n", " octet = chaine_binaire_complete[i:i+8]\n", " if len(octet) < 8:\n", " break\n", " caractere = chr(int(octet, 2))\n", " message_decode += caractere\n", "\n", " # On s'arrête si on détecte le délimiteur\n", " if message_decode.endswith(DELIMITEUR):\n", " # On retourne le message sans le délimiteur\n", " return message_decode[:-len(DELIMITEUR)]\n", "\n", " return \"No hidden message found (or delimiter not found).\"\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 602 }, "id": "74Rinzm2nM7x", "outputId": "5a12f02b-6db5-431b-f872-b28e57dd12fe" }, "outputs": [ { "data": { "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAEAAQABAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APAkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSv/2Q==", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAAAAAB5Gfe6AAACuUlEQVR4AWJkYBzZgIlhhIPRABjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoChjhCWA0BYymgNEsMMLBaAoY4QlgNAWMpoDRLDDCwWgKGOEJYDQFjKaA0SwwwsFoCgBshCcABgCdkAL/fkEcHQAAAABJRU5ErkJggg==", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Message well hidden within : image_secrete.bmp\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/qx/qc20ksl50kb3r1gbtqwgqm2h0000gn/T/ipykernel_3336/3844268843.py:25: DeprecationWarning: 'mode' parameter is deprecated and will be removed in Pillow 13 (2026-10-15)\n", " img_modifiee = Image.fromarray(pixels_modifies, mode='L')\n" ] }, { "data": { "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAEAAQABAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APBlnmO7Msh3IEbLHlRjAPsNo49h6Vcjv7wMjC7n3Iioh8w5VVIKgegBAIHYirEV/eLNFMt3OJYU2RuJDuRemAew5PA9auQatqUOPK1C7TCKg2zMPlXOB16DJwPenfa7mRQslxKwEYiwzk/IDkL9AQDjpVqO/vAyMLufciKiHzDlVUgqB6AEAgdiKcJHdUVnZgg2qCc7RknA9OST+NXjf3ksKwyXc7xKmxUaQlQvBwB6fKvHsPSpjd3M0axy3EsiKFVVZyQAudoA9snHpk1aW/vD5ebuc+Vt8v8AeH5NuduPTGTj0yatR6nfhoGF7cgwArCfNb92CMYXnjjjinRTzK0TLLIGh/1ZDHKc549OSTx3NWre7uYJIpIriWN4gVjZHIKA5yAe3U/masW93cwSRSRXEsbxArGyOQUBzkA9up/M1Yt7u5gkikiuJY3iBWNkcgoDnIB7dT+Zqzb3dzBJFJFcSxvECsbI5BQHOQD26n8zVa30ywgkikisraN4gVjZIlBQHOQDjjqfzNaaVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK+BkqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWUqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlfAyVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrKVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSvgZKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVlKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJXwMlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqylWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEqwlWEr4GSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVZSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCVYSrCV8DJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKspVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhKsJVhK/9k=", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAAAAAB5Gfe6AAAC8UlEQVR4AWJkaGxkZARjxkbGRhBgBAmAhECiYBpEgGTB8mAOIyNYFUgFWBeY39gIZTdCFYNEQUIgxbhomOWMAwUYBsriQWIvE8MIB6MBMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEg9EUMMITwGgKGE0Bo1lghIPRFDDCE8BoChhNAaNZYISD0RQwwhPAaAoYTQGjWWCEA8BGfAoAADMZJv0gGr2bAAAAAElFTkSuQmCC", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Message : Decode this!\n" ] } ], "source": [ "display(Image.open(nom_fichier))\n", "# 2. Cacher le message\n", "cacher_messageGSB(nom_fichier, mon_message, nom_fichier_sortie)\n", "\n", "#display the image\n", "display(Image.open(nom_fichier_sortie))\n", "\n", "# 3. Extraire le message\n", "message_trouve = extraire_messageGSB(nom_fichier_sortie)\n", "print(f\"Message : {message_trouve}\")\n" ] }, { "cell_type": "markdown", "metadata": { "id": "sITRU6MzoIpA" }, "source": [ "Avec une clé" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "4vYMdWoxoIWZ" }, "outputs": [], "source": [ "def generer_positions(cle, nb_bits_necessaires, max_pixels):\n", " # On transforme la clé (ex: 1369) en une liste d'entiers [1, 3, 6, 9]\n", " sauts = [int(chiffre) for chiffre in str(cle)]\n", "\n", " positions = []\n", " pos_actuelle = 0\n", "\n", " for i in range(nb_bits_necessaires):\n", " saut = sauts[i % len(sauts)]\n", " # Sécurité : si un chiffre de la clé est 0, on avance d'au moins 1\n", " # pour ne pas écraser le même pixel\n", " if saut == 0:\n", " saut = 1\n", "\n", " pos_actuelle += saut\n", "\n", " # On vérifie qu'on ne dépasse pas la fin de l'image\n", " if pos_actuelle >= max_pixels:\n", " raise ValueError(f\"The image is too small. It can only hold {i} bits with this key.\")\n", "\n", " positions.append(pos_actuelle)\n", "\n", " return positions\n", "\n", "# --- 2. ENCODAGE AVEC CLÉ ---\n", "\n", "def cacher_message_cle(chemin_image_entree, message_secret, cle, chemin_image_sortie):\n", " img = Image.open(chemin_image_entree).convert(\"L\")\n", " pixels_plats = np.array(img).flatten()\n", "\n", " message_binaire = texte_vers_binaire(message_secret + DELIMITEUR)\n", "\n", " # On génère exactement les positions dont on a besoin\n", " positions = generer_positions(cle, len(message_binaire), len(pixels_plats))\n", "\n", " # On remplace les LSB uniquement sur ces positions précises\n", " for i in range(len(message_binaire)):\n", " pos = positions[i]\n", " bit_a_cacher = int(message_binaire[i])\n", " pixels_plats[pos] = (pixels_plats[pos] & 254) | bit_a_cacher\n", "\n", " img_modifiee = Image.fromarray(pixels_plats.reshape(np.array(img).shape), mode=\"L\")\n", " img_modifiee.save(chemin_image_sortie, format=\"BMP\")\n", " print(f\"Hidden message found with key '{cle}' !\")\n", "\n", "# --- 3. DÉCODAGE AVEC CLÉ ---\n", "\n", "def extraire_message_cle(chemin_image, cle):\n", " img = Image.open(chemin_image).convert(\"L\")\n", " pixels_plats = np.array(img).flatten()\n", "\n", " sauts = [int(chiffre) for chiffre in str(cle)]\n", " pos_actuelle = 0\n", " index_saut = 0\n", "\n", " bits_extraits = \"\"\n", " message_decode = \"\"\n", "\n", " # On parcourt l'image en sautant de pixel en pixel selon la clé\n", " while pos_actuelle < len(pixels_plats):\n", " saut = sauts[index_saut % len(sauts)]\n", " if saut == 0: saut = 1\n", " pos_actuelle += saut\n", "\n", " if pos_actuelle >= len(pixels_plats):\n", " break\n", "\n", " # On extrait le bit\n", " bits_extraits += str(pixels_plats[pos_actuelle] & 1)\n", " index_saut += 1\n", "\n", " # Tous les 8 bits, on convertit en texte\n", " if len(bits_extraits) == 8:\n", " message_decode += chr(int(bits_extraits, 2))\n", " bits_extraits = \"\"\n", "\n", " # On s'arrête si on trouve le délimiteur\n", " if message_decode.endswith(DELIMITEUR):\n", " return message_decode[:-len(DELIMITEUR)]\n", "\n", " return \"No hidden message found.\"\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "pf1ydHiSpkno", "outputId": "3bf962b6-0169-4ad8-a168-2971c308174c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hidden message found with key '1369' !\n", "Test right key : Try out keys!\n", "Test wrong key : No hidden message found.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/qx/qc20ksl50kb3r1gbtqwgqm2h0000gn/T/ipykernel_3336/840764186.py:42: DeprecationWarning: 'mode' parameter is deprecated and will be removed in Pillow 13 (2026-10-15)\n", " img_modifiee = Image.fromarray(pixels_plats.reshape(np.array(img).shape), mode='L')\n" ] } ], "source": [ "image_originale = \"image_test_8bits.bmp\"\n", "image_stego_cle = \"image_test_stego_cle.bmp\"\n", "mon_message = \"Try out keys!\"\n", "ma_cle = 1369\n", "\n", "# 1. On cache\n", "cacher_message_cle(image_originale, mon_message, ma_cle, image_stego_cle)\n", "\n", "# 2. On extrait avec la BONNE clé\n", "print(\"Test right key :\", extraire_message_cle(image_stego_cle, 1369))\n", "\n", "# 3. On extrait avec une MAUVAISE clé (ça devrait sortir n'importe quoi ou échouer)\n", "print(\"Test wrong key :\", extraire_message_cle(image_stego_cle, 4444))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "studies (3.13.9)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.9" } }, "nbformat": 4, "nbformat_minor": 0 }