<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Boudjaj</id>
	<title>Wiki du LAMA (UMR 5127) - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Boudjaj"/>
	<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php/Sp%C3%A9cial:Contributions/Boudjaj"/>
	<updated>2026-04-10T22:17:46Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16058</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16058"/>
		<updated>2025-05-18T23:54:31Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Histoire */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changements effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtient alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tous les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à caché une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;informations. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16057</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16057"/>
		<updated>2025-05-18T23:53:43Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Présentation du projet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changements effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtient alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tous les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à caché une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;informations. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16056</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16056"/>
		<updated>2025-05-18T23:52:48Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Conclusion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changements effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtient alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tous les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à caché une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;informations. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16055</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16055"/>
		<updated>2025-05-18T23:51:07Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changements effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtient alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tous les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à caché une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16054</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16054"/>
		<updated>2025-05-18T23:48:53Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changements effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16053</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16053"/>
		<updated>2025-05-18T23:47:13Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problème */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus l&#039;altération sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16052</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16052"/>
		<updated>2025-05-18T23:46:32Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problème */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bits nécessaires à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16051</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16051"/>
		<updated>2025-05-18T23:43:56Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16050</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16050"/>
		<updated>2025-05-18T23:38:09Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
{{hst}}&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{hsb}}&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16049</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16049"/>
		<updated>2025-05-18T23:35:27Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Exemples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 8 bits.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16048</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16048"/>
		<updated>2025-05-18T23:34:49Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Exemples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image originale.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16047</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16047"/>
		<updated>2025-05-18T23:34:24Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problème */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&#039;&#039;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&#039;&#039;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16046</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16046"/>
		<updated>2025-05-18T23:33:38Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problèmes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problème===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16045</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16045"/>
		<updated>2025-05-18T23:32:55Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
Nous pouvons conclure par une comparaison des deux méthodes.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet de cacher une grande quantité d&#039;information. Cependant, elle est fortement visible à l&#039;œil nu. Elle vaut le coup pour des messages courts mais devient moins utile dès lors que le message gagne en taille.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie BPC ne permet pas forcément de cacher autant d&#039;informations, puisque cela dépend du nombre de blocs complexes. En contrepartie, elle est bien moins repérable et convient parfaitement à de longs messages.&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16044</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16044"/>
		<updated>2025-05-18T23:25:53Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16043</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16043"/>
		<updated>2025-05-18T23:25:41Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&amp;lt;br&amp;gt;&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bcp_decomp.PNG|frameless|left|La nouvelle décomposition de l&#039;image.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc.&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_bloc.PNG|frameless|left|La décomposition d&#039;un bloc.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&amp;lt;br&amp;gt;&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&amp;lt;br&amp;gt;&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Bpc_damier.PNG‎|frameless|left|Représentation visuelle du damier.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&amp;lt;br&amp;gt;&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bpc_damier.PNG&amp;diff=16042</id>
		<title>Fichier:Bpc damier.PNG</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bpc_damier.PNG&amp;diff=16042"/>
		<updated>2025-05-18T23:17:10Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bpc_bloc.PNG&amp;diff=16041</id>
		<title>Fichier:Bpc bloc.PNG</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bpc_bloc.PNG&amp;diff=16041"/>
		<updated>2025-05-18T23:16:52Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bcp_decomp.PNG&amp;diff=16040</id>
		<title>Fichier:Bcp decomp.PNG</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Bcp_decomp.PNG&amp;diff=16040"/>
		<updated>2025-05-18T23:16:33Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16039</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16039"/>
		<updated>2025-05-18T23:13:40Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Exemples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC8.png ‎|200px|frameless|left|Image modifiée sur 8 bits.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 8 bits.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC8.png&amp;diff=16038</id>
		<title>Fichier:BPC8.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC8.png&amp;diff=16038"/>
		<updated>2025-05-18T23:13:36Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16037</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16037"/>
		<updated>2025-05-18T23:11:11Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Exemples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:BPC4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC4.png&amp;diff=16036</id>
		<title>Fichier:BPC4.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC4.png&amp;diff=16036"/>
		<updated>2025-05-18T23:08:53Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC1.png&amp;diff=16035</id>
		<title>Fichier:BPC1.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:BPC1.png&amp;diff=16035"/>
		<updated>2025-05-18T23:08:41Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16034</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16034"/>
		<updated>2025-05-18T23:06:50Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problèmes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB4.png ‎|200px|frameless|left|Image modifiée sur 4 bits de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 4 bits&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16033</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16033"/>
		<updated>2025-05-18T23:04:40Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problèmes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:LSB4.png|200px|thumb|left|Image modifiée sur 4 bits de poids faible.]]&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:LSB4.png&amp;diff=16032</id>
		<title>Fichier:LSB4.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:LSB4.png&amp;diff=16032"/>
		<updated>2025-05-18T23:02:09Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16031</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16031"/>
		<updated>2025-05-18T23:00:46Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* La stéganographie LSB */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:Chablais-orig.png|200px|frameless|left|Image originale.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Fichier:LSB1.png ‎|200px|frameless|left|Image modifiée sur 1 bit de poids faible.]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image originale.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;Image modifiée sur 1 bit&amp;lt;br&amp;gt;de poids faible.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:LSB1.png&amp;diff=16030</id>
		<title>Fichier:LSB1.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:LSB1.png&amp;diff=16030"/>
		<updated>2025-05-18T22:48:23Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Chablais-orig.png&amp;diff=16029</id>
		<title>Fichier:Chablais-orig.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Chablais-orig.png&amp;diff=16029"/>
		<updated>2025-05-18T22:47:55Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16028</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16028"/>
		<updated>2025-05-18T22:43:07Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG|200px|thumb|left|La décomposition d&#039;une image.]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16027</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16027"/>
		<updated>2025-05-18T22:41:44Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Principe de base */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:Image_decomp_lsb.PNG]]&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16026</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16026"/>
		<updated>2025-05-18T22:36:30Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
