kebson commited on
Commit
db4d3c4
·
verified ·
1 Parent(s): 76b7d95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -56
app.py CHANGED
@@ -1,103 +1,102 @@
1
  import gradio as gr
2
- from PIL import Image
3
  import cv2
4
- import pytesseract
5
  import numpy as np
 
 
6
 
7
- # 🔴 IMPORTANT : chemin ABSOLU vers tesseract (HF Docker)
8
- pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"
 
 
 
 
9
 
10
 
11
  def extract_descriptions(image: Image.Image):
12
- """
13
- Extrait uniquement le contenu de la colonne 'Description'
14
- depuis une image de facture (tableau).
15
- """
16
  if image is None:
17
  return "Aucune image fournie."
18
 
19
- # Conversion PIL -> OpenCV
20
  img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
21
 
22
- # OCR avec positions
23
- data = pytesseract.image_to_data(
24
- img,
25
- output_type=pytesseract.Output.DICT,
26
- config="--psm 6"
27
- )
28
 
29
  words = []
30
- for i in range(len(data["text"])):
31
- txt = data["text"][i].strip()
32
- if txt:
33
- words.append({
34
- "text": txt,
35
- "x": data["left"][i],
36
- "y": data["top"][i],
37
- "w": data["width"][i],
38
- "h": data["height"][i]
39
- })
40
-
41
- # 1️⃣ Détection de l'en-tête "Description"
 
 
 
 
 
42
  header = next(
43
- (w for w in words if w["text"].lower() == "description"),
44
  None
45
  )
46
 
47
  if header is None:
48
  return "❌ Colonne 'Description' non détectée."
49
 
50
- # 2️⃣ Définition de la zone de la colonne Description
51
- x_min = header["x"] - 10
52
- x_max = header["x"] + header["w"] + 350
53
  y_min = header["y"] + header["h"] + 10
54
 
55
- # 3️⃣ Filtrage des mots dans cette colonne
56
  column_words = [
57
  w for w in words
58
  if x_min <= w["x"] <= x_max and w["y"] > y_min
59
  ]
60
 
61
- # 4️⃣ Regroupement par lignes (Y proche)
62
  lines = {}
63
  for w in column_words:
64
- key = w["y"] // 15
65
  lines.setdefault(key, []).append(w)
66
 
67
- extracted_lines = []
68
- for key in sorted(lines.keys()):
69
- line_words = sorted(lines[key], key=lambda x: x["x"])
70
- line_text = " ".join(w["text"] for w in line_words)
 
71
 
72
- # Filtrage des éléments non désirés
73
- if any(k in line_text.lower() for k in ["vat", "gross", "net", "each"]):
 
74
  continue
75
- if line_text.replace(".", "").replace(",", "").isdigit():
76
  continue
77
 
78
- extracted_lines.append(line_text)
79
 
80
- # 5️⃣ Fusion des cellules multilignes
81
- final_descriptions = []
82
  buffer = ""
83
 
84
- for line in extracted_lines:
85
- # Détection de début de nouvelle ligne de cellule (ex: "1.")
86
  if line[:2].replace(".", "").isdigit():
87
  if buffer:
88
- final_descriptions.append(buffer.strip())
89
  buffer = line.split(".", 1)[-1].strip()
90
  else:
91
  buffer += " " + line
92
 
93
  if buffer:
94
- final_descriptions.append(buffer.strip())
95
 
96
- # Résultat final
97
- if not final_descriptions:
98
- return "⚠️ Aucun contenu détecté dans la colonne Description."
99
 
100
- return "\n".join(final_descriptions)
101
 
102
 
103
  # =========================
@@ -108,11 +107,10 @@ demo = gr.Interface(
108
  fn=extract_descriptions,
109
  inputs=gr.Image(type="pil", label="Image de facture"),
110
  outputs=gr.Textbox(lines=20, label="Descriptions extraites"),
111
- title="Extraction de la colonne Description (Factures)",
112
  description=(
113
- "Charge une image de facture contenant un tableau "
114
- "et récupère uniquement le contenu de la colonne 'Description', "
115
- "cellule par cellule."
116
  )
117
  )
