kebson commited on
Commit
76bdf65
·
verified ·
1 Parent(s): ed975bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -60
app.py CHANGED
@@ -4,7 +4,7 @@ import unicodedata
4
  from paddleocr import PaddleOCR
5
 
6
  # -------------------------------------------------
7
- # OCR (compatible Hugging Face)
8
  # -------------------------------------------------
9
  ocr = PaddleOCR(
10
  lang="fr",
@@ -12,7 +12,7 @@ ocr = PaddleOCR(
12
  )
13
 
14
  # -------------------------------------------------
15
- # Normalisation texte (casse + accents)
16
  # -------------------------------------------------
17
  def normalize(text: str) -> str:
18
  text = text.lower()
@@ -21,33 +21,19 @@ def normalize(text: str) -> str:
21
  return " ".join(text.split())
22
 
23
  # -------------------------------------------------
24
- # Titres valides de la colonne 2
25
- # -------------------------------------------------
26
- COL_TITLES = {
27
- "designation",
28
- "designations",
29
- "description",
30
- "description des services"
31
- }
32
-
33
- # -------------------------------------------------
34
- # Mots / lignes à ignorer
35
  # -------------------------------------------------
 
36
  IGNORE_KEYWORDS = {
37
- "prix", "total", "ht", "htva", "tva",
38
- "ttc", "general", "generale"
39
  }
40
 
41
- # -------------------------------------------------
42
- # Métadonnées à exclure (hors tableau)
43
- # -------------------------------------------------
44
- META_KEYWORDS = {
45
- "dpo", "dao", "ref", "reference",
46
- "date", "nme", ":"
47
- }
48
 
49
  # -------------------------------------------------
50
- # Fonction principale
51
  # -------------------------------------------------
52
  def extract_second_column(image):
53
  if image is None:
@@ -60,8 +46,8 @@ def extract_second_column(image):
60
  return "OCR : aucun texte détecté."
61
 
62
  data = result[0]
63
- texts = data.get("rec_texts", [])
64
- boxes = data.get("dt_polys", [])
65
 
66
  blocks = []
67
  for text, box in zip(texts, boxes):
@@ -74,61 +60,52 @@ def extract_second_column(image):
74
 
75
  blocks.append((t, x, y))
76
 
77
- if len(blocks) < 5:
78
- return "Pas assez de texte exploitable."
79
-
80
  # -------------------------------------------------
81
- # 1. Détection du X de la colonne cible (par le titre)
82
  # -------------------------------------------------
83
- col_x = None
84
  title_y = None
 
85
 
86
  for text, x, y in blocks:
87
- if normalize(text) in COL_TITLES:
88
  col_x = x
89
  title_y = y
90
  break
91
 
92
  if col_x is None:
93
- return "Titre de la colonne cible non détecté."
94
 
95
  # -------------------------------------------------
96
- # 2. Sélection des blocs de la colonne (SOUS le titre)
97
  # -------------------------------------------------
98
- X_THRESHOLD = 45
99
  column_blocks = [
100
  (t, x, y) for t, x, y in blocks
101
- if abs(x - col_x) < X_THRESHOLD and y > title_y
102
  ]
103
 
104
  if not column_blocks:
105
- return "Colonne détectée mais vide."
106
 
107
  # -------------------------------------------------
108
- # 3. Tri vertical (haut → bas)
109
  # -------------------------------------------------
110
  column_blocks.sort(key=lambda e: e[2])
111
 
112
  # -------------------------------------------------
113
- # 4. Fusion contrôlée des lignes OCR
114
  # -------------------------------------------------
115
  merged = []
116
  current = ""
117
  last_y = None
118
- Y_THRESHOLD = 22
119
 
120
  for text, x, y in column_blocks:
121
  nt = normalize(text)
122
 
123
- # Ignore lignes de totaux / prix
124
  if any(k in nt for k in IGNORE_KEYWORDS):
125
  continue
126
 
127
- # Ignore métadonnées résiduelles
128
- if any(k in nt for k in META_KEYWORDS):
129
- continue
130
-
131
- if last_y is None or abs(y - last_y) > Y_THRESHOLD:
132
  if current:
133
  merged.append(current.strip())
134
  current = text
@@ -141,40 +118,33 @@ def extract_second_column(image):
141
  merged.append(current.strip())
142
 
143
  # -------------------------------------------------
144
- # 5. Nettoyage final (cellules texte métier uniquement)
145
  # -------------------------------------------------
146
  final = []
147
  for line in merged:
148
- nt = normalize(line)
149
-
150
- if len(nt) < 4:
151
  continue
152
 
153
- if sum(c.isdigit() for c in line) > len(line) / 2:
154
  continue
155
 
156
  final.append(line)
157
 
158
  if not final:
159
- return "Aucune cellule texte valide trouvée."
160
 
161
- # -------------------------------------------------
162
- # 6. Résultat numéroté
163
- # -------------------------------------------------
164
  return "\n".join(f"{i+1}. {line}" for i, line in enumerate(final))
165
 
 
166
  # -------------------------------------------------
167
- # Interface Gradio (Hugging Face)
168
  # -------------------------------------------------
169
  demo = gr.Interface(
170
  fn=extract_second_column,
171
  inputs=gr.Image(type="pil", label="Image du tableau"),
172
  outputs=gr.Textbox(label="Contenu de la colonne 2"),
173
- title="Extraction fiable de la colonne 2",
174
- description=(
175
- "Extraction robuste de la deuxième colonne des tableaux scannés "
176
- "(Désignation, DESIGNATIONS, Description, Description des services)."
177
- )
178
  )
179
 
180
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
4
  from paddleocr import PaddleOCR
5
 
6
  # -------------------------------------------------
7
+ # OCR
8
  # -------------------------------------------------
9
  ocr = PaddleOCR(
10
  lang="fr",
 
12
  )
13
 
14
  # -------------------------------------------------
15
+ # Normalisation texte
16
  # -------------------------------------------------
17
  def normalize(text: str) -> str:
18
  text = text.lower()
 
21
  return " ".join(text.split())
22
 
23
  # -------------------------------------------------
24
+ # Paramètres spécifiques image "Description des services"
 
 
 
 
 
 
 
 
 
 
25
  # -------------------------------------------------
26
+ COLUMN_TITLE = "description des services"
27
  IGNORE_KEYWORDS = {
28
+ "prix", "total", "ht", "htva", "tva", "ttc",
29
+ "general", "generale"
30
  }
31
 
32
+ X_THRESHOLD = 45
33
+ Y_NEW_CELL = 32 # seuil volontairement élevé empêche fusion abusive
 
 
 
 
 
34
 
35
  # -------------------------------------------------
36
+ # Extraction colonne 2
37
  # -------------------------------------------------
38
  def extract_second_column(image):
39
  if image is None:
 
46
  return "OCR : aucun texte détecté."
47
 
48
  data = result[0]
49
+ texts = data["rec_texts"]
50
+ boxes = data["dt_polys"]
51
 
52
  blocks = []
53
  for text, box in zip(texts, boxes):
 
60
 
61
  blocks.append((t, x, y))
62
 
 
 
 
63
  # -------------------------------------------------
64
+ # 1. Trouver le titre exact de la colonne
65
  # -------------------------------------------------
 
66
  title_y = None
67
+ col_x = None
68
 
69
  for text, x, y in blocks:
70
+ if normalize(text) == COLUMN_TITLE:
71
  col_x = x
72
  title_y = y
73
  break
74
 
75
  if col_x is None:
76
+ return "Titre 'Description des services' non détecté."
77
 
78
  # -------------------------------------------------
79
+ # 2. Garder uniquement le texte SOUS le titre
80
  # -------------------------------------------------
 
81
  column_blocks = [
82
  (t, x, y) for t, x, y in blocks
83
+ if abs(x - col_x) < X_THRESHOLD and y > title_y + 15
84
  ]
85
 
86
  if not column_blocks:
87
+ return "Aucune cellule détectée sous la colonne."
88
 
89
  # -------------------------------------------------
90
+ # 3. Tri vertical
91
  # -------------------------------------------------
92
  column_blocks.sort(key=lambda e: e[2])
93
 
94
  # -------------------------------------------------
95
+ # 4. Fusion contrôlée (évite fusion Tuyau / Coude)
96
  # -------------------------------------------------
97
  merged = []
98
  current = ""
99
  last_y = None
 
100
 
101
  for text, x, y in column_blocks:
102
  nt = normalize(text)
103
 
 
104
  if any(k in nt for k in IGNORE_KEYWORDS):
105
  continue
106
 
107
+ # nouvelle cellule
108
+ if last_y is None or abs(y - last_y) > Y_NEW_CELL:
 
 
 
109
  if current:
110
  merged.append(current.strip())
111
  current = text
 
118
  merged.append(current.strip())
119
 
120
  # -------------------------------------------------
121
+ # 5. Nettoyage final
122
  # -------------------------------------------------
123
  final = []
124
  for line in merged:
125
+ if not line[0].isupper():
 
 
126
  continue
127
 
128
+ if sum(c.isdigit() for c in line) > len(line) * 0.3:
129
  continue
130
 
131
  final.append(line)
132
 
133
  if not final:
134
+ return "Aucune cellule valide trouvée."
135
 
 
 
 
136
  return "\n".join(f"{i+1}. {line}" for i, line in enumerate(final))
137
 
138
+
139
  # -------------------------------------------------
140
+ # Interface Gradio
141
  # -------------------------------------------------
142
  demo = gr.Interface(
143
  fn=extract_second_column,
144
  inputs=gr.Image(type="pil", label="Image du tableau"),
145
  outputs=gr.Textbox(label="Contenu de la colonne 2"),
146
+ title="Extraction fiable Colonne 2 (Description des services)",
147
+ description="Extraction robuste et ordonnée des cellules texte."
 
 
 
148
  )
149
 
150
  demo.launch(server_name="0.0.0.0", server_port=7860)