ngupta2026 commited on
Commit
95e66f9
·
verified ·
1 Parent(s): 52c61b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -52
app.py CHANGED
@@ -1,61 +1,73 @@
1
- import pytesseract
2
  from PIL import Image
3
  import torch
 
 
 
4
 
5
- from transformers import LayoutLMTokenizerFast, LayoutLMForTokenClassification
 
6
 
 
7
 
 
8
  # LABELS
 
 
 
9
 
10
- label2id = {"O":0, "COMPANY":1, "DATE":2, "TOTAL":3}
11
- id2label = {v:k for k,v in label2id.items()}
12
-
13
- # ✅ USE YOUR MODEL
14
  MODEL_NAME = "ngupta2026/sroie-layoutlm"
15
 
16
-
17
-
18
  model = LayoutLMForTokenClassification.from_pretrained(MODEL_NAME)
19
  tokenizer = LayoutLMTokenizerFast.from_pretrained(MODEL_NAME)
20
-
21
- # NORMALIZE
22
-
23
-
24
-
25
-
26
-
27
-
 
 
 
 
 
 
 
28
  def normalize(box, width, height):
29
  return [
30
  int(1000 * box[0] / width),
31
  int(1000 * box[3] / height),
32
  ]
33
 
 
 
 
 
34
 
35
- # MAIN FUNCTION
36
-
37
- def process(image):
38
-
39
-
40
- data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
41
 
42
  words = []
43
  boxes = []
44
 
45
  for i in range(len(data["text"])):
46
- if data["text"][i].strip() != "":
47
 
 
48
 
49
- x = data["left"][i]
50
- y = data["top"][i]
51
- w = data["width"][i]
52
  h = data["height"][i]
53
 
54
- words.append(data["text"][i])
55
- boxes.append([x, y, x+w, y+h])
56
-
57
-
58
 
 
 
59
 
60
 
61
  width, height = image.size
@@ -65,13 +77,10 @@ def process(image):
65
  encoding = tokenizer(
66
  words,
67
  boxes=boxes,
68
- padding="max_length",
69
- truncation=True,
70
- is_split_into_words=True,
71
- max_length=128
72
  )
73
 
74
-
75
 
76
 
77
  with torch.no_grad():
@@ -79,50 +88,127 @@ def process(image):
79
 
80
  predictions = torch.argmax(outputs.logits, dim=2)[0][:len(words)]
81
 
82
- result = {}
83
-
84
-
85
-
 
86
 
87
  for word, pred in zip(words, predictions):
88
 
89
  label = id2label[pred.item()]
90
 
91
-
92
  if label == "COMPANY":
93
- result.setdefault("company", []).append(word)
94
- elif label == "DATE":
95
- result.setdefault("date", []).append(word)
96
- elif label == "TOTAL":
97
- result.setdefault("total", []).append(word)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
 
 
 
 
99
 
 
100
 
 
 
 
 
101
 
 
 
102
 
 
103
 
 
 
104
 
 
105
 
 
 
 
106
 
 
107
 
 
 
 
108
 
 
 
 
 
109
 
 
110
 
 
 
 
 
 
 
 
 
 
 
111
 
 
112
 
 
 
113
 
 
 
 
 
114
 
115
- return result
116
 
 
 
117
 
118
- # UI
119
 
120
- demo = gr.Interface(
121
- fn=process,
122
- inputs=gr.Image(type="pil"),
123
- outputs="json",
124
- title="Document AI Extractor"
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  )
127
 
128
  demo.launch()
 
 
1
  from PIL import Image
2
  import torch
3
+ import re
4
+ import os
5
+ import smtplib
6
 
7
+ from email.mime.text import MIMEText
8
+ from email.mime.multipart import MIMEMultipart
9
 
10
+ from transformers import LayoutLMTokenizerFast, LayoutLMForTokenClassification
11
 
12
+ # =====================================================
13
  # LABELS
14
+ # =====================================================
15
+ label2id = {"O": 0, "COMPANY": 1, "DATE": 2, "TOTAL": 3}
16
+ id2label = {v: k for k, v in label2id.items()}
17
 
18
+ # =====================================================
19
+ # LOAD MODEL
20
+ # =====================================================
 
21
  MODEL_NAME = "ngupta2026/sroie-layoutlm"
22
 
 
 
23
  model = LayoutLMForTokenClassification.from_pretrained(MODEL_NAME)
24
  tokenizer = LayoutLMTokenizerFast.from_pretrained(MODEL_NAME)
25
+ model.to(device)
26
+ model.eval()
27
+
28
+ # =====================================================
29
+ # EMAIL CONFIG
30
+ # Add these in Hugging Face Space Secrets:
31
+ # EMAIL_USER = yourgmail@gmail.com
32
+ # EMAIL_PASS = your_app_password
33
+ # =====================================================
34
+ EMAIL_USER = os.getenv("EMAIL_USER")
35
+ EMAIL_PASS = os.getenv("EMAIL_PASS")
36
+
37
+ # =====================================================
38
+ # NORMALIZE BOXES
39
+ # =====================================================
40
  def normalize(box, width, height):
