Dabococo commited on
Commit
f9bb48d
·
verified ·
1 Parent(s): d74da64

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -46
app.py CHANGED
@@ -16,43 +16,63 @@ def image_to_base64(image):
16
  image.save(buffered, format="JPEG")
17
  return base64.b64encode(buffered.getvalue()).decode("utf-8")
18
 
19
- def parse_markdown_table_to_df(text):
20
  """Parse un tableau Markdown en Pandas DataFrame de manière robuste."""
21
- # Extraire le tableau Markdown avec une regex
22
- table_pattern = r'(\|.*?\n(?:\|[-| :]*?\n)?(?:\|.*?\n)+)'
23
- match = re.search(table_pattern, text, re.DOTALL)
24
- if not match:
25
- return pd.DataFrame({"Erreur": ["Aucun tableau Markdown trouvé dans la réponse"]})
26
 
27
- table_text = match.group(1).strip()
28
  lines = table_text.split('\n')
29
 
30
  # Ignorer les lignes vides et la ligne des séparateurs (|---|)
31
- lines = [line.strip() for line in lines if line.strip() and not re.match(r'\|[-| :]*\|', line)]
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- if not lines:
34
- return pd.DataFrame({"Erreur": ["Tableau vide ou mal formé"]})
 
 
35
 
36
- # Extraire les en-têtes
37
- headers = [h.strip() for h in lines[0].split('|')[1:-1]] # Ignore les | aux extrémités
38
- if not headers:
39
  return pd.DataFrame({"Erreur": ["Aucun en-tête valide trouvé"]})
40
 
41
- # Extraire les lignes de données
42
  rows = []
43
- for line in lines[1:]:
44
- cells = [cell.strip() for cell in line.split('|')[1:-1]] # Ignore les | aux extrémités
45
- if len(cells) == len(headers): # Vérifie l'alignement
46
- rows.append(cells)
47
- else:
48
- # Si le nombre de colonnes ne correspond pas, remplir avec des chaînes vides
49
- cells.extend([''] * (len(headers) - len(cells)))
50
- rows.append(cells[:len(headers)])
 
51
 
52
  # Créer le DataFrame
53
  df = pd.DataFrame(rows, columns=headers)
54
  return df if not df.empty else pd.DataFrame({"Erreur": ["Aucune donnée valide extraite"]})
55
 
 
 
 
 
 
 
 
 
 
 
 
56
  def process_image_and_get_response(image):
57
  """Process the uploaded image, send to Groq vision model, parse response to table, and generate Excel."""
58
  if image is None:
@@ -61,13 +81,20 @@ def process_image_and_get_response(image):
61
  # Convert image to base64
62
  base64_image = image_to_base64(image)
63
 
64
- # Prompt optimisé pour un Markdown propre
65
  prompt = (
66
- "Extrait le tableau en entier de cette image et recopie-le à l'identique ici au format Markdown. "
67
- "Utilise des | pour les colonnes et une ligne |---|---| pour les séparateurs. "
68
- "N'inclus aucun texte avant ou après le tableau. "
69
- "Assure-toi que chaque ligne a exactement le même nombre de colonnes que les en-têtes, "
70
- "en remplissant les cellules vides avec '' si nécessaire."
 
 
 
 
 
 
 
71
  )
72
 
73
  try:
@@ -85,8 +112,8 @@ def process_image_and_get_response(image):
85
  ]
86
  }
87
  ],
88
- temperature=0.5,
89
- max_completion_tokens=2048,
90
  top_p=1,
91
  stream=False,
92
  stop=None
@@ -94,21 +121,27 @@ def process_image_and_get_response(image):
94
 
95
  response = completion.choices[0].message.content.strip()
96
 
97
- # Parse la réponse en DataFrame
98
- df = parse_markdown_table_to_df(response)
 
 
 
 
99
  excel_file = None
