Marsouuu commited on
Commit
b3d7d0c
·
1 Parent(s): 7a46795

Amélioration robuste du traitement PDF avec PyMuPDF sans dépendance à Poppler

Browse files
Files changed (1) hide show
  1. app.py +160 -139
app.py CHANGED
@@ -4,47 +4,11 @@ import os
4
  import json
5
  import time
6
  import base64
7
- import subprocess
8
  from PIL import Image
9
- from pdf2image import convert_from_path
10
-
11
- # Vérification de la disponibilité de Poppler
12
- def check_poppler():
13
- poppler_path = None
14
- potential_paths = [
15
- '/usr/bin',
16
- '/usr/local/bin',
17
- '/opt/homebrew/bin',
18
- '/app/bin'
19
- ]
20
-
21
- for path in potential_paths:
22
- if os.path.exists(os.path.join(path, 'pdftoppm')):
23
- poppler_path = path
24
- print(f"✅ Poppler trouvé dans: {poppler_path}")
25
- return poppler_path
26
-
27
- # Essayer de localiser avec la commande which
28
- try:
29
- which_result = subprocess.run(['which', 'pdftoppm'], capture_output=True, text=True)
30
- if which_result.returncode == 0:
31
- poppler_bin = which_result.stdout.strip()
32
- poppler_path = os.path.dirname(poppler_bin)
33
- print(f"✅ Poppler trouvé via 'which' dans: {poppler_path}")
34
- return poppler_path
35
- except:
36
- pass
37
-
38
- print("⚠️ AVERTISSEMENT: Poppler non trouvé dans les chemins standards!")
39
- print("⚠️ Les fonctionnalités de prévisualisation et de traitement PDF pourraient ne pas fonctionner.")
40
- print("⚠️ Veuillez installer Poppler:")
41
- print(" - Linux: apt-get install poppler-utils")
42
- print(" - macOS: brew install poppler")
43
- print(" - Windows: Téléchargez depuis https://github.com/oschwartz10612/poppler-windows/")
44
- return None
45
-
46
- # Vérifier Poppler au démarrage
47
- POPPLER_PATH = check_poppler()
48
 
49
  # Configuration
50
  GOOGLE_API_KEY = "AIzaSyA4ma5pE1pPCzHHn-i9tDWuKqQEgSltMtI"
@@ -884,42 +848,55 @@ def format_table(table_data):
884
 
885
  def process_single_image(image):
886
  """Process a single image and extract information"""
887
- response = model.generate_content(
888
- [GEMINI_PROMPT, image],
889
- generation_config={
890
- "temperature": 0.1,
891
- "top_p": 0.8,
892
- "top_k": 40,
893
- "max_output_tokens": 2048,
894
- }
895
- )
896
-
897
  try:
898
- response_text = response.text.strip()
899
- if response_text.startswith("```json"):
900
- response_text = response_text.replace("```json", "").replace("```", "").strip()
901
- elif response_text.startswith("```"):
902
- response_text = response_text.replace("```", "").strip()
 
 
 
 
 
903
 
904
- json_data = json.loads(response_text)
905
-
906
- # Vérifier et corriger le format des tableaux si nécessaire
907
- if "tables" in json_data and json_data["tables"]:
908
- for i, table in enumerate(json_data["tables"]):
909
- if "data" not in table or not table["data"]:
910
- table["data"] = []
911
-
912
- # S'assurer que la table a un titre
913
- if "title" not in table or not table["title"]:
914
- table["title"] = f"Tableau {i+1}"
915
-
916
- # S'assurer que la table a une description
917
- if "description" not in table:
918
- table["description"] = ""
919
-
920
- return json_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
921
  except Exception as e:
922
- print(f"Error parsing JSON: {str(e)}")
923
  return {"error": str(e)}
924
 
925
  def merge_results(results):
@@ -958,46 +935,76 @@ def process_document(file, progress=gr.Progress()):
958
 
959
  try:
