From d602514e3808ff9139d39f2d424276627b67feff Mon Sep 17 00:00:00 2001 From: Arthur DANJOU Date: Wed, 11 Mar 2026 11:09:35 +0100 Subject: [PATCH] Add new images and update README for Cybersecurity section - Added `image_secrete.bmp` and `image_test_8bits.bmp` to the Cybersecurity directory. - Updated README.md to include a new section for Cybersecurity, highlighting data security and analysis. --- M2/Cybersecurity/LSB_GSB.ipynb | 624 ++++++++++++++++++++++++++ M2/Cybersecurity/image_secrete.bmp | Bin 0 -> 66614 bytes M2/Cybersecurity/image_test_8bits.bmp | Bin 0 -> 66614 bytes README.md | 1 + 4 files changed, 625 insertions(+) create mode 100644 M2/Cybersecurity/LSB_GSB.ipynb create mode 100644 M2/Cybersecurity/image_secrete.bmp create mode 100644 M2/Cybersecurity/image_test_8bits.bmp diff --git a/M2/Cybersecurity/LSB_GSB.ipynb b/M2/Cybersecurity/LSB_GSB.ipynb new file mode 100644 index 0000000..107f41e --- /dev/null +++ b/M2/Cybersecurity/LSB_GSB.ipynb @@ -0,0 +1,624 @@ +{ + "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 +} diff --git a/M2/Cybersecurity/image_secrete.bmp b/M2/Cybersecurity/image_secrete.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5be97bae09f889b92efcd42f9ec955718f7361d1 GIT binary patch literal 66614 zcmeI*_cxY(90%~Xk*p|`m8{69$cmIrW{Vkngd&vo9%$0udr~CrJ?u6WrD3#< zho1BN4bLy{`<(ln`{Jkj++6o{eZQag`(@{0CMr)V8OX}YLQYN&ef#!BzkdB7FE0-T1qCQ7Dndy~3ChaK=-d?^8fTpG<1`ZquEiEl*YincBph3{l(Sfe6F7)*Dps%lw z!Gi~5$dDlzI&>%u3=CjsXoz9MhQY|l2*$?7FflQKsi`T<%*-%+_;8q;o5RAw0wYF@ zfTg7+MvfebQKLq|%E}7X*4D7Gv4O3vE$r;rIE)`Z9up=^fTyP?yu7?HapFWwnluUC-rn%> z@xkQDli};@3qL*fB;ONJ{^I9fd~o;LU3>}X3Us@nKNf1 zBqRi(p`n;HYZk)7!Vn%Fj);f|%$_|Pk&%&zii$#XbTsD7nS+>^7{tcLA}%ft@$vDP zJ9jP;5)zP@n234v<{>F53CYRHNJ&Y-{Q2{-V8H^Urluk-Ee#77E<}2IIx;dckeQi@ zMT-_;@#4kE%F4o$B}=e$=~66PwhYUcFUN`%E3k6qN~~J73aeMI#+o&2uy*ZQtXsDZ z>({Txh7B9AapOj8+O!FqH*ZFEb~bWya*&&wi!EEWVC&Yc*tTsOwr}5#9Xobl=gytT z%gaN4em-{X+J)V_cVo|f|8OFT)lb~rKP2~cI_IjU%!qUH*VnO&6~J&>lSX`zKuI~ z?%?j-ySR7n9`4`2j|UGP;NioEc=YHI9zTAJCr_T>>C>kuD=R~Jc{wU7Do|NjiK?n9 zR99D{rltnZo;}0!=g;xt#S6TA`4X>Qy~69)ukq&18`Rd;qOPtEZ{NPfyLa#K{{4H@ z*Vm(=p#dL0e89(#AJN#@h)wn(=@cxhYzr6qF`Uls4xct0( z2ls!t|Hb`3?tgOsm;2w`|L6Pz=RY|A!ucQ0KXLwx^KYF0wZoJNA%x|r~gj>o&G!hclz)2-|4^8f2aRW|DFCj{dfBB^xx^f(|@P`PXC?$JNA%x|r~gj>o&G!hclz)2-|4^8f2aRW|DFCj{dfBB z^xx^f(|@P`PXC?$JNA%x|r~gj>o&G!hclz)2 z-|4^8f2aRW|DFCj{dfBB^xx^f(|@P`PXC?$JNA%x|r~gj>o&G!hclz)2-|4^8f2aRW|DFCj{dfBB^xx^fcXUEPP_WB?7ZDYc5dYVI zmX(o{lb2UeR8~?^QB_q_*VO3p-?Vjf_4WSxPlm=uCMITP<`$NgUH*fWjjg?%ql1%^ zvx}RnhkJMbv8Sh(m$$c%kDsruuKu}(h!mH+(*wvY(p literal 0 HcmV?d00001 diff --git a/M2/Cybersecurity/image_test_8bits.bmp b/M2/Cybersecurity/image_test_8bits.bmp new file mode 100644 index 0000000000000000000000000000000000000000..aa5643fcf5498ac0d3106c79bef2d80a4858baa0 GIT binary patch literal 66614 zcmeI*^;1-P9LDiCp66wvomLA&+bnB(~Q&Us&{eMT-`tSg~RhFJ7DyB}!1TWJyYuDuuPR zHKj|Jrc9YKlr3A9a^=cVzI=HqRH#73iWR9;sS-9eHdL-$8CzRh?Ck8Yx3{NCl`2%N zS``Ne2dY)8M)m5|sZpZ_HEY(SR;^mpu3eitb?Q*JZe1K59jRBZ9`)O|+xo$1o03thW*#ogVVZr!@ky?b{&JUr;p zqX#{E_M}&@UU+(X(z|zWyu7^V)29!8`}U<@zkc-Z-=6^k1~72oKn4vO#Nfe$88T!D zLx&D!*sx&?A3mHBBStWCx-EnCL&<;z*IVg-SLfdmBw zv2x`~R;^ma>eZ`Rvt|uz*REyVx^=8yznTM}%a$!{-MW=+ z+qSWN`*wEh*ul=7JK42s7rS@wCL|<;J$v@Bckf>I?b}CaXeeP}VeH?(p92RDaPZ(k z!o$Nkbm$O=4<9BXB7(@sNRAvi!qKBgiHeFMIy#z|m>7;7J4S45EOBvh96x@X6DLk^ z^5jWQojS$o)2BIe<_u@go+Um$o^$8UasK>yE?l_4#fulYbm{!{hBv#-thMATT)X~ zdH3!e@87@Y!-o%i{P>YipFZ*V^Jl(%`NG$)U-|a!8{fZwCoL_F^z?KxGBWt_;|D)~ z{^ZxMU;O_4o6O8iva+(s&dw$$C+E*4!O+M!S8fy2JbClwFJNX~u#knNRpBBo#rM zwRd&v(6LkJE?wQbb@%AevzKRYuReYI^&c>B(BL6MhYcSwa+LSzF=NM#pWrjmchcl3 zQ>RUzF>}`JIez{DbLY)puyE1hB}@wXXDSEzi{!=o;#x-@X6v@zduoU%!1%OV9Z6^Vjdptn3_petrM?{pz}Uwy8r3^uknw@e;WU4{IB_s=6{<1YW}bF53T=b{Y&eATL0Aguhze{{;&NH?f+>1 zOZ$J?|J44k_P@3Nulfhof2jUN^*^eAQvH|e-&Fsn`bX7&s{U2=zp8&${kQ7hRsXO4 z2laob|3&>j>VH!Im-^q-|EK;(^?$1WRsFx}e^&pu`rp<6ulxh$KPdk~`5($ZQT~ha zZevvL*+jz|5Ev%%0E^9tMYG^|Ev6CB&^3UbJXJ%#RU?Bfp{=58l`S0@I<-f~+m;WyRUH-fLclqz~ z-{rr{f0zF*|6TsO{CD~9^55mZ%YT>uF8^KryZm?g@ABW}zsrA@|1SSs{=58l`S0@I z<-f~+m;WyRUH-fLclqz~-{rr{f0zF*|6TsO{CD~9^55mZ%YT>uF8^KryZm?g@ABW} zzsrA@|1SSs{=58l`S0@I<-f~+m;WyRUH-fLclqz~-{rr{f0zF*|6TsO{CD~9^55mZ z%YT>uF8^KryZm?g@ABW}zsrA@|1SSs{=58l`S0@I<-f~+m;WyRUH-fLclqz~-{rr{ vf0zF*|6TsO{CD~9^55mZ%YT>uF8^KryZm?g@ABW}zh~!QU}*Gz_TT>jz77cP literal 0 HcmV?d00001 diff --git a/README.md b/README.md index e6ea484..661dc91 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ |--------|-------------| | `Advanced Machine Learning` | Advanced ML techniques | | `Clustering In Practice` | Unsupervised learning and clustering | +| `Cybersecurity` | Data security and analysis | | `Data Visualisation` | Data visualization principles and tools | | `Deep Learning` | Neural networks and deep architectures | | `Enjeux Climatiques` | Climate issues and data analysis |