100
- if not df.empty and "Erreur" not in df.columns:
101
- # Crée un fichier Excel temporaire
102
- with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
103
- with pd.ExcelWriter(tmp.name, engine='openpyxl') as writer:
104
  df.to_excel(writer, sheet_name='Tableau_Extrait', index=False)
105
- excel_file = tmp.name
106
- else:
107
- # Si parsing échoue, crée un Excel avec un message d'erreur
108
- with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
109
- with pd.ExcelWriter(tmp.name, engine='openpyxl') as writer:
110
- df.to_excel(writer, sheet_name='Erreur', index=False)
111
- excel_file = tmp.name
 
 
112
 
113
  return response, excel_file
114
 
@@ -120,11 +153,11 @@ iface = gr.Interface(
120
  fn=process_image_and_get_response,
121
  inputs=gr.Image(type="pil", label="Uploader une image contenant un tableau"),
122
  outputs=[
123
- gr.Textbox(label="Réponse de l'IA (tableau Markdown pour copier-coller)"),
124
- gr.File(label="Télécharger le fichier Excel")
125
  ],
126
  title="Extraction de Tableau depuis Image avec Groq et Export Excel",
127
- description="Uploader une image avec un tableau. L'IA extrait et formate en Markdown, puis génère un Excel rempli."
128
  )
129
 
130
  # Launch the interface
 
16
  image.save(buffered, format="JPEG")
17
  return base64.b64encode(buffered.getvalue()).decode("utf-8")
18
 
19
+ def parse_markdown_table_to_df(table_text):
20
  """Parse un tableau Markdown en Pandas DataFrame de manière robuste."""
21
+ # Nettoyer les <br> en \n pour les sauts de ligne dans les cellules
22
+ table_text = re.sub(r'<br>', '\n', table_text)
 
 
 
23
 
 
24
  lines = table_text.split('\n')
25
 
26
  # Ignorer les lignes vides et la ligne des séparateurs (|---|)
27
+ data_lines = []
28
+ separator_found = False
29
+ for line in lines:
30
+ stripped = line.strip()
31
+ if not stripped:
32
+ continue
33
+ if re.match(r'\|[-| :]+\|', stripped):
34
+ separator_found = True
35
+ continue
36
+ data_lines.append(stripped)
37
+
38
+ if not data_lines:
39
+ return pd.DataFrame({"Erreur": ["Aucun tableau Markdown trouvé dans la réponse"]})
40
 
41
+ # Extraire les en-têtes (première ligne)
42
+ header_line = data_lines[0]
43
+ headers = [h.strip() for h in header_line.split('|')[1:-1]]
44
+ num_columns = len(headers)
45
 
46
+ if num_columns == 0:
 
 
47
  return pd.DataFrame({"Erreur": ["Aucun en-tête valide trouvé"]})
48
 
49
+ # Extraire les lignes de données (lignes suivantes)
50
  rows = []
51
+ for line in data_lines[1:]:
52
+ cells = line.split('|')[1:-1]
53
+ cleaned_cells = [cell.strip() for cell in cells]
54
+ # Gérer le mismatch de colonnes
55
+ if len(cleaned_cells) < num_columns:
56
+ cleaned_cells.extend([''] * (num_columns - len(cleaned_cells)))
57
+ elif len(cleaned_cells) > num_columns:
58
+ cleaned_cells = cleaned_cells[:num_columns] # Tronquer si trop de colonnes
59
+ rows.append(cleaned_cells)
60
 
61
  # Créer le DataFrame
62
  df = pd.DataFrame(rows, columns=headers)
63
  return df if not df.empty else pd.DataFrame({"Erreur": ["Aucune donnée valide extraite"]})
64
 
65
+ def extract_additional_text_and_table(response):
66
+ """Extraire le texte additionnel et le tableau Markdown de la réponse structurée."""
67
+ if 'Additional text:' in response and 'Table:' in response:
68
+ parts = response.split('Table:', 1)
69
+ additional_text = parts[0].replace('Additional text:', '').strip()
70
+ table_text = parts[1].strip() if len(parts) > 1 else ''
71
+ else:
72
+ additional_text = ''
73
+ table_text = response.strip()
74
+ return additional_text, table_text
75
+
76
  def process_image_and_get_response(image):