960
  if file.name.lower().endswith('.pdf'):
961
- # Utiliser le chemin Poppler détecté au démarrage
962
- if POPPLER_PATH:
963
- images = convert_from_path(
964
- file.name,
965
- poppler_path=POPPLER_PATH,
966
- use_pdftocairo=True,
967
- dpi=150
968
- )
969
- else:
970
- print("Trying without poppler_path")
971
- images = convert_from_path(
972
- file.name,
973
- use_pdftocairo=True,
974
- dpi=150
975
- )
976
 
977
- if len(images) > 10:
978
- return {"error": TEXT["error"]["too_many_pages"]}
 
979
 
980
- results = []
981
- for i, image in enumerate(images):
982
- progress(i / len(images), desc=TEXT["processing"])
983
- result = process_single_image(image)
984
- if result and "error" not in result:
985
- results.append(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
986
 
987
- if results:
988
- return merge_results(results)
989
- else:
990
- return {"error": TEXT["error"]["no_info"]}
 
991
 
992
  elif file.name.lower().endswith(('.png', '.jpg', '.jpeg')):
993
- image = Image.open(file.name)
994
- return process_single_image(image)
 
 
 
 
995
 
996
  else:
997
  return {"error": TEXT["error"]["file_not_found"]}
998
 
999
  except Exception as e:
1000
- print(f"Error processing document: {str(e)}")
1001
  return {"error": str(e)}
1002
 
1003
  def update_preview(file):
@@ -1005,45 +1012,59 @@ def update_preview(file):
1005
  if not file:
1006
  return []
1007
 
1008
- if file.name.lower().endswith('.pdf'):
1009
- try:
1010
- # Utiliser le chemin Poppler détecté au démarrage
1011
- if POPPLER_PATH:
1012
- images = convert_from_path(
1013
- file.name,
1014
- first_page=1,
1015
- last_page=3,
1016
- poppler_path=POPPLER_PATH,
1017
- use_pdftocairo=True,
1018
- dpi=150
1019
- )
1020
- else:
1021
- # Essayer sans spécifier le chemin, en utilisant des options simplifiées
1022
- print("Trying without poppler_path")
1023
- images = convert_from_path(
1024
- file.name,
1025
- first_page=1,
1026
- last_page=3,
1027
- use_pdftocairo=True,
1028
- dpi=150
1029
- )
1030
-
1031
- image_paths = []
1032
 
1033
- for i, img in enumerate(images):
1034
- temp_filename = f"temp_preview_{i}.jpg"
1035
- img.save(temp_filename)
1036
- image_paths.append(temp_filename)
1037
 
1038
- print(f"Successfully created {len(image_paths)} preview images")
1039
- return image_paths
1040
- except Exception as e:
1041
- print(f"Error converting PDF to images: {str(e)}")
1042
- # En cas d'erreur, retourner une image d'erreur qui sera affichée
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1043
  return []
1044
- elif file.name.lower().endswith(('.png', '.jpg', '.jpeg')):
1045
- return [file.name]
1046
- else:
1047
  return []
1048
 
1049
  def process_and_display(file):
 
4
  import json
5
  import time
6
  import base64
7
+ import fitz # PyMuPDF pour la manipulation des PDF
8
  from PIL import Image
9
+ import io
10
+ import tempfile
11
+ import shutil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # Configuration
14
  GOOGLE_API_KEY = "AIzaSyA4ma5pE1pPCzHHn-i9tDWuKqQEgSltMtI"
 
848
 
849
  def process_single_image(image):
850
  """Process a single image and extract information"""
 
 
 
 
 
 
 
 
 
 
851
  try:
852
+ print("Envoi de l'image à Gemini pour analyse...")
853
+ response = model.generate_content(
854
+ [GEMINI_PROMPT, image],
855
+ generation_config={
856
+ "temperature": 0.1,
857
+ "top_p": 0.8,
858
+ "top_k": 40,
859
+ "max_output_tokens": 2048,
860
+ }
861
+ )
862
 
863
+ try:
864
+ response_text = response.text.strip()
865
+ print(f"Réponse reçue de Gemini, longueur: {len(response_text)} caractères")
866
+
867
+ # Nettoyage du texte JSON
868
+ if response_text.startswith("```json"):
869
+ response_text = response_text.replace("```json", "").replace("```", "").strip()
870
+ elif response_text.startswith("```"):
871
+ response_text = response_text.replace("```", "").strip()
872
+
873
+ # Parse JSON
874
+ json_data = json.loads(response_text)
875
+
876
+ # Vérifier et corriger le format des tableaux si nécessaire
877
+ if "tables" in json_data and json_data["tables"]:
878
+ for i, table in enumerate(json_data["tables"]):
879
+ if "data" not in table or not table["data"]:
880
+ table["data"] = []
881
+
882
+ # S'assurer que la table a un titre
883
+ if "title" not in table or not table["title"]:
884
+ table["title"] = f"Tableau {i+1}"
885
+
886
+ # S'assurer que la table a une description
887
+ if "description" not in table:
888
+ table["description"] = ""
889
+
890
+ return json_data
891
+ except json.JSONDecodeError as e:
892
+ print(f"Erreur de décodage JSON: {str(e)}")
893
+ print(f"Contenu problématique: {response_text[:500]}...")
894
+ return {"error": "Erreur de format JSON dans la réponse"}
895
+ except Exception as e:
896
+ print(f"Erreur lors du traitement de la réponse Gemini: {str(e)}")
897
+ return {"error": str(e)}
898
  except Exception as e:
899
+ print(f"Erreur lors de l'appel à Gemini: {str(e)}")
900
  return {"error": str(e)}
901
 
902
  def merge_results(results):
 
935
 
936
  try:
937
  if file.name.lower().endswith('.pdf'):
938
+ # Créer une copie temporaire du fichier au cas où il serait déplacé/modifié pendant le traitement
939
+ temp_dir = tempfile.mkdtemp()
940
+ temp_pdf = os.path.join(temp_dir, "temp.pdf")
941
+ shutil.copy2(file.name, temp_pdf)
 
 
 
 
 
 
 
 
 
 
 
942
 
943
+ try:
944
+ # Ouvrir le PDF avec PyMuPDF
945
+ doc = fitz.open(temp_pdf)
946
 
947
+ if doc.page_count > 10:
948
+ # Nettoyer les fichiers temporaires
949
+ shutil.rmtree(temp_dir, ignore_errors=True)
950
+ return {"error": TEXT["error"]["too_many_pages"]}
951
+
952
+ print(f"Traitement d'un PDF de {doc.page_count} pages")
953
+ results = []
954
+
955
+ for i in range(doc.page_count):
956
+ progress((i+1) / doc.page_count, desc=f"{TEXT['processing']} page {i+1}/{doc.page_count}")
957
+ try:
958
+ page = doc[i]
959
+ # Augmenter la résolution pour une meilleure qualité
960
+ zoom = 2.0 # zoom factor
961
+ mat = fitz.Matrix(zoom, zoom)
962
+ pix = page.get_pixmap(matrix=mat, alpha=False)
963
+
964
+ # Convertir le pixmap en PIL Image
965
+ img_data = pix.tobytes("jpeg")
966
+ pil_img = Image.open(io.BytesIO(img_data))
967
+
968
+ # Traiter l'image avec le modèle Gemini
969
+ result = process_single_image(pil_img)
970
+ if result and "error" not in result:
971
+ results.append(result)
972
+ print(f"Page {i+1} traitée avec succès")
973
+ else:
974
+ print(f"Pas d'informations extraites de la page {i+1}")
975
+ except Exception as e:
976
+ print(f"Erreur lors du traitement de la page {i+1}: {str(e)}")
977
+
978
+ # Fermer le document PDF
979
+ doc.close()
980
+
981
+ # Nettoyer les fichiers temporaires
982
+ shutil.rmtree(temp_dir, ignore_errors=True)
983
+
984
+ if results:
985
+ return merge_results(results)
986
+ else:
987
+ return {"error": TEXT["error"]["no_info"]}
988
 
989
+ except Exception as e:
990
+ print(f"Erreur lors du traitement du PDF: {str(e)}")
991
+ # Nettoyer les fichiers temporaires
992
+ shutil.rmtree(temp_dir, ignore_errors=True)
993
+ return {"error": str(e)}
994
 
995
  elif file.name.lower().endswith(('.png', '.jpg', '.jpeg')):
996
+ try:
997
+ image = Image.open(file.name)
998
+ return process_single_image(image)
999
+ except Exception as e:
1000
+ print(f"Erreur lors du traitement de l'image: {str(e)}")
1001
+ return {"error": str(e)}
1002
 
1003
  else:
1004
  return {"error": TEXT["error"]["file_not_found"]}
1005
 
1006
  except Exception as e:
1007
+ print(f"Erreur inattendue dans process_document: {str(e)}")
1008
  return {"error": str(e)}
1009
 
1010
  def update_preview(file):
 
1012
  if not file:
1013
  return []
1014
 
1015
+ try:
1016
+ if file.name.lower().endswith('.pdf'):
1017
+ # Créer une copie temporaire du fichier au cas où il serait déplacé/modifié pendant le traitement
1018
+ temp_dir = tempfile.mkdtemp()
1019
+ temp_pdf = os.path.join(temp_dir, "temp.pdf")
1020
+ shutil.copy2(file.name, temp_pdf)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1021
 
1022
+ try:
1023
+ # Utiliser PyMuPDF pour convertir les pages en images
1024
+ doc = fitz.open(temp_pdf)
1025
+ image_paths = []
1026
 
1027
+ # Ne traiter que les 3 premières pages
1028
+ max_pages = min(3, doc.page_count)
1029
+ print(f"PDF a {doc.page_count} pages, prévisualisant {max_pages} pages")
1030
+
1031
+ for i in range(max_pages):
1032
+ try:
1033
+ page = doc[i]
1034
+ # Augmenter la résolution pour une meilleure qualité
1035
+ zoom = 2.0 # zoom factor
1036
+ mat = fitz.Matrix(zoom, zoom)
1037
+ pix = page.get_pixmap(matrix=mat, alpha=False)
1038
+
1039
+ # Sauvegarder l'image
1040
+ temp_filename = f"temp_preview_{i}.jpg"
1041
+ pix.save(temp_filename, "jpeg")
1042
+ image_paths.append(temp_filename)
1043
+ print(f"Page {i+1} convertie et sauvegardée dans {temp_filename}")
1044
+ except Exception as e:
1045
+ print(f"Erreur lors du traitement de la page {i+1}: {str(e)}")
1046
+
1047
+ # Fermer le document PDF
1048
+ doc.close()
1049
+ print(f"Prévisualisation créée avec succès: {len(image_paths)} images")
1050
+
1051
+ # Nettoyer les fichiers temporaires
1052
+ shutil.rmtree(temp_dir, ignore_errors=True)
1053
+
1054
+ return image_paths
1055
+ except Exception as e:
1056
+ print(f"Erreur lors de la conversion PDF: {str(e)}")
1057
+ # Nettoyer les fichiers temporaires
1058
+ shutil.rmtree(temp_dir, ignore_errors=True)
1059
+ return []
1060
+
1061
+ elif file.name.lower().endswith(('.png', '.jpg', '.jpeg')):
1062
+ return [file.name]
1063
+ else:
1064
+ print(f"Format de fichier non pris en charge: {file.name}")
1065
  return []
1066
+ except Exception as e:
1067
+ print(f"Erreur inattendue dans update_preview: {str(e)}")
 
1068
  return []
1069
 
1070
  def process_and_display(file):