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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -33
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  import numpy as np
3
  import unicodedata
4
  from paddleocr import PaddleOCR
 
5
 
6
  # -------------------------------------------------
7
  # OCR
@@ -12,7 +13,7 @@ ocr = PaddleOCR(
12
  )
13
 
14
  # -------------------------------------------------
15
- # Normalisation texte
16
  # -------------------------------------------------
17
  def normalize(text: str) -> str:
18
  text = text.lower()
@@ -21,19 +22,25 @@ def normalize(text: str) -> str:
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,8 +53,8 @@ def extract_second_column(image):
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,43 +67,45 @@ def extract_second_column(image):
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)
@@ -104,8 +113,7 @@ def extract_second_column(image):
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,24 +126,25 @@ def extract_second_column(image):
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
  # -------------------------------------------------
@@ -143,8 +152,11 @@ 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)
 
2
  import numpy as np
3
  import unicodedata
4
  from paddleocr import PaddleOCR
5
+ from sklearn.cluster import KMeans
6
 
7
  # -------------------------------------------------
8
  # OCR
 
13
  )
14
 
15
  # -------------------------------------------------
16
+ # Normalisation texte (casse + accents)
17
  # -------------------------------------------------
18
  def normalize(text: str) -> str:
19
  text = text.lower()
 
22
  return " ".join(text.split())
23
 
24
  # -------------------------------------------------
25
+ # Titres valides de la colonne 2
26
  # -------------------------------------------------
27
+ COL_TITLES = {
28
+ "designation",
29
+ "designations",
30
+ "description",
31
+ "description des services"
32
  }
33
 
34
+ # -------------------------------------------------
35
+ # Mots / lignes à ignorer
36
+ # -------------------------------------------------
37
+ IGNORE_KEYWORDS = {
38
+ "prix", "total", "ht", "htva", "tva",
39
+ "ttc", "general", "generale"
40
+ }
41
 
42
  # -------------------------------------------------
43
+ # Fonction principale
44
  # -------------------------------------------------
45
  def extract_second_column(image):
46
  if image is None:
 
53
  return "OCR : aucun texte détecté."
54
 
55
  data = result[0]
56
+ texts = data.get("rec_texts", [])
57
+ boxes = data.get("dt_polys", [])
58
 
59
  blocks = []
60
  for text, box in zip(texts, boxes):
 
67
 
68
  blocks.append((t, x, y))
69
 
70
+ if len(blocks) < 5:
71
+ return "Pas assez de texte exploitable."
72
+
73
  # -------------------------------------------------
74
+ # 1. Détection du X de la colonne cible via son titre
75
  # -------------------------------------------------
 
76
  col_x = None
 
77
  for text, x, y in blocks:
78
+ if normalize(text) in COL_TITLES:
79
  col_x = x
 
80
  break
81
 
82
  if col_x is None:
83
+ return "Titre de la colonne cible non détecté."
84
 
85
  # -------------------------------------------------
86
+ # 2. Sélection des blocs proches du X détecté
87
  # -------------------------------------------------
88
+ X_THRESHOLD = 45
89
  column_blocks = [
90
  (t, x, y) for t, x, y in blocks
91
+ if abs(x - col_x) < X_THRESHOLD
92
  ]
93
 
94
  if not column_blocks:
95
+ return "Colonne détectée mais vide."
96
 
97
  # -------------------------------------------------
98
+ # 3. Tri vertical (haut → bas)
99
  # -------------------------------------------------
100
  column_blocks.sort(key=lambda e: e[2])
101
 
102
  # -------------------------------------------------
103
+ # 4. Fusion intelligente des lignes OCR
104
  # -------------------------------------------------
105
  merged = []
106
  current = ""
107
  last_y = None
108
+ Y_THRESHOLD = 22
109
 
110
  for text, x, y in column_blocks:
111
  nt = normalize(text)
 
113
  if any(k in nt for k in IGNORE_KEYWORDS):
114
  continue
115
 
116
+ if last_y is None or abs(y - last_y) > Y_THRESHOLD:
 
117
  if current:
118
  merged.append(current.strip())
119
  current = text
 
126
  merged.append(current.strip())
127
 
128
  # -------------------------------------------------
129
+ # 5. Nettoyage final (cellules texte uniquement)
130
  # -------------------------------------------------
131
  final = []
132
  for line in merged:
133
+ nt = normalize(line)
134
+ if len(nt) < 4:
135
  continue
136
+ if sum(c.isdigit() for c in line) > len(line) / 2:
 
137
  continue
 
138
  final.append(line)
139
 
140
  if not final:
141
+ return "Aucune cellule texte valide trouvée."
142
 
143
+ # -------------------------------------------------
144
+ # 6. Résultat numéroté
145
+ # -------------------------------------------------
146
  return "\n".join(f"{i+1}. {line}" for i, line in enumerate(final))
147
 
 
148
  # -------------------------------------------------
149
  # Interface Gradio
150
  # -------------------------------------------------
 
152
  fn=extract_second_column,
153
  inputs=gr.Image(type="pil", label="Image du tableau"),
154
  outputs=gr.Textbox(label="Contenu de la colonne 2"),
155
+ title="Extraction fiable de la colonne 2 (Désignation / Description)",
156
+ description=(
157
+ "Extraction robuste de la deuxième colonne des tableaux scannés "
158
+ "(Désignation, DESIGNATIONS, Description, Description des services)."
159
+ )
160
  )
161
 
162
  demo.launch(server_name="0.0.0.0", server_port=7860)