77
  """Process the uploaded image, send to Groq vision model, parse response to table, and generate Excel."""
78
  if image is None:
 
81
  # Convert image to base64
82
  base64_image = image_to_base64(image)
83
 
84
+ # Prompt optimisé pour structure, précision et inclusion des infos supplémentaires
85
  prompt = (
86
+ "Analyse l'image et extrait tout le contenu. "
87
+ "D'abord, extrait tout texte additionnel autour ou en dehors du tableau (titres, notes, pieds de page, etc.), en le recopiant mot pour mot. "
88
+ "Ensuite, extrait le tableau en entier, en recopiant toutes les lignes et colonnes à l'identique, y compris les lignes vides ou partielles. "
89
+ "Utilise un format Markdown pour le tableau avec des | pour les colonnes et une ligne |---|---| pour les séparateurs. "
90
+ "Assure-toi que CHAQUE ligne (en-têtes, séparateurs, données) a EXACTEMENT le même nombre de colonnes (compte les | : il doit y en avoir 10 pour 9 colonnes, incluant les | de début et fin). "
91
+ "Pour les sauts de ligne dans une cellule, utilise \n au lieu de <br>. "
92
+ "Remplis les cellules vides avec '' si nécessaire pour maintenir l'alignement. "
93
+ "N'ajoute aucun texte explicatif dans le tableau. "
94
+ "Structure ta réponse exactement comme suit :\n"
95
+ "Additional text: [tout le texte additionnel, s'il y en a ; sinon, laisse vide]\n"
96
+ "Table:\n"
97
+ "[le tableau Markdown ici]"
98
  )
99
 
100
  try:
 
112
  ]
113
  }
114
  ],
115
+ temperature=0.2, # Réduit pour plus de précision et respect de la structure
116
+ max_completion_tokens=4096, # Augmenté pour tableaux complexes
117
  top_p=1,
118
  stream=False,
119
  stop=None
 
121
 
122
  response = completion.choices[0].message.content.strip()
123
 
124
+ # Extraire texte additionnel et tableau
125
+ additional_text, table_text = extract_additional_text_and_table(response)
126
+
127
+ # Parse le tableau en DataFrame
128
+ df = parse_markdown_table_to_df(table_text)
129
+
130
  excel_file = None
131
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
132
+ with pd.ExcelWriter(tmp.name, engine='openpyxl') as writer:
133
+ # Feuille pour le tableau
134
+ if not df.empty and "Erreur" not in df.columns:
135
  df.to_excel(writer, sheet_name='Tableau_Extrait', index=False)
136
+ else:
137
+ pd.DataFrame({"Message": ["Erreur lors du parsing du tableau"]}).to_excel(writer, sheet_name='Tableau_Extrait', index=False)
138
+
139
+ # Feuille pour le texte additionnel, si présent
140
+ if additional_text:
141
+ additional_df = pd.DataFrame({"Texte Additionnel": additional_text.split('\n')})
142
+ additional_df.to_excel(writer, sheet_name='Infos_Supplementaires', index=False)
143
+
144
+ excel_file = tmp.name
145
 
146
  return response, excel_file
147
 
 
153
  fn=process_image_and_get_response,
154
  inputs=gr.Image(type="pil", label="Uploader une image contenant un tableau"),
155
  outputs=[
156
+ gr.Textbox(label="Réponse de l'IA (texte additionnel + tableau Markdown pour copier-coller)"),
157
+ gr.File(label="Télécharger le fichier Excel (avec tableau et infos supp.)")
158
  ],
159
  title="Extraction de Tableau depuis Image avec Groq et Export Excel",
160
+ description="Uploader une image avec un tableau. L'IA extrait le texte additionnel et le tableau, puis génère un Excel avec des feuilles séparées."
161
  )
162
 
163
  # Launch the interface