De plus, nous ne considérons pas les formats d&#039;image avec compression.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Dans un premier temps, décomposons l&#039;organisation d&#039;une image : &amp;lt;br&amp;gt;&lt;br /&gt;
Une image est composée de pixels eux-mêmes composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]]&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous utiliserons la bibliothèque Pillow qui permet de manipuler facilement des images.&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nom_img_fin: str, nb_bits: int = 1, ):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join((f&amp;quot;{ord(c):08b}&amp;quot;) for c in msg )&lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{msg_len:016b}&amp;quot; # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(f&amp;quot;Le message est trop long pour être encodé dans cette image avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            message_bit = int(message_complet[bit_index])&lt;br /&gt;
            bit_index += 1&lt;br /&gt;
&lt;br /&gt;
            byte &amp;amp;= ~(1 &amp;lt;&amp;lt; b)&lt;br /&gt;
            byte |= (message_bit &amp;lt;&amp;lt; b)&lt;br /&gt;
&lt;br /&gt;
        data_bytes[i] = byte&lt;br /&gt;
&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé avec succès dans {nom_img_fin}avec {str(nb_bits)} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    valeur = 0&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        &lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bit = (byte &amp;gt;&amp;gt; b) &amp;amp; 1&lt;br /&gt;
            bits_lus += str(bit)&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                for i in range(16):&lt;br /&gt;
                    valeur = (valeur &amp;lt;&amp;lt; 1) | (1 if bits_lus[i] == &#039;1&#039; else 0)&lt;br /&gt;
                    taille_msg = valeur&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ : {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image :&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixels RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changements entre deux bits consécutifs pour chaque ligne et chaque colonne du bloc. Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Nous fixerons un seuil de complexité arbitraire. Si le nombre de changements est supérieur à ce seuil, le bloc est complexe. Sinon, il est simple.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité (si, dû aux changement effectués sur les bits, le nombre de changement diminue), ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier :&lt;br /&gt;
Un damier de 8*8 pixels correspond simplement à une suite de pixels noirs et pixels blancs. On peut ainsi le représenter par une succession de 3 canaux RGB fixés à 0 suivis de 3 canaux RGB fixés à F (en base 16).&lt;br /&gt;
On obtiens alors deux suites d&#039;octets de même longueur qu&#039;on peut &amp;quot;XOR&amp;quot; l&#039;une avec l&#039;autre.&lt;br /&gt;
Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. Elle est donc appliquée sur tout les blocs qui étaient classés comme &amp;quot;complexe&amp;quot;, et dans lesquels on à cacher une partie du message, qui sont ensuite passés à &amp;quot;simple&amp;quot;.&lt;br /&gt;
===Exemples===&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image originale&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image lsb&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on voit pas la diférence&lt;br /&gt;
[[Fichier:image_decomp_lsb.png]] Image ou on modifi MSB&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #VERIF : print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #VERIF : print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Image_decomp_lsb.PNG&amp;diff=16025</id>
		<title>Fichier:Image decomp lsb.PNG</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Image_decomp_lsb.PNG&amp;diff=16025"/>
		<updated>2025-05-18T22:10:12Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16024</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=16024"/>
		<updated>2025-05-18T21:59:01Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Histoire */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
Nous pouvons définir la stéganographie comme suit :&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie ci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire de sorte à cacher un message.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
rom PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nb_bits: int = 1, nom_img_fin: str = &amp;quot;SECRETFIN_BYTE2.png&amp;quot;):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(bin(ord(c))[2:].zfill(8) for c in msg) &lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = bin(msg_len)[2:].zfill(16) # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(&amp;quot;Le message est trop long pour être encodé dans cette image avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        byte_bin = list(bin(byte)[2:].zfill(8)) # On s&#039;assure que la chaîne fait 8 bits&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            byte_bin[-(b + 1)] = message_complet[bit_index] &lt;br /&gt;
            bit_index += 1&lt;br /&gt;
        data_bytes[i] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(&amp;quot;Message encodé avec succès dans &amp;quot; + nom_img_fin + &amp;quot; avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        byte_bin = bin(byte)[2:].zfill(8)&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bits_lus += byte_bin[-(b + 1)]&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                taille_msg = int(bits_lus, 2)&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;MESSAGE DÉCODÉ :&amp;quot;, message)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15937</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15937"/>
		<updated>2025-05-12T04:57:36Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définieci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
rom PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nb_bits: int = 1, nom_img_fin: str = &amp;quot;SECRETFIN_BYTE2.png&amp;quot;):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(bin(ord(c))[2:].zfill(8) for c in msg) &lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = bin(msg_len)[2:].zfill(16) # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(&amp;quot;Le message est trop long pour être encodé dans cette image avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        byte_bin = list(bin(byte)[2:].zfill(8)) # On s&#039;assure que la chaîne fait 8 bits&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            byte_bin[-(b + 1)] = message_complet[bit_index] &lt;br /&gt;
            bit_index += 1&lt;br /&gt;
        data_bytes[i] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(&amp;quot;Message encodé avec succès dans &amp;quot; + nom_img_fin + &amp;quot; avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        byte_bin = bin(byte)[2:].zfill(8)&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bits_lus += byte_bin[-(b + 1)]&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                taille_msg = int(bits_lus, 2)&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;MESSAGE DÉCODÉ :&amp;quot;, message)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
