petit push sprint 3 fini

This commit is contained in:
AlgaLaptop
2026-01-05 10:19:59 +01:00
parent d20ef2c406
commit 6ec6ac85a4
52 changed files with 1059 additions and 842 deletions
+225 -43
View File
@@ -1,9 +1,14 @@
package fr.iutfbleau.sae.mpif;
import fr.iutfbleau.sae.mimage.RGBImage;
import fr.iutfbleau.sae.util.BitInputStream;
import fr.iutfbleau.sae.util.BitInputStream;
import fr.iutfbleau.sae.util.DecodeNode;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -15,73 +20,250 @@ public class PIFReader {
private int[] lenG;
private int[] lenB;
public RGBImage read(String filepath)
throws Exception {
// j'Utilise d'un BufferedInputStream pour une meilleure performance
FileInputStream fis = new FileInputStream(filepath);
BufferedInputStream bis = new BufferedInputStream(fis);
BitInputStream lecteur = new BitInputStream(bis);
/**
* Lit et décode un fichier PIF.
*
* @param filepath chemin du fichier PIF
* @return l'image RGB décodée
* @throws Exception si erreur de lecture
*/
public RGBImage decodePifFile(File file) throws Exception {
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bos = new BufferedInputStream(fis);
BitInputStream lecteur = new BitInputStream(bos);
// je lis l'entête et les tables canoniques
// Je lis l'en-tête et les tables canoniques
this.readHeader(lecteur);
this.readCanonicalTables(lecteur);
// je reconstructe les tables canoniques car dans le fichier on a juste les longueurs en bits
// Je reconstruis les tables canoniques car dans le fichier on a juste les longueurs en bits
Map<String, Integer> canonR = rebuildCanonical(lenR);
Map<String, Integer> canonG = rebuildCanonical(lenG);
Map<String, Integer> canonB = rebuildCanonical(lenB);
// Je construis les arbres de décodage
DecodeNode trieR = buildDecodageTree(canonR);
DecodeNode trieG = buildDecodageTree(canonG);
DecodeNode trieB = buildDecodageTree(canonB);
// Je décode les pixels
RGBImage img = decodePixels(lecteur, trieR, trieG, trieB);
lecteur.closeFlux();
System.out.println("Fichier PIF lu avec succès : " + width + "x" + height);
return img;
}
public void readHeader(BitInputStream in) {
// La largeur et l'hauteur de l'image occupe chaqun deux octets soit 16 bits :
/**
* Lit l'en-tête du fichier PIF (largeur et hauteur sur 16 bits chacune).
* @throws IOException si erreur de lecture
*/
public void readHeader(BitInputStream in) throws IOException {
this.width = in.readBits(16);
this.height = in.readBits(16);
System.out.println("Dimensions lues : " + this.width + "x" + this.height);
}
public void readCanonicalTables(BitInputStream in) {
this.lenR = new int[256];
this.lenG = new int[256];
this.lenB = new int[256];
/**
* Lit les trois tables de longueurs (R, G, B).
* Chaque table contient 256 valeurs sur 5 bits.
* @throws IOException si erreur de lecture
*/
public void readCanonicalTables(BitInputStream in) throws IOException {
// Table Rouge
this.lenR = new int[256];
for (int i = 0; i < 256; i++){
lenR[i] = in.readBits(8);
this.lenR[i] = in.readBits(8);
}
for (int j = 0; j < 256; j++){
lenG[j] = in.readBits(8);
// Table Vert
this.lenG = new int[256];
for (int i = 0; i < 256; i++){
this.lenG[i] = in.readBits(8);
}
// Table Bleu
this.lenB = new int[256];
for (int i = 0; i < 256; i++){
this.lenB[i] = in.readBits(8);
}
System.out.println("Tables de longueurs lues");
}
/**
* Reconstruit les codes canoniques à partir des longueurs.
*
* @param lengths tableau de 256 longueurs
* @return Map<code, symbole> pour le décodage
*/
public Map<String, Integer> rebuildCanonical(int[] lengths) {
// je cree une liste de paires (symbole, longueur)
List<Map.Entry<Integer, Integer>> entiers = new ArrayList<>();
for (int i = 0; i < lengths.length; i++) {
if (lengths[i] > 0) {
entiers.add(new java.util.AbstractMap.SimpleEntry<>(i, lengths[i]));
}
}
// Je trie par longueur croissante, puis par symbole croissant
entiers.sort((a, b) -> {
int cmp = a.getValue().compareTo(b.getValue());
if (cmp != 0) return cmp;
return a.getKey().compareTo(b.getKey());
});
// je genere les codes canoniques
Map<String, Integer> codes = new HashMap<>();
int code = 0;
int previousLength = 0;
for (Map.Entry<Integer, Integer> entry : entiers) {
int symbol = entry.getKey();
int length = entry.getValue();
// Decalage pour aligner le code sur la longueur courante
code <<= (length - previousLength);
// je convertit ce code en texte binaire
String codeStr = Integer.toBinaryString(code);
// On s'assure que la chaîne a la bonne longueur en ajoutant des zéros à gauche si nécessaire
while (codeStr.length() < length) {
codeStr = "0" + codeStr;
}
// je restocke le code + symbole en inversant car la map est inverse dans l'encodage
codes.put(codeStr, symbol);
code++;
previousLength = length;
}
return codes;
}
/**
* Construit l'arbre de décodage à partir des codes canoniques.
*
* @param codes Map<String (code binaire), symbole> les codes canoniques avec les bits comme clés et les symboles comme valeurs
* @return la racine de l'arbre de décodage
*/
public DecodeNode buildDecodageTree(Map<String,Integer> codes) {
DecodeNode root = new DecodeNode(); // la racine de larbre
for (Map.Entry<String, Integer> entry : codes.entrySet()) {
String code = entry.getKey(); // 101011101110 par exemple
int symbol = entry.getValue(); // 0,1,2,3 etc. par exemple
DecodeNode current = root;
// je parcours le code bit à bit
for (int i = 0; i < code.length(); i++) {
char bit = code.charAt(i);
if(i == code.length() - 1) {
// Dernier bit: je cree une feuille avec la valeur du symbol
if(bit == '0') {
current.left = new DecodeNode(null, null, symbol);
} else {
current.right = new DecodeNode(null, null, symbol);
}
} else {
// Si c'est pas le dernier bit : je creer un node interne
if(bit == '0') {
if(current.left == null) {
current.left = new DecodeNode(); // Node interne
}
current = current.left;
} else {
if(current.right == null) {
current.right = new DecodeNode(); // node intern
}
current = current.right;
}
}
}
}
return root;
}
/**
* Décode les pixels à partir des arbres de décodage.
*
* @param in flux d'entrée binaire
* @param red arbre de décodage pour la composante rouge
* @param green arbre de décodage pour la composante verte
* @param blue arbre de décodage pour la composante bleue
* @return l'image reconstruite
*/
public RGBImage decodePixels(BitInputStream in, DecodeNode red, DecodeNode green, DecodeNode blue) throws IOException{
RGBImage image = new RGBImage(width, height);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
// je decode chaque composante en parcourant son arbre
int r = decodeSymbole(in, red);
int g = decodeSymbole(in, green);
int b = decodeSymbole(in, blue);
// je cree et je place le pixel
Pixel pixel = new Pixel(r, g, b);
image.setPixel(x, y, pixel);
}
}
return image;
}
/**
* Décode un symbole en parcourant l'arbre bit par bit
* @param in le flux d'entrée binaire
* @param root la racine de l'arbre de décodage
* @return le symbole décodé (valeur entre 0 et 255)
* @throws IOException si une erreur d'entrée/sortie se produit
*/
private int decodeSymbole(BitInputStream in, DecodeNode root) throws IOException {
DecodeNode current = root;
// je parcours l'arbre en suivant les bits du flux jusqu'à atteindre une feuille
while (!current.isLeaf()) {
int bit = in.readBit();
if (bit == 0) {
current = current.left;
} else {
current = current.right;
}
if (current == null) {
throw new IOException("code invalide: noeud null rencontre");
}
}
// si on est arrivé à une feuille, on retourne la valeur
if (current.value == -1) {
throw new IOException("Feuille sans valeur assignée");
}
return current.value;
}
public static boolean isPIFFile(File f) {
if (f == null){
return false;
}
if (!f.exists() || !f.isFile()){
return false;
}
//je verifi l'extension
String name = f.getName().toLowerCase();
if (!name.endsWith(".pif")) {
return false;
}
for (int k = 0; k < 256; k++){
lenB[k] = in.readBits(8);
}
}
public Map<String,Integer> rebuildCanonical(int[] lengths) {
// TODO: Implement canonical table reconstruction
return null;
return f.length() >= 772; // taille minimal pour un fichier pif longueur largeur et tables de frequance
}
public RGBImage decodePixels(BitInputStream in, DecodeNode red, DecodeNode green, DecodeNode blue) {
// TODO: Implement pixel decoding
return null;
}
public DecodeNode buildDecodageTree(Map<String,Integer> codes) {
// TODO: Implement trie building
return null;
}
}