118
 
 
1
  import gradio as gr
 
2
  import cv2
 
3
  import numpy as np
4
+ from PIL import Image
5
+ from paddleocr import PaddleOCR
6
 
7
+ # Initialisation OCR (CPU, stable HF)
8
+ ocr = PaddleOCR(
9
+ use_angle_cls=True,
10
+ lang="en",
11
+ use_gpu=False
12
+ )
13
 
14
 
15
  def extract_descriptions(image: Image.Image):
 
 
 
 
16
  if image is None:
17
  return "Aucune image fournie."
18
 
 
19
  img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
20
 
21
+ # OCR Paddle
22
+ result = ocr.ocr(img, cls=True)
 
 
 
 
23
 
24
  words = []
25
+ for line in result[0]:
26
+ box, (text, score) = line
27
+ if score < 0.5:
28
+ continue
29
+
30
+ x_coords = [p[0] for p in box]
31
+ y_coords = [p[1] for p in box]
32
+
33
+ words.append({
34
+ "text": text.strip(),
35
+ "x": min(x_coords),
36
+ "y": min(y_coords),
37
+ "w": max(x_coords) - min(x_coords),
38
+ "h": max(y_coords) - min(y_coords)
39
+ })
40
+
41
+ # 1️⃣ Détecter l'en-tête "Description"
42
  header = next(
43
+ (w for w in words if "description" in w["text"].lower()),
44
  None
45
  )
46
 
47
  if header is None:
48
  return "❌ Colonne 'Description' non détectée."
49
 
50
+ # 2️⃣ Définir la zone de la colonne
51
+ x_min = header["x"] - 15
52
+ x_max = header["x"] + header["w"] + 380
53
  y_min = header["y"] + header["h"] + 10
54
 
 
55
  column_words = [
56
  w for w in words
57
  if x_min <= w["x"] <= x_max and w["y"] > y_min
58
  ]
59
 
60
+ # 3️⃣ Grouper par lignes
61
  lines = {}
62
  for w in column_words:
63
+ key = int(w["y"] // 18)
64
  lines.setdefault(key, []).append(w)
65
 
66
+ raw_lines = []
67
+ for k in sorted(lines):
68
+ line = " ".join(
69
+ w["text"] for w in sorted(lines[k], key=lambda x: x["x"])
70
+ )
71
 
72
+ # Filtrage facture
73
+ low = line.lower()
74
+ if any(x in low for x in ["vat", "gross", "net", "total", "each"]):
75
  continue
76
+ if line.replace(".", "").replace(",", "").isdigit():
77
  continue
78
 
79
+ raw_lines.append(line)
80
 
81
+ # 4️⃣ Fusion cellules multilignes
82
+ final = []
83
  buffer = ""
84
 
85
+ for line in raw_lines:
 
86
  if line[:2].replace(".", "").isdigit():
87
  if buffer:
88
+ final.append(buffer.strip())
89
  buffer = line.split(".", 1)[-1].strip()
90
  else:
91
  buffer += " " + line
92
 
93
  if buffer:
94
+ final.append(buffer.strip())
95
 
96
+ if not final:
97
+ return "⚠️ Aucun texte extrait."
 
98
 
99
+ return "\n".join(final)
100
 
101
 
102
  # =========================
 
107
  fn=extract_descriptions,
108
  inputs=gr.Image(type="pil", label="Image de facture"),
109
  outputs=gr.Textbox(lines=20, label="Descriptions extraites"),
110
+ title="Extraction colonne Description – PaddleOCR",
111
  description=(
112
+ "OCR robuste basé sur PaddleOCR. "
113
+ "Extraction automatique des cellules de la colonne Description."
 
114
  )
115
  )
116