Noureddinesa commited on
Commit
8969b1a
1 Parent(s): fd85787

Upload 4 files

Browse files
Files changed (4) hide show
  1. App.py +45 -0
  2. arial.ttf +0 -0
  3. requirements.txt +9 -0
  4. utilitis.py +262 -0
App.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from utilitis import Draw,Change_Image,check_if_changed,Update
3
+ from PIL import Image
4
+ import time
5
+
6
+
7
+
8
+ st.set_page_config(layout='wide')
9
+ st.title("Bienvenue à Textra Web App")
10
+ st.markdown("### Drag and Drop votre facture ici:")
11
+ st.write("(PNG, JPG, JPEG)")
12
+ uploaded_file = st.file_uploader("Ou selectioner une image:", type=["png", "jpg", "jpeg"], accept_multiple_files=False)
13
+
14
+ if uploaded_file is not None:
15
+ image_initiale = Image.open(uploaded_file)
16
+ image_initiale = image_initiale.convert("RGB")
17
+ @st.cache_data
18
+ def process_image(uploaded_file):
19
+ image = Image.open(uploaded_file)
20
+ image = image.convert("RGB")
21
+ return Draw(image)
22
+
23
+ # Process the image and retrieve results
24
+ image, Results,execution_time = process_image(uploaded_file)
25
+ # Execution Time
26
+ st.write(f"Execution Time: {execution_time:.2f} seconds")
27
+ # Change Image
28
+ Change_Image(image,image_initiale)
29
+ # Some Initializations
30
+ sauvgarder_button = st.sidebar.empty()
31
+ success_message = st.sidebar.empty()
32
+ st.sidebar.title('Results')
33
+ # Get Track of User Modeifications :
34
+ New_results = Update(Results)
35
+ # Check if any input has been changed
36
+ if check_if_changed(Results,New_results):
37
+ st.write(check_if_changed(Results,New_results))
38
+ if sauvgarder_button.button("Sauvegarder"):
39
+ success_message.success("Les résultats ont été sauvegardés avec succès !")
40
+ time.sleep(1)
41
+ success_message.empty()
42
+ st.write(New_results)
43
+
44
+
45
+
arial.ttf ADDED
Binary file (915 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ numpy==1.26.4
2
+ paddleocr==2.7.0.3
3
+ paddlepaddle==2.6.0
4
+ pillow==10.2.0
5
+ streamlit==1.33.0
6
+ torch==2.2.2
7
+ torchaudio==2.2.2
8
+ torchvision==0.17.2
9
+ transformers==4.39.2
utilitis.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from paddleocr import PaddleOCR
3
+ from PIL import ImageDraw, ImageFont,ImageEnhance
4
+ import torch
5
+ from transformers import AutoProcessor,LayoutLMv3ForTokenClassification
6
+ import numpy as np
7
+ import time
8
+
9
+ model_Hugging_path = "Noureddinesa/Output_LayoutLMv3_v7"
10
+
11
+
12
+ #############################################################################
13
+ #############################################################################
14
+ def Labels():
15
+ labels = ['InvNum', 'InvDate', 'Fourni', 'TTC', 'TVA', 'TT', 'Autre']
16
+ id2label = {v: k for v, k in enumerate(labels)}
17
+ label2id = {k: v for v, k in enumerate(labels)}
18
+ return id2label, label2id
19
+
20
+ #############################################################################
21
+ #############################################################################
22
+ def Paddle():
23
+ ocr = PaddleOCR(use_angle_cls=False,lang='fr',rec=False)
24
+ return ocr
25
+
26
+ def processbbox(BBOX, width, height):
27
+ bbox = []
28
+ bbox.append(BBOX[0][0])
29
+ bbox.append(BBOX[0][1])
30
+ bbox.append(BBOX[2][0])
31
+ bbox.append(BBOX[2][1])
32
+ #Scaling
33
+ bbox[0]= 1000*bbox[0]/width # X1
34
+ bbox[1]= 1000*bbox[1]/height # Y1
35
+ bbox[2]= 1000*bbox[2]/width # X2
36
+ bbox[3]= 1000*bbox[3]/height # Y2
37
+ for i in range(4):
38
+ bbox[i] = int(bbox[i])
39
+ return bbox
40
+
41
+
42
+ def Preprocess(image):
43
+ image_array = np.array(image)
44
+ ocr = Paddle()
45
+ width, height = image.size
46
+ results = ocr.ocr(image_array, cls=True)
47
+ results = results[0]
48
+ test_dict = {'image': image ,'tokens':[], "bboxes":[]}
49
+ for item in results :
50
+ bbox = processbbox(item[0], width, height)
51
+ test_dict['tokens'].append(item[1][0])
52
+ test_dict['bboxes'].append(bbox)
53
+
54
+ print(test_dict['bboxes'])
55
+ print(test_dict['tokens'])
56
+ return test_dict
57
+
58
+ #############################################################################
59
+ #############################################################################
60
+ def Encode(image):
61
+ example = Preprocess(image)
62
+ image = example["image"]
63
+ words = example["tokens"]
64
+ boxes = example["bboxes"]
65
+ processor = AutoProcessor.from_pretrained(model_Hugging_path, apply_ocr=False)
66
+ encoding = processor(image, words, boxes=boxes,return_offsets_mapping=True,truncation=True, max_length=512, padding="max_length", return_tensors="pt")
67
+ offset_mapping = encoding.pop('offset_mapping')
68
+ return encoding, offset_mapping,words
69
+ #############################################################################
70
+ #############################################################################
71
+ def unnormalize_box(bbox, width, height):
72
+ return [
73
+ width * (bbox[0] / 1000),
74
+ height * (bbox[1] / 1000),
75
+ width * (bbox[2] / 1000),
76
+ height * (bbox[3] / 1000),
77
+ ]
78
+ #############################################################################
79
+ #############################################################################
80
+ def Run_model(image):
81
+ encoding,offset_mapping,words = Encode(image)
82
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
83
+ # load the fine-tuned model from the hub
84
+ model = LayoutLMv3ForTokenClassification.from_pretrained(model_Hugging_path)
85
+ model.to(device)
86
+ # forward pass
87
+ outputs = model(**encoding)
88
+
89
+ predictions = outputs.logits.argmax(-1).squeeze().tolist()
90
+ token_boxes = encoding.bbox.squeeze().tolist()
91
+
92
+ width, height = image.size
93
+
94
+ id2label, _ = Labels()
95
+ is_subword = np.array(offset_mapping.squeeze().tolist())[:,0] != 0
96
+ true_predictions = [id2label[pred] for idx, pred in enumerate(predictions) if not is_subword[idx]]
97
+ true_boxes = [unnormalize_box(box, width, height) for idx, box in enumerate(token_boxes) if not is_subword[idx]]
98
+ return true_predictions,true_boxes,words
99
+
100
+ #############################################################################
101
+ #############################################################################
102
+ def Get_Json(true_predictions,words):
103
+ Results = {}
104
+ i = 0
105
+ for prd in true_predictions:
106
+ if prd in ['InvNum','Fourni', 'InvDate','TT','TTC','TVA']:
107
+ #print(i,prd,words[i-1])
108
+ Results[prd] = words[i-1]
109
+ i+=1
110
+ key_mapping = {'InvNum':'Numéro de facture','Fourni':'Fournisseur', 'InvDate':'Date Facture','TT':'Total HT','TTC':'Total TTC','TVA':'TVA'}
111
+ Results = {key_mapping.get(key, key): value for key, value in Results.items()}
112
+ return Results
113
+
114
+ #############################################################################
115
+ #############################################################################
116
+ def Draw(image):
117
+ start_time = time.time()
118
+
119
+ image = enhance_image(image,1.3,1.5)
120
+ true_predictions, true_boxes,words = Run_model(image)
121
+ draw = ImageDraw.Draw(image)
122
+
123
+ label2color = {
124
+ 'InvNum': 'blue',
125
+ 'InvDate': 'green',
126
+ 'Fourni': 'orange',
127
+ 'TTC':'purple',
128
+ 'TVA': 'magenta',
129
+ 'TT': 'red',
130
+ 'Autre': 'black'
131
+ }
132
+
133
+ # Adjust the thickness of the rectangle outline and label text position
134
+ rectangle_thickness = 4
135
+ label_x_offset = 20
136
+ label_y_offset = -30
137
+
138
+ # Custom font size
139
+ custom_font_size = 25
140
+
141
+ # Load a font with the custom size
142
+ font_path = "arial.ttf" # Specify the path to your font file
143
+ custom_font = ImageFont.truetype(font_path, custom_font_size)
144
+
145
+ for prediction, box in zip(true_predictions, true_boxes):
146
+ predicted_label = prediction
147
+ # Check if the predicted label exists in the label2color dictionary
148
+ if predicted_label in label2color:
149
+ color = label2color[predicted_label]
150
+ else:
151
+ color = 'black' # Default color if label is not found
152
+ if predicted_label != "Autre":
153
+ draw.rectangle(box, outline=color, width=rectangle_thickness)
154
+ # Draw text using the custom font and size
155
+ draw.rectangle((box[0], box[1]+ label_y_offset,box[2],box[3]+ label_y_offset), fill=color)
156
+ draw.text((box[0] + label_x_offset, box[1] + label_y_offset), text=predicted_label, fill='white', font=custom_font)
157
+
158
+ # Get the Results Json File
159
+ Results = Get_Json(true_predictions,words)
160
+
161
+ end_time = time.time()
162
+ execution_time = end_time - start_time
163
+
164
+ return image,Results,execution_time
165
+
166
+ #############################################################################
167
+ #############################################################################
168
+
169
+ def Add_Results(data):
170
+ # Render the table
171
+ for key, value in data.items():
172
+ data[key] = st.sidebar.text_input(key, value)
173
+ #############################################################################
174
+ #############################################################################
175
+
176
+ def check_if_changed(original_values, updated_values):
177
+ for key, value in original_values.items():
178
+ if updated_values[key] != value:
179
+ return True
180
+ return False
181
+ #############################################################################
182
+ #############################################################################
183
+
184
+ def Update(Results):
185
+ New_results = {}
186
+
187
+ if "Fournisseur" in Results.keys():
188
+ text_fourni = st.sidebar.text_input("Fournisseur", value=Results["Fournisseur"])
189
+ New_results["Fournisseur"] = text_fourni
190
+
191
+ if "Date Facture" in Results.keys():
192
+ text_InvDate = st.sidebar.text_input("Date Facture", value=Results["Date Facture"])
193
+ New_results["Date Facture"] = text_InvDate
194
+
195
+ if "Numéro de facture" in Results.keys():
196
+ text_InvNum = st.sidebar.text_input("Numéro de facture", value=Results["Numéro de facture"])
197
+ New_results["Numéro de facture"] = text_InvNum
198
+
199
+ if "Total HT" in Results.keys():
200
+ text_TT = st.sidebar.text_input("Total HT", value=Results["Total HT"])
201
+ New_results["Total HT"] = text_TT
202
+
203
+ if "TVA" in Results.keys():
204
+ text_TVA = st.sidebar.text_input("TVA", value=Results["TVA"])
205
+ New_results["TVA"] = text_TVA
206
+
207
+ if "Total TTC" in Results.keys():
208
+ text_TTC = st.sidebar.text_input("TTC", value=Results["Total TTC"])
209
+ New_results["Total TTC"] = text_TTC
210
+ return New_results
211
+
212
+ #############################################################################
213
+ #############################################################################
214
+ def Change_Image(image1,image2):
215
+ # Initialize session state
216
+ if 'current_image' not in st.session_state:
217
+ st.session_state.current_image = 'image1'
218
+
219
+ # Button to switch between images
220
+ if st.sidebar.button('Switcher'):
221
+ if st.session_state.current_image == 'image1':
222
+ st.session_state.current_image = 'image2'
223
+ else:
224
+ st.session_state.current_image = 'image1'
225
+ # Display the selected image
226
+ if st.session_state.current_image == 'image1':
227
+ st.image(image1, caption='Output', use_column_width=True)
228
+ else:
229
+ st.image(image2, caption='Image initiale', use_column_width=True)
230
+
231
+ #############################################################################
232
+ #############################################################################
233
+ def enhance_image(image,brightness_factor, contrast_factor):
234
+ enhancer = ImageEnhance.Brightness(image)
235
+ brightened_image = enhancer.enhance(brightness_factor)
236
+ enhancer = ImageEnhance.Contrast(brightened_image)
237
+ enhanced_image = enhancer.enhance(contrast_factor)
238
+ return enhanced_image
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+