41
  return [
42
  int(1000 * box[0] / width),
43
  int(1000 * box[3] / height),
44
  ]
45
 
46
+ # =====================================================
47
+ # EXTRACT DATA
48
+ # =====================================================
49
+ def extract_receipt(image):
50
 
51
+ data = pytesseract.image_to_data(
52
+ image,
53
+ output_type=pytesseract.Output.DICT
54
+ )
 
 
55
 
56
  words = []
57
  boxes = []
58
 
59
  for i in range(len(data["text"])):
 
60
 
61
+ text = data["text"][i].strip()
62
 
63
+ if text != "":
 
 
64
  h = data["height"][i]
65
 
66
+ words.append(text)
67
+ boxes.append([x, y, x + w, y + h])
 
 
68
 
69
+ if len(words) == 0:
70
+ return {"error": "No text detected"}
71
 
72
 
73
  width, height = image.size
 
77
  encoding = tokenizer(
78
  words,
79
  boxes=boxes,
80
+ max_length=512
 
 
 
81
  )
82
 
83
+ encoding = {k: v.to(device) for k, v in encoding.items()}
84
 
85
 
86
  with torch.no_grad():
 
88
 
89
  predictions = torch.argmax(outputs.logits, dim=2)[0][:len(words)]
90
 
91
+ result = {
92
+ "company": [],
93
+ "date": [],
94
+ "total": []
95
+ }
96
 
97
  for word, pred in zip(words, predictions):
98
 
99
  label = id2label[pred.item()]
100
 
101
+ # company from model
102
  if label == "COMPANY":
103
+ result["company"].append(word)
104
+
105
+ # date from regex
106
+ if re.search(r"\d{2}[/-]\d{2}[/-]\d{2,4}", word):
107
+ result["date"].append(word)
108
+
109
+ # total from regex
110
+ if re.search(r"\d+(\.\d{2})?", word):
111
+ try:
112
+ value = float(word.replace(",", ""))
113
+ if value > 50:
114
+ result["total"].append(word)
115
+ except:
116
+ pass
117
+
118
+ result["company"] = (
119
+ " ".join(result["company"])
120
+ if result["company"] else "Not Found"
121
+ )
122
+
123
+ result["date"] = (
124
+ result["date"][0]
125
+ if result["date"] else "Not Found"
126
+ )
127
 
128
+ result["total"] = (
129
+ result["total"][-1]
130
+ if result["total"] else "Not Found"
131
+ )
132
 
133
+ return result
134
 
135
+ # =====================================================
136
+ # SEND EMAIL
137
+ # =====================================================
138
+ def send_claim_email(to_email, extracted):
139
 
140
+ if not EMAIL_USER or not EMAIL_PASS:
141
+ return "Email secrets not configured."
142
 
143
+ subject = "Insurance Claim Request"
144
 
145
+ body = f"""
146
+ Dear Claims Team,
147
 
148
+ I would like to request reimbursement for an eligible expense.
149
 
150
+ Provider Name: {extracted['company']}
151
+ Bill Date: {extracted['date']}
152
+ Claim Amount: ₹{extracted['total']}
153
 
154
+ Please process the claim.
155
 
156
+ Regards
157
+ Customer
158
+ """
159
 
160
+ msg = MIMEMultipart()
161
+ msg["From"] = EMAIL_USER
162
+ msg["To"] = to_email
163
+ msg["Subject"] = subject
164
 
165
+ msg.attach(MIMEText(body, "plain"))
166
 
167
+ try:
168
+ server = smtplib.SMTP("smtp.gmail.com", 587)
169
+ server.starttls()
170
+ server.login(EMAIL_USER, EMAIL_PASS)
171
+ server.sendmail(
172
+ EMAIL_USER,
173
+ to_email,
174
+ msg.as_string()
175
+ )
176
+ server.quit()
177
 
178
+ return f"✅ Email sent successfully to {to_email}"
179
 
180
+ except Exception as e:
181
+ return f"❌ Email failed: {str(e)}"
182
 
183
+ # =====================================================
184
+ # MAIN UI FUNCTION
185
+ # =====================================================
186
+ def process_and_send(image, email_id):
187
 
188
+ extracted = extract_receipt(image)
189
 
190
+ if "error" in extracted:
191
+ return extracted, extracted["error"]
192
 
193
+ email_status = send_claim_email(email_id, extracted)
194
 
195
+ return extracted, email_status
 
 
 
 
196
 
197
+ # =====================================================
198
+ # UI
199
+ # =====================================================
200
+ demo = gr.Interface(
201
+ fn=process_and_send,
202
+ inputs=[
203
+ gr.Image(type="pil", label="Upload Receipt"),
204
+ gr.Textbox(label="Insurance Email ID")
205
+ ],
206
+ outputs=[
207
+ gr.JSON(label="Extracted Data"),
208
+ gr.Textbox(label="Email Status")
209
+ ],
210
+ title="📄 AI Insurance Claim Generator",
211
+ description="Upload receipt → Extract details → Auto send claim email"
212
  )
213
 
214
  demo.launch()