finir le sprint 3

This commit is contained in:
AlgaLaptop
2026-01-04 18:05:46 +01:00
parent bceb70c052
commit 182cd2bd28
10 changed files with 163 additions and 118 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
package fr.iutfbleau.sae.mhuffman;
import fr.iutfbleau.sae.mimage.Pixel;
import fr.iutfbleau.sae.mimage.RGBImage;
/**
@@ -26,7 +27,7 @@ import fr.iutfbleau.sae.mimage.RGBImage;
* </p>
*
* @author Algassimou Pellel Diallo
* @version 1.0
* @version 1.1
* @since 2025-12-13
*/
public class FrequencyTable {
@@ -62,22 +63,28 @@ public class FrequencyTable {
* </p>
*
* @param img l'image RGB à analyser
* @throws IllegalArgumentException si l'image est null
*/
public void computeFromImage(RGBImage img) {
/*Nb: une composante de couleur est un entier entre 0 et 255 qui représente la part de rouge,vert ou bleu
dans la couleur d'un pixel.
*/
/* pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
puis on fait de même pour les composantes verte et bleue.
on répète ce processus pour tous les pixels de l'image.
*/
for (int ligne = 0; ligne < img.getWidth(); ligne++) {
for (int colonne = 0; colonne < img.getHeight(); colonne++) {
this.freqR[img.getPixel(ligne, colonne).getR()]++; // Incrémente la fréquence de la composante rouge
this.freqG[img.getPixel(ligne, colonne).getG()]++; // Incrémente la fréquence de la composante verte
this.freqB[img.getPixel(ligne, colonne).getB()]++; // Incrémente la fréquence de la composante bleue
if (img == null) {
throw new IllegalArgumentException("L'image ne peut pas être null");
}
/* Pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
* c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
* Ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
* Puis on fait de même pour les composantes verte et bleue.
* On répète ce processus pour tous les pixels de l'image.
*/
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
// On récupère le pixel une seule fois pour optimiser
Pixel pixel = img.getPixel(x, y);
// Puis on incrémente les trois fréquences
this.freqR[pixel.getR()]++;
this.freqG[pixel.getG()]++;
this.freqB[pixel.getB()]++;
}
}
}
@@ -93,7 +100,6 @@ public class FrequencyTable {
/**
* Retourne le tableau des fréquences de la composante verte.
*
* @return un tableau de 256 entiers représentant les fréquences du vert
*/
public int[] getGreen() {
@@ -108,8 +114,4 @@ public class FrequencyTable {
public int[] getBlue() {
return this.freqB;
}
}
}
+44 -10
View File
@@ -21,10 +21,14 @@ package fr.iutfbleau.sae.mhuffman;
*/
public class HuffmanNode {
/** Valeur de la composante (cette valeur est appelée symbole, voir l'histoire de huffman, tres interressant) représentée par ce nœud (si feuille). c'est la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel. */
/**
* Valeur de la composante (symbole) représentée par ce nœud (uniquement pour les feuilles).
* Représente la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel.
* Pour les nœuds internes, cette valeur vaut -1.
*/
private int value;
/** Fréquence du symbole (somme des fréquences des enfants). */
/** Fréquence du symbole (somme des fréquences des enfants pour les nœuds internes). */
private int frequence;
/** Fils gauche du nœud (null si feuille). */
@@ -40,10 +44,18 @@ public class HuffmanNode {
* issue de la table de fréquences.
* </p>
*
* @param value la valeur (symbole) représentée par ce nœud
* @param value la valeur (symbole) représentée par ce nœud (entre 0 et 255)
* @param frequence la fréquence d'apparition de la valeur
* @throws IllegalArgumentException si la valeur n'est pas entre 0 et 255
* ou si la fréquence est négative
*/
public HuffmanNode(int value, int frequence) {
if (value < 0 || value > 255) {
throw new IllegalArgumentException("La valeur doit être entre 0 et 255");
}
if (frequence < 0) {
throw new IllegalArgumentException("La fréquence ne peut pas être négative");
}
this.value = value;
this.frequence = frequence;
this.left = null;
@@ -59,8 +71,13 @@ public class HuffmanNode {
*
* @param left le fils gauche
* @param right le fils droit
* @throws IllegalArgumentException si l'un des fils est null
*/
public HuffmanNode(HuffmanNode left, HuffmanNode right) {
if (left == null || right == null) {
throw new IllegalArgumentException("Les fils ne peuvent pas être null");
}
this.value = -1; // Valeur sentinelle pour les nœuds internes
this.left = left;
this.right = right;
this.frequence = left.frequence + right.frequence;
@@ -87,7 +104,7 @@ public class HuffmanNode {
/**
* Retourne le fils gauche du nœud.
*
* @return le fils gauche
* @return le fils gauche, ou null si le nœud est une feuille
*/
public HuffmanNode getLeft() {
return this.left;
@@ -96,7 +113,7 @@ public class HuffmanNode {
/**
* Retourne le fils droit du nœud.
*
* @return le fils droit
* @return le fils droit, ou null si le nœud est une feuille
*/
public HuffmanNode getRight() {
return this.right;
@@ -108,12 +125,29 @@ public class HuffmanNode {
* Cette méthode n'a de sens que si le nœud est une feuille.
* </p>
*
* @return la valeur du symbole
* @return la valeur du symbole (entre 0 et 255)
* @throws IllegalStateException si le nœud n'est pas une feuille
*/
public int getValue() {
if (!this.isLeaf()) {
throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
}
if (!this.isLeaf()) {
throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
}
return this.value;
}
}
/**
* Retourne une représentation textuelle du nœud.
* <p>
* Utile pour le débogage et l'affichage de l'arbre.
* </p>
*
* @return une chaîne décrivant le nœud
*/
@Override
public String toString() {
if (this.isLeaf()) {
return "Feuille(valeur=" + this.value + ", freq=" + this.frequence + ")";
}
return "Noeud(freq=" + this.frequence + ")";
}
}
@@ -167,36 +167,6 @@ public class HuffmanTree {
generateCodesRec(node.getLeft(), prefiixe + "0");
// On va a droite en ajoutant "1" au code
generateCodesRec(node.getRight(), prefiixe + "1");
// this.codes = new HashMap<>();
// this.chaineCarac = new String();
// if(root.isLeaf()){
// codes.put(root.getValue(),Integer.parseInt(chaineCarac));
// return codes;
// }
// HuffmanNode temp = root;
// if (root.getLeft() != null) {
// root = root.getLeft();
// chaineCarac = chaineCarac + "0";
// generateCodes();
// // on retire le dernier bit lorsqu'on remonte car sinon les codes seront faussés
// chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
// }
// if (temp.getRight() != null) {
// root = temp.getRight();
// chaineCarac = chaineCarac + "1";
// generateCodes();
// chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1);
// }
// root = temp;
// return codes;
}
/**
+7 -6
View File
@@ -1,13 +1,11 @@
package fr.iutfbleau.sae.mpif;
import fr.iutfbleau.sae.mimage.RGBImage;
import fr.iutfbleau.sae.util.BitInputStream;
import fr.iutfbleau.sae.util.BitOutputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.Map;
import fr.iutfbleau.sae.mimage.RGBImage;
public class PIFReader {
@@ -19,8 +17,11 @@ public class PIFReader {
public RGBImage read(String filepath)
throws Exception {
// j'Utilise d'un BufferedInputStream pour une meilleure performance
FileInputStream fis = new FileInputStream(filepath);
BitInputStream lecteur = new BitInputStream(fis);
BufferedInputStream bis = new BufferedInputStream(fis);
BitInputStream lecteur = new BitInputStream(bis);
// je lis l'entête et les tables canoniques
this.readHeader(lecteur);
@@ -42,7 +43,7 @@ public class PIFReader {
}
public void readHeader(BitInputStream in) {
// TODO: Implement header reading
}
public void readCanonicalTables(BitInputStream in) {
+65 -51
View File
@@ -1,6 +1,7 @@
package fr.iutfbleau.sae.mpif;
import fr.iutfbleau.sae.mimage.RGBImage;
import fr.iutfbleau.sae.mimage.Pixel;
import fr.iutfbleau.sae.util.BitOutputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
@@ -16,9 +17,9 @@ public class PIFWriter {
throws Exception {
// Création du flux de sortie binaire
FileOutputStream fos =new FileOutputStream(filepath);
BufferedOutputStream bos =new BufferedOutputStream(fos);
BitOutputStream ecriveur =new BitOutputStream(bos);
FileOutputStream fos = new FileOutputStream(filepath);
BufferedOutputStream bos = new BufferedOutputStream(fos);
BitOutputStream ecriveur = new BitOutputStream(bos);
// Écriture de l'en-tête
writeHeader(ecriveur, image.getWidth(), image.getHeight());
@@ -31,96 +32,109 @@ public class PIFWriter {
ecriveur.fermerFlux();
System.err.println("SYSTEME");
System.out.println("Fichier PIF écrit avec succès");
}
// Ecriture de l'en-tête du fichier PIF (largeur et hauteur)
public void writeHeader(BitOutputStream out,int width, int height){
public void writeHeader(BitOutputStream out, int width, int height) {
try {
out.writeBits(width >> 8 & 0xFF, 8); // octet de poids fort
out.writeBits(width & 0xFF, 8); // octet de poids faible
out.writeBits(height >> 8 & 0xFF, 8); // octet de poids fort
out.writeBits(height & 0xFF, 8); // octet de poids faible
out.writeBits(width, 16); // ✅ Simplifié : 16 bits d'un coup
out.writeBits(height, 16); // ✅ Simplifié : 16 bits d'un coup
} catch (Exception e) {
System.err.println("Erreur lors de lécriture de len-tête du fichier PIF");
System.err.println("Erreur lors de l'écriture de l'en-tête du fichier PIF");
}
}
public void writeTables(BitOutputStream out, Map<Integer, String> canonR,
Map<Integer, String> canonG, Map<Integer, String> canonB){
Map<Integer, String> canonG, Map<Integer, String> canonB) {
try {
// Écriture des longueurs des codes canoniques pour chaque composante
for (int i = 0; i < 256; i++) {
int len;
if (canonR.containsKey(i)) { // petite securité (au cas où)
if (canonR.containsKey(i)) {
len = canonR.get(i).length();
}else {
} else {
len = 0;
}
out.writeBits(len, 8);
out.writeBits(len, 8);
}
for (int i = 0; i < 256; i++) {
int len;
if (canonG.containsKey(i)) {
len = canonG.get(i).length();
}else {
} else {
len = 0;
}
out.writeBits(len, 8);
out.writeBits(len, 8);
}
for (int i = 0; i < 256; i++) {
int len;
if (canonB.containsKey(i)) {
len = canonB.get(i).length();
}else {
} else {
len = 0;
}
out.writeBits(len, 8);
}
} catch (IOException e) {
System.err.println("Erreur lors de lécriture des tables de fréquences dans le fichier PIF");
System.err.println("Erreur lors de l'écriture des tables de fréquences dans le fichier PIF");
}
}
private void writeBitFromString(BitOutputStream out, String code){
try {
for (int i = 0; i < code.length(); i++) {
if (code.charAt(i) == '1') {
out.writeBit(1);
} else {
out.writeBit(0);
}
// Debug pour compter les symboles utilisés et calculer les longueurs moyennes
int totalBitsR = 0, totalBitsG = 0, totalBitsB = 0;
int countR = 0, countG = 0, countB = 0;
for (int i = 0; i < 256; i++) {
if (canonR.containsKey(i)) {
totalBitsR += canonR.get(i).length();
countR++;
}
if (canonG.containsKey(i)) {
totalBitsG += canonG.get(i).length();
countG++;
}
if (canonB.containsKey(i)) {
totalBitsB += canonB.get(i).length();
countB++;
}
} catch (IOException e) {
System.err.println("Erreur lors de l’écriture des bits dans le fichier PIF");
}
System.out.println("Longueur moyenne Rouge : " + (totalBitsR / (double)countR));
System.out.println("Longueur moyenne Vert : " + (totalBitsG / (double)countG));
System.out.println("Longueur moyenne Bleu : " + (totalBitsB / (double)countB));
System.out.println("Symboles utilisés - R:" + countR + " G:" + countG + " B:" + countB);
}
// Méthode pour encoder les pixels de l'image en utilisant les codes canoniques
public void encodePixels(BitOutputStream out, RGBImage image, Map<Integer, String> canonRED, Map<Integer, String> canonGREEN, Map<Integer, String> canonBLUE){
public void encodePixels(BitOutputStream out, RGBImage image,
Map<Integer, String> canonRED,
Map<Integer, String> canonGREEN,
Map<Integer, String> canonBLUE) {
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Récupérer les valeurs R, G, B du pixel
int r = image.getPixel(x, y).getR();
int g = image.getPixel(x, y).getG();
int b = image.getPixel(x, y).getB();
// Écrire les codes dans le flux binaire
writeBitFromString(out, canonRED.get(r));
writeBitFromString(out, canonGREEN.get(g));
writeBitFromString(out, canonBLUE.get(b));
}
}
}
try {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Récupérer le pixel une seule fois pour optimiser
Pixel pixel = image.getPixel(x, y);
int r = pixel.getR();
int g = pixel.getG();
int b = pixel.getB();
}
// OPTIMISATION : Utiliser writeBitString() directement
out.writeBitString(canonRED.get(r));
out.writeBitString(canonGREEN.get(g));
out.writeBitString(canonBLUE.get(b));
}
}
} catch (IOException e) {
System.err.println("Erreur lors de l'écriture des pixels dans le fichier PIF");
}
}
}
@@ -109,6 +109,30 @@ public class BitOutputStream {
this.fluxSortie.flush(); // Force l'écriture dans le flux sous-jacent dans le but de vider le buffer
}
/**
* Écrit une séquence de bits à partir d'une chaîne de '0' et '1'.
*
* @param codeBinaire chaîne contenant uniquement '0' et '1'
* @throws IOException si une erreur d'écriture survient
* @throws IllegalArgumentException si la chaîne contient autre chose que '0' ou '1'
*/
public void writeBitString(String codeBinaire) throws IOException {
if (codeBinaire == null) {
throw new IllegalArgumentException("Le code binaire ne peut pas être null");
}
for (int i = 0; i < codeBinaire.length(); i++) {
char c = codeBinaire.charAt(i);
if (c == '0') {
writeBit(0);
} else if (c == '1') {
writeBit(1);
} else {
throw new IllegalArgumentException("Le code binaire ne doit contenir que '0' et '1'");
}
}
}
/**
* Vide les buffers internes et ferme le flux de sortie.
*