&lt;br /&gt;
def complexite(bloc_pixels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de calculer la complexité d&#039;un bloc de 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrée : Un tableau qui contient les pixels du bloc actuel.&lt;br /&gt;
    Sortie : int, un compteur qui indique le nombre de changements de bits consécutifs.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    changement = 0&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des colonnes (pixels de même x) :&lt;br /&gt;
    for x in range(len(bloc_pixels[0])):&lt;br /&gt;
        for couleur in range(3):  # Pour chaque composante RGB&lt;br /&gt;
            for bit_pos in range(8):  # Pour chaque position de bit (de 0 à 7)&lt;br /&gt;
                prev_bit = None # Parce qu&#039;au début il n&#039;y a pas de bit précédent&lt;br /&gt;
                for y in range(len(bloc_pixels)):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1 # On extrait le bit spécifique de la composante couleur RGB&lt;br /&gt;
                     &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit: # On regarde s&#039;il y a un chagement entre deux bits consécutifs&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    # On regarde la complexité des lignes (pixels de même y) :&lt;br /&gt;
    for y in range(len(bloc_pixels)):&lt;br /&gt;
        for couleur in range(3):&lt;br /&gt;
            for bit_pos in range(8):&lt;br /&gt;
                prev_bit = None&lt;br /&gt;
                for x in range(len(bloc_pixels[0])):&lt;br /&gt;
                    if bloc_pixels[y][x] is None:&lt;br /&gt;
                        continue&lt;br /&gt;
                            &lt;br /&gt;
                    bit = (bloc_pixels[y][x][couleur] &amp;gt;&amp;gt; bit_pos) &amp;amp; 1&lt;br /&gt;
                        &lt;br /&gt;
                    if prev_bit is not None and bit != prev_bit:&lt;br /&gt;
                        changement += 1&lt;br /&gt;
                        &lt;br /&gt;
                    prev_bit = bit&lt;br /&gt;
        &lt;br /&gt;
    return changement&lt;br /&gt;
&lt;br /&gt;
def bpc(img_name: str, msg: str, nom_img_fin: str = &amp;quot;BPC8bis2.png&amp;quot;, damier_name: str = &amp;quot;damier.png&amp;quot;, nb_bits: int = 1, seuil_complexite: int = 500):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de caché un message dans une image en fonction de la complexité de blocs 8*8 pixels.&lt;br /&gt;
&lt;br /&gt;
    Entrées :&lt;br /&gt;
        -img_name : Nom de l&#039;image d&#039;origine&lt;br /&gt;
        -msg : message à caché&lt;br /&gt;
        -nom_img_fin : nom de l&#039;image qui contient le message caché&lt;br /&gt;
        -damier_name : nom de l&#039;image qui contient le damier&lt;br /&gt;
        -nb_bits : nombre de bits sur lesquels on encode le message&lt;br /&gt;
    &lt;br /&gt;
    Sortie : img_fin, une image qui contient un message.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image et la convertir en RGB&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Évite les problèmes liés à la transparence&lt;br /&gt;
    largeur, hauteur = image_rgb.size&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # De même pour le damier 8*8&lt;br /&gt;
    damier_ori = Image.open(damier_name)&lt;br /&gt;
    damier_rgb = damier_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_damier = bytearray(damier_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    # Conversion du message à encoder en binaire&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(f&amp;quot;{ord(c):08b}&amp;quot; for c in msg)&lt;br /&gt;
    msg_len_bin = f&amp;quot;{len(msg_bin):016b}&amp;quot;  # On encode la taille sur 16 bits&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin  # Str binaire&lt;br /&gt;
    bit_index = 0  # Index dans le message binaire&lt;br /&gt;
&lt;br /&gt;
    blocs_modifies = [] # Permet de garder une trace des blocs qui ont été modifiés&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On parcours tout les blocs de l&#039;image&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None) # Si le pixel est hors de l&#039;image (pas multiple de 8), on ajoute None à la ligne puis on passe au suivant&lt;br /&gt;
                        continue&lt;br /&gt;
&lt;br /&gt;
                    pixel = image_rgb.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On regarde la complexité du bloc&lt;br /&gt;
&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
            #print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Complexité = {comp}, {&#039;Complexe&#039; if est_complexe else &#039;Simple&#039;}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
            if not est_complexe or bit_index &amp;gt;= len(message_complet): # Si le bloc n&#039;est pas complexe ou si le message est déjà entièrement encodé, on passe au bloc suivant&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            blocs_modifies.append((bloc_x, bloc_y)) # Sinon on ajoute le bloc aux blocs modifiés&lt;br /&gt;
            &lt;br /&gt;
            # Encodage dans le bloc complexe :&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue  # Évite de parcourir un pixel hors de l&#039;image&lt;br /&gt;
&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3  # Car 3 octets par pixel (R, G et B)&lt;br /&gt;
                    &lt;br /&gt;
                    for couleur in range(3):  # R, G et B&lt;br /&gt;
                        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                            break  # On arrête si tout le message est encodé&lt;br /&gt;
&lt;br /&gt;
                        byte = data_bytes[pixel_index + couleur]&lt;br /&gt;
                        byte_bin = list(f&amp;quot;{byte:08b}&amp;quot;) # On vérifie que la chaîne fait bien 8 bits&lt;br /&gt;
                        &lt;br /&gt;
                        # Modification des nb_bits les moins significatifs&lt;br /&gt;
                        for b in range(nb_bits):&lt;br /&gt;
                            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                                break&lt;br /&gt;
                            byte_bin[-(b + 1)] = message_complet[bit_index]&lt;br /&gt;
                            bit_index += 1&lt;br /&gt;
                        &lt;br /&gt;
                        data_bytes[pixel_index + couleur] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
                    &lt;br /&gt;
                    if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                        break  # Sortir des boucles si message complet encodé&lt;br /&gt;
                &lt;br /&gt;
                if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet) or len(blocs_modifies) == 0: &lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            bloc_pixels_modifies = [] # Puisqu&#039;on a modifier les octets du blocs, on doit vérifier s&#039;il est encore complexe&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    # On reconstruit le pixel à partir de data_bytes modifié :&lt;br /&gt;
                    pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                    r = data_bytes[pixel_index]&lt;br /&gt;
                    g = data_bytes[pixel_index + 1]&lt;br /&gt;
                    b = data_bytes[pixel_index + 2]&lt;br /&gt;
                    pixel = (r, g, b)&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels_modifies.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            nouvelle_complexite = complexite(bloc_pixels_modifies)&lt;br /&gt;
            est_encore_complexe = nouvelle_complexite &amp;gt; seuil_complexite&lt;br /&gt;
            #print(f&amp;quot;Bloc ({bloc_x},{bloc_y}): Nouvelle complexité = {nouvelle_complexite}, {&#039;Encore complexe&#039; if est_encore_complexe else &#039;Devenu simple&#039;}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            if not est_encore_complexe: # Si le bloc passe de complexe à simple, on applique un XOR avec le damier.&lt;br /&gt;
                print(f&amp;quot;On applique le damier au bloc: ({bloc_x},{bloc_y})&amp;quot;)&lt;br /&gt;
                for y in range(8):&lt;br /&gt;
                    for x in range(8):&lt;br /&gt;
                        pixel_x = bloc_x + x&lt;br /&gt;
                        pixel_y = bloc_y + y&lt;br /&gt;
                        &lt;br /&gt;
                        if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                            continue&lt;br /&gt;
                        &lt;br /&gt;
                        pixel_index = (pixel_y * largeur + pixel_x) * 3&lt;br /&gt;
                        damier_index = (y * 8 + x) * 3&lt;br /&gt;
                        &lt;br /&gt;
                        for c in range(3):&lt;br /&gt;
                            data_bytes[pixel_index + c] ^= data_damier[damier_index + c] # XOR sur chaque octet d&#039;un pixel&lt;br /&gt;
&lt;br /&gt;
    if bit_index &amp;lt; len(message_complet): # On vérifie si le message a été entièrement encodé&lt;br /&gt;
        print(f&amp;quot;ATTENTION: Message incomplet! Seulement {bit_index}/{len(message_complet)} bits encodés.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(f&amp;quot;Message entièrement encodé: {bit_index}/{len(message_complet)} bits.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(f&amp;quot;Message encodé dans {nom_img_fin} avec {nb_bits} bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def bpc_discover(img_name: str, nb_bits: int = 1, seuil_complexite: int = 500): &lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Une fonction qui permet de décoder un message précedment encoder à l&#039;aide de la fonction bpc.&lt;br /&gt;
    Entrées:&lt;br /&gt;
        -img_name, str : nom de l&#039;image contenant le message caché&lt;br /&gt;
        -nb_bits, int : Nombre de bits de poids faible utilisés par couleur pour l&#039;encodage&lt;br /&gt;
        &lt;br /&gt;
    Sortie: Le message décodé, str&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 8):&lt;br /&gt;
        print(f&amp;quot;Le nombre de bits par couleur doit être entre 1 et 8. Nombre de bits entré :{nb_bits}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # On ouvre l&#039;image&lt;br /&gt;
    image = Image.open(img_name).convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    largeur, hauteur = image.size&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
    total_bits = None&lt;br /&gt;
    &lt;br /&gt;
    for bloc_y in range(0, hauteur, 8): # On lit les pixels bloc par bloc (8x8)&lt;br /&gt;
        for bloc_x in range(0, largeur, 8):&lt;br /&gt;
            bloc_pixels = []&lt;br /&gt;
            for y in range(8):&lt;br /&gt;
                ligne_pixels = []&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        ligne_pixels.append(None)&lt;br /&gt;
                        continue&lt;br /&gt;
                    &lt;br /&gt;
                    pixel = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    ligne_pixels.append(pixel)&lt;br /&gt;
                bloc_pixels.append(ligne_pixels)&lt;br /&gt;
            &lt;br /&gt;
            comp = complexite(bloc_pixels) # On calcule la complexité&lt;br /&gt;
            est_complexe = comp &amp;gt; seuil_complexite&lt;br /&gt;
&lt;br /&gt;
            if not est_complexe:&lt;br /&gt;
                continue # Si le bloc n&#039;est pas complexe, on passe au suivant&lt;br /&gt;
                &lt;br /&gt;
            #print(f&amp;quot;Lecture du bloc complexe ({bloc_x},{bloc_y}): Complexité = {comp}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            for y in range(8): # On lit les bits cachés dans le bloc complexe&lt;br /&gt;
                for x in range(8):&lt;br /&gt;
                    pixel_x = bloc_x + x&lt;br /&gt;
                    pixel_y = bloc_y + y&lt;br /&gt;
                    &lt;br /&gt;
                    if pixel_x &amp;gt;= largeur or pixel_y &amp;gt;= hauteur:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    r, g, b = image.getpixel((pixel_x, pixel_y))&lt;br /&gt;
                    for couleur in (r, g, b):&lt;br /&gt;
                        for i in range(nb_bits):&lt;br /&gt;
                            bit = (couleur &amp;gt;&amp;gt; i) &amp;amp; 1&lt;br /&gt;
                            bits_lus += str(bit)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is None and len(bits_lus) &amp;gt;= 16:&lt;br /&gt;
                                taille_msg = int(bits_lus[:16], 2)&lt;br /&gt;
                                total_bits = 16 + taille_msg&lt;br /&gt;
                                print(f&amp;quot;Taille du message : {taille_msg} bits&amp;quot;)&lt;br /&gt;
                            &lt;br /&gt;
                            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                                break&lt;br /&gt;
                        &lt;br /&gt;
                        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                            break&lt;br /&gt;
                    &lt;br /&gt;
                    if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                        break&lt;br /&gt;
                &lt;br /&gt;
                if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        &lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    if taille_msg is None:&lt;br /&gt;
        print(&amp;quot;Impossible de lire la taille du message.&amp;quot;)&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if len(bits_lus) &amp;lt; total_bits:&lt;br /&gt;
        print(f&amp;quot;Message incomplet: seulement {len(bits_lus) - 16}/{taille_msg} bits lus&amp;quot;) # On continue quand même avec les bits qu&#039;on a&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    &lt;br /&gt;
    if len(message_bits) % 8 != 0:&lt;br /&gt;
        message_bits = message_bits + &#039;0&#039; * (8 - len(message_bits) % 8)&lt;br /&gt;
    &lt;br /&gt;
    octets = [message_bits[i:i + 8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(b, 2)) for b in octets)&lt;br /&gt;
    &lt;br /&gt;
    print(f&amp;quot;MESSAGE DÉCODÉ ({len(bits_lus) - 16}/{taille_msg} bits): {message}&amp;quot;)&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15936</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15936"/>
		<updated>2025-05-12T04:56:12Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définieci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
rom PIL import Image&lt;br /&gt;
&lt;br /&gt;
def cache(img_name: str, msg: str, nb_bits: int = 1, nom_img_fin: str = &amp;quot;SECRETFIN_BYTE2.png&amp;quot;):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir et convertir l&#039;image en mode RGB :&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;) # Evite les problèmes liés à la transparence.&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
    &lt;br /&gt;
    # Encodage du message en binaire.&lt;br /&gt;
    msg_bin = &amp;quot;&amp;quot;.join(bin(ord(c))[2:].zfill(8) for c in msg) &lt;br /&gt;
    msg_len = len(msg_bin)&lt;br /&gt;
    msg_len_bin = bin(msg_len)[2:].zfill(16) # 16 bits pour représenter la taille du message&lt;br /&gt;
    message_complet = msg_len_bin + msg_bin&lt;br /&gt;
&lt;br /&gt;
    # Vérification de la capacité&lt;br /&gt;
    capacite = len(data_bytes) * nb_bits&lt;br /&gt;
    if len(message_complet) &amp;gt; capacite:&lt;br /&gt;
        print(&amp;quot;Le message est trop long pour être encodé dans cette image avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Encodage dans le bytearray.&lt;br /&gt;
    bit_index = 0&lt;br /&gt;
    for i in range(len(data_bytes)):&lt;br /&gt;
        byte = data_bytes[i]&lt;br /&gt;
        byte_bin = list(bin(byte)[2:].zfill(8)) # On s&#039;assure que la chaîne fait 8 bits&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
                break&lt;br /&gt;
            # Remplacer les bits les moins significatifs&lt;br /&gt;
            byte_bin[-(b + 1)] = message_complet[bit_index] &lt;br /&gt;
            bit_index += 1&lt;br /&gt;
        data_bytes[i] = int(&amp;quot;&amp;quot;.join(byte_bin), 2)&lt;br /&gt;
        if bit_index &amp;gt;= len(message_complet):&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction de l&#039;image.&lt;br /&gt;
    img_fin = Image.frombytes(&amp;quot;RGB&amp;quot;, image_rgb.size, bytes(data_bytes))&lt;br /&gt;
    img_fin.save(nom_img_fin)&lt;br /&gt;
    print(&amp;quot;Message encodé avec succès dans &amp;quot; + nom_img_fin + &amp;quot; avec &amp;quot; + str(nb_bits) + &amp;quot; bit(s) par couleur.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def discover(img_name: str, nb_bits: int = 1):&lt;br /&gt;
    if not (1 &amp;lt;= nb_bits &amp;lt;= 4):&lt;br /&gt;
        print(&amp;quot;Le nombre de bits par couleur doit être entre 1 et 4.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ouvrir l&#039;image.&lt;br /&gt;
    image_ori = Image.open(img_name)&lt;br /&gt;
    image_rgb = image_ori.convert(&amp;quot;RGB&amp;quot;)&lt;br /&gt;
    data_bytes = bytearray(image_rgb.tobytes())&lt;br /&gt;
&lt;br /&gt;
    bits_lus = &amp;quot;&amp;quot;&lt;br /&gt;
    taille_msg = None&lt;br /&gt;
&lt;br /&gt;
    for byte in data_bytes:&lt;br /&gt;
        byte_bin = bin(byte)[2:].zfill(8)&lt;br /&gt;
        for b in range(nb_bits):&lt;br /&gt;
            bits_lus += byte_bin[-(b + 1)]&lt;br /&gt;
&lt;br /&gt;
            # Lecture des 16 premiers bits, qui contiennent la taille.&lt;br /&gt;
            if len(bits_lus) == 16 and taille_msg is None:&lt;br /&gt;
                taille_msg = int(bits_lus, 2)&lt;br /&gt;
                total_bits = 16 + taille_msg&lt;br /&gt;
&lt;br /&gt;
            if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
                break&lt;br /&gt;
        if taille_msg is not None and len(bits_lus) &amp;gt;= total_bits:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
    # Reconstruction du message.&lt;br /&gt;
    message_bits = bits_lus[16:16 + taille_msg]&lt;br /&gt;
    octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]&lt;br /&gt;
    message = &amp;quot;&amp;quot;.join(chr(int(o, 2)) for o in octets)&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;MESSAGE DÉCODÉ :&amp;quot;, message)&lt;br /&gt;
    return message&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15935</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15935"/>
		<updated>2025-05-12T04:53:19Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&amp;quot;Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définieci-dessus à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15934</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15934"/>
		<updated>2025-05-12T04:43:30Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Problèmes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
(du grec steganos, caché, et graphein, écrire)&amp;lt;br&amp;gt;&lt;br /&gt;
Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie plus tôt à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15933</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15933"/>
		<updated>2025-05-12T04:43:13Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* La stéganographie BPC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
(du grec steganos, caché, et graphein, écrire)&amp;lt;br&amp;gt;&lt;br /&gt;
Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie plus tôt à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.&lt;br /&gt;
&lt;br /&gt;
Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité.&lt;br /&gt;
Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe.&lt;br /&gt;
Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage.&lt;br /&gt;
Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe. &lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15932</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15932"/>
		<updated>2025-05-12T04:42:13Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
(du grec steganos, caché, et graphein, écrire)&amp;lt;br&amp;gt;&lt;br /&gt;
Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&amp;lt;br&amp;gt;&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie plus tôt à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&amp;lt;br&amp;gt;&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue.&lt;br /&gt;
Chaque composante peut prendre une valeur allant de 0 à 255.&amp;lt;br&amp;gt;&lt;br /&gt;
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l&#039;œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.&amp;lt;br&amp;gt;&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Problèmes===&lt;br /&gt;
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.&amp;lt;br&amp;gt;&lt;br /&gt;
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15931</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15931"/>
		<updated>2025-05-12T04:39:48Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : /* Histoire */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
(du grec steganos, caché, et graphein, écrire)&lt;br /&gt;
Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d&#039;une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l&#039;aide de logiciels spécialisés.&lt;br /&gt;
On attribue la première mention de la stéganographie telle que définie plus tôt à l&#039;historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.&lt;br /&gt;
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code et problèmes===&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15854</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15854"/>
		<updated>2025-05-01T16:56:55Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code et problèmes===&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;br /&gt;
===Principe de base===&lt;br /&gt;
===Exemples===&lt;br /&gt;
===Code===&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15853</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15853"/>
		<updated>2025-05-01T16:55:39Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;br /&gt;
&lt;br /&gt;
==La stéganographie BPC==&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15852</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15852"/>
		<updated>2025-05-01T16:54:59Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tuteur : Pierre Hyvernat &amp;lt;br&amp;gt;&lt;br /&gt;
Elève : Hania Boudjaj&lt;br /&gt;
&lt;br /&gt;
==Présentation du projet==&lt;br /&gt;
&lt;br /&gt;
==Histoire==&lt;br /&gt;
&lt;br /&gt;
==La stéganographie LSB==&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15851</id>
		<title>Stéganographie &quot;BPC&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=St%C3%A9ganographie_%22BPC%22&amp;diff=15851"/>
		<updated>2025-05-01T15:21:46Z</updated>

		<summary type="html">&lt;p&gt;Boudjaj : Page créée avec « Stéganographie BPC »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Stéganographie BPC&lt;/div&gt;</summary>
		<author><name>Boudjaj</name></author>
	</entry>
</feed>