Loren commited on
Commit
23d8470
1 Parent(s): d3373b1

Custom multipage

Browse files
app_pages/__pycache__/ocr_comparator.cpython-37.pyc CHANGED
Binary files a/app_pages/__pycache__/ocr_comparator.cpython-37.pyc and b/app_pages/__pycache__/ocr_comparator.cpython-37.pyc differ
 
app_pages/ocr_comparator.py CHANGED
@@ -19,917 +19,910 @@ from pytesseract import Output
19
  import os
20
  from mycolorpy import colorlist as mcp
21
 
 
22
  ###################################################################################################
23
- ## FUNCTIONS
24
  ###################################################################################################
 
25
 
26
- @st.cache
27
- def convert_df(in_df):
28
- """Convert data frame function, used by download button
29
-
30
- Args:
31
- in_df (data frame): data frame to convert
32
-
33
- Returns:
34
- data frame: converted data frame
35
- """
36
- # IMPORTANT: Cache the conversion to prevent computation on every rerun
37
- return in_df.to_csv().encode('utf-8')
38
-
39
- ###
40
- def easyocr_coord_convert(in_list_coord):
41
- """Convert easyocr coordinates to standard format used by others functions
42
-
43
- Args:
44
- in_list_coord (list of numbers): format [x_min, x_max, y_min, y_max]
45
-
46
- Returns:
47
- list of lists: format [ [x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max] ]
48
- """
49
-
50
- coord = in_list_coord
51
- return [[coord[0], coord[2]], [coord[1], coord[2]], [coord[1], coord[3]], [coord[0], coord[3]]]
52
-
53
- ###
54
- @st.cache(show_spinner=False)
55
- def initializations():
56
- """Initializations for the app
57
-
58
- Returns:
59
- list of strings : list of OCR solutions names
60
- (['EasyOCR', 'PPOCR', 'MMOCR', 'Tesseract'])
61
- dict : names and indices of the OCR solutions
62
- ({'EasyOCR': 0, 'PPOCR': 1, 'MMOCR': 2, 'Tesseract': 3})
63
- list of dicts : list of languages supported by each OCR solution
64
- list of int : columns for recognition details results
65
- dict : confidence color scale
66
- plotly figure : confidence color scale figure
67
- """
68
- # the readers considered
69
- out_reader_type_list = ['EasyOCR', 'PPOCR', 'MMOCR', 'Tesseract']
70
- out_reader_type_dict = {'EasyOCR': 0, 'PPOCR': 1, 'MMOCR': 2, 'Tesseract': 3}
71
-
72
- # Columns for recognition details results
73
- out_cols_size = [2] + [2,1]*(len(out_reader_type_list)-1) # Except Tesseract
74
-
75
- # Dicts of laguages supported by each reader
76
- out_dict_lang_easyocr = {'Abaza': 'abq', 'Adyghe': 'ady', 'Afrikaans': 'af', 'Angika': 'ang', \
77
- 'Arabic': 'ar', 'Assamese': 'as', 'Avar': 'ava', 'Azerbaijani': 'az', 'Belarusian': 'be', \
78
- 'Bulgarian': 'bg', 'Bihari': 'bh', 'Bhojpuri': 'bho', 'Bengali': 'bn', 'Bosnian': 'bs', \
79
- 'Simplified Chinese': 'ch_sim', 'Traditional Chinese': 'ch_tra', 'Chechen': 'che', \
80
- 'Czech': 'cs', 'Welsh': 'cy', 'Danish': 'da', 'Dargwa': 'dar', 'German': 'de', \
81
- 'English': 'en', 'Spanish': 'es', 'Estonian': 'et', 'Persian (Farsi)': 'fa', 'French': 'fr', \
82
- 'Irish': 'ga', 'Goan Konkani': 'gom', 'Hindi': 'hi', 'Croatian': 'hr', 'Hungarian': 'hu', \
83
- 'Indonesian': 'id', 'Ingush': 'inh', 'Icelandic': 'is', 'Italian': 'it', 'Japanese': 'ja', \
84
- 'Kabardian': 'kbd', 'Kannada': 'kn', 'Korean': 'ko', 'Kurdish': 'ku', 'Latin': 'la', \
85
- 'Lak': 'lbe', 'Lezghian': 'lez', 'Lithuanian': 'lt', 'Latvian': 'lv', 'Magahi': 'mah', \
86
- 'Maithili': 'mai', 'Maori': 'mi', 'Mongolian': 'mn', 'Marathi': 'mr', 'Malay': 'ms', \
87
- 'Maltese': 'mt', 'Nepali': 'ne', 'Newari': 'new', 'Dutch': 'nl', 'Norwegian': 'no', \
88
- 'Occitan': 'oc', 'Pali': 'pi', 'Polish': 'pl', 'Portuguese': 'pt', 'Romanian': 'ro', \
89
- 'Russian': 'ru', 'Serbian (cyrillic)': 'rs_cyrillic', 'Serbian (latin)': 'rs_latin', \
90
- 'Nagpuri': 'sck', 'Slovak': 'sk', 'Slovenian': 'sl', 'Albanian': 'sq', 'Swedish': 'sv', \
91
- 'Swahili': 'sw', 'Tamil': 'ta', 'Tabassaran': 'tab', 'Telugu': 'te', 'Thai': 'th', \
92
- 'Tajik': 'tjk', 'Tagalog': 'tl', 'Turkish': 'tr', 'Uyghur': 'ug', 'Ukranian': 'uk', \
93
- 'Urdu': 'ur', 'Uzbek': 'uz', 'Vietnamese': 'vi'}
94
-
95
- out_dict_lang_ppocr = {'Abaza': 'abq', 'Adyghe': 'ady', 'Afrikaans': 'af', 'Albanian': 'sq', \
96
- 'Angika': 'ang', 'Arabic': 'ar', 'Avar': 'ava', 'Azerbaijani': 'az', 'Belarusian': 'be', \
97
- 'Bhojpuri': 'bho','Bihari': 'bh','Bosnian': 'bs','Bulgarian': 'bg','Chinese & English': 'ch', \
98
- 'Chinese Traditional': 'chinese_cht', 'Croatian': 'hr', 'Czech': 'cs', 'Danish': 'da', \
99
- 'Dargwa': 'dar', 'Dutch': 'nl', 'English': 'en', 'Estonian': 'et', 'French': 'fr', \
100
- 'German': 'german','Goan Konkani': 'gom','Hindi': 'hi','Hungarian': 'hu','Icelandic': 'is', \
101
- 'Indonesian': 'id', 'Ingush': 'inh', 'Irish': 'ga', 'Italian': 'it', 'Japan': 'japan', \
102
- 'Kabardian': 'kbd', 'Korean': 'korean', 'Kurdish': 'ku', 'Lak': 'lbe', 'Latvian': 'lv', \
103
- 'Lezghian': 'lez', 'Lithuanian': 'lt', 'Magahi': 'mah', 'Maithili': 'mai', 'Malay': 'ms', \
104
- 'Maltese': 'mt', 'Maori': 'mi', 'Marathi': 'mr', 'Mongolian': 'mn', 'Nagpur': 'sck', \
105
- 'Nepali': 'ne', 'Newari': 'new', 'Norwegian': 'no', 'Occitan': 'oc', 'Persian': 'fa', \
106
- 'Polish': 'pl', 'Portuguese': 'pt', 'Romanian': 'ro', 'Russia': 'ru', 'Saudi Arabia': 'sa', \
107
- 'Serbian(cyrillic)': 'rs_cyrillic', 'Serbian(latin)': 'rs_latin', 'Slovak': 'sk', \
108
- 'Slovenian': 'sl', 'Spanish': 'es', 'Swahili': 'sw', 'Swedish': 'sv', 'Tabassaran': 'tab', \
109
- 'Tagalog': 'tl', 'Tamil': 'ta', 'Telugu': 'te', 'Turkish': 'tr', 'Ukranian': 'uk', \
110
- 'Urdu': 'ur', 'Uyghur': 'ug', 'Uzbek': 'uz', 'Vietnamese': 'vi', 'Welsh': 'cy'}
111
-
112
- out_dict_lang_mmocr = {'English & Chinese': 'en'}
113
-
114
- out_dict_lang_tesseract = {'Afrikaans': 'afr','Albanian': 'sqi','Amharic': 'amh', \
115
- 'Arabic': 'ara', 'Armenian': 'hye','Assamese': 'asm','Azerbaijani - Cyrilic': 'aze_cyrl', \
116
- 'Azerbaijani': 'aze', 'Basque': 'eus','Belarusian': 'bel','Bengali': 'ben','Bosnian': 'bos', \
117
- 'Breton': 'bre', 'Bulgarian': 'bul','Burmese': 'mya','Catalan; Valencian': 'cat', \
118
- 'Cebuano': 'ceb', 'Central Khmer': 'khm','Cherokee': 'chr','Chinese - Simplified': 'chi_sim', \
119
- 'Chinese - Traditional': 'chi_tra','Corsican': 'cos','Croatian': 'hrv','Czech': 'ces', \
120
- 'Danish':'dan','Dutch; Flemish':'nld','Dzongkha':'dzo','English, Middle (1100-1500)':'enm', \
121
- 'English': 'eng','Esperanto': 'epo','Estonian': 'est','Faroese': 'fao', \
122
- 'Filipino (old - Tagalog)': 'fil','Finnish': 'fin','French, Middle (ca.1400-1600)': 'frm', \
123
- 'French': 'fra','Galician': 'glg','Georgian - Old': 'kat_old','Georgian': 'kat', \
124
- 'German - Fraktur': 'frk','German': 'deu','Greek, Modern (1453-)': 'ell','Gujarati': 'guj', \
125
- 'Haitian; Haitian Creole': 'hat','Hebrew': 'heb','Hindi': 'hin','Hungarian': 'hun', \
126
- 'Icelandic': 'isl','Indonesian': 'ind','Inuktitut': 'iku','Irish': 'gle', \
127
- 'Italian - Old': 'ita_old','Italian': 'ita','Japanese': 'jpn','Javanese': 'jav', \
128
- 'Kannada': 'kan','Kazakh': 'kaz','Kirghiz; Kyrgyz': 'kir','Korean (vertical)': 'kor_vert', \
129
- 'Korean': 'kor','Kurdish (Arabic Script)': 'kur_ara','Lao': 'lao','Latin': 'lat', \
130
- 'Latvian':'lav','Lithuanian':'lit','Luxembourgish':'ltz','Macedonian':'mkd','Malay':'msa', \
131
- 'Malayalam': 'mal','Maltese': 'mlt','Maori': 'mri','Marathi': 'mar','Mongolian': 'mon', \
132
- 'Nepali': 'nep','Norwegian': 'nor','Occitan (post 1500)': 'oci', \
133
- 'Orientation and script detection module':'osd','Oriya':'ori','Panjabi; Punjabi':'pan', \
134
- 'Persian':'fas','Polish':'pol','Portuguese':'por','Pushto; Pashto':'pus','Quechua':'que', \
135
- 'Romanian; Moldavian; Moldovan': 'ron','Russian': 'rus','Sanskrit': 'san', \
136
- 'Scottish Gaelic': 'gla','Serbian - Latin': 'srp_latn','Serbian': 'srp','Sindhi': 'snd', \
137
- 'Sinhala; Sinhalese': 'sin','Slovak': 'slk','Slovenian': 'slv', \
138
- 'Spanish; Castilian - Old': 'spa_old','Spanish; Castilian': 'spa','Sundanese': 'sun', \
139
- 'Swahili': 'swa','Swedish': 'swe','Syriac': 'syr','Tajik': 'tgk','Tamil': 'tam', \
140
- 'Tatar':'tat','Telugu':'tel','Thai':'tha','Tibetan':'bod','Tigrinya':'tir','Tonga':'ton', \
141
- 'Turkish': 'tur','Uighur; Uyghur': 'uig','Ukrainian': 'ukr','Urdu': 'urd', \
142
- 'Uzbek - Cyrilic': 'uzb_cyrl','Uzbek': 'uzb','Vietnamese': 'vie','Welsh': 'cym', \
143
- 'Western Frisian': 'fry','Yiddish': 'yid','Yoruba': 'yor'}
144
-
145
- out_list_dict_lang = [out_dict_lang_easyocr, out_dict_lang_ppocr, out_dict_lang_mmocr, \
146
- out_dict_lang_tesseract]
147
-
148
- # Initialization of detection form
149
- if 'columns_size' not in st.session_state:
150
- st.session_state.columns_size = [2] + [1 for x in out_reader_type_list[1:]]
151
- if 'column_width' not in st.session_state:
152
- st.session_state.column_width = [500] + [400 for x in out_reader_type_list[1:]]
153
- if 'columns_color' not in st.session_state:
154
- st.session_state.columns_color = ["rgb(228,26,28)"] + \
155
- ["rgb(0,0,0)" for x in out_reader_type_list[1:]]
156
- if 'list_coordinates' not in st.session_state:
157
- st.session_state.list_coordinates = []
158
-
159
- # Confidence color scale
160
- out_list_confid = list(np.arange(0,101,1))
161
- out_list_grad = mcp.gen_color_normalized(cmap="Greens",data_arr=np.array(out_list_confid))
162
- out_dict_back_colors = {out_list_confid[i]: out_list_grad[i] \
163
- for i in range(len(out_list_confid))}
164
-
165
- list_y = [1 for i in out_list_confid]
166
- df_confid = pd.DataFrame({'% confidence scale': out_list_confid, 'y': list_y})
167
-
168
- out_fig = px.scatter(df_confid, x='% confidence scale', y='y', \
169
- hover_data={'% confidence scale': True, 'y': False},
170
- color=out_dict_back_colors.values(), range_y=[0.9,1.1], range_x=[0,100],
171
- color_discrete_map="identity",height=50,symbol='y',symbol_sequence=['square'])
172
- out_fig.update_xaxes(showticklabels=False)
173
- out_fig.update_yaxes(showticklabels=False, range=[0.1, 1.1], visible=False)
174
- out_fig.update_traces(marker_size=50)
175
- out_fig.update_layout(paper_bgcolor="white", margin=dict(b=0,r=0,t=0,l=0), xaxis_side="top", \
176
- showlegend=False)
177
-
178
- return out_reader_type_list, out_reader_type_dict, out_list_dict_lang, \
179
- out_cols_size, out_dict_back_colors, out_fig
180
-
181
- ###
182
- @st.experimental_memo(show_spinner=False)
183
- def init_easyocr(in_params):
184
- """Initialization of easyOCR reader
185
-
186
- Args:
187
- in_params (list): list with the language
188
-
189
- Returns:
190
- easyocr reader: the easyocr reader instance
191
- """
192
- out_ocr = easyocr.Reader(in_params)
193
- return out_ocr
194
-
195
- ###
196
- @st.cache(show_spinner=False)
197
- def init_ppocr(in_params):
198
- """Initialization of PPOCR reader
199
-
200
- Args:
201
- in_params (dict): dict with parameters
202
-
203
- Returns:
204
- ppocr reader: the ppocr reader instance
205
- """
206
- out_ocr = PaddleOCR(lang=in_params[0], **in_params[1])
207
- return out_ocr
208
-
209
- ###
210
- @st.experimental_memo(show_spinner=False)
211
- def init_mmocr(in_params):
212
- """Initialization of MMOCR reader
213
-
214
- Args:
215
- in_params (dict): dict with parameters
216
-
217
- Returns:
218
- mmocr reader: the ppocr reader instance
219
- """
220
- out_ocr = MMOCR(recog=None, **in_params[1])
221
- return out_ocr
222
-
223
- ###
224
- def init_readers(in_list_params):
225
- """Initialization of the readers, and return them as list
226
-
227
- Args:
228
- in_list_params (list): list of dicts of parameters for each reader
229
-
230
- Returns:
231
- list: list of the reader's instances
232
- """
233
- # Instantiations of the readers :
234
- # - EasyOCR
235
- with st.spinner("EasyOCR reader initialization in progress ..."):
236
- reader_easyocr = init_easyocr([in_list_params[0][0]])
237
-
238
- # - PPOCR
239
- # Paddleocr
240
- with st.spinner("PPOCR reader initialization in progress ..."):
241
- reader_ppocr = init_ppocr(in_list_params[1])
242
-
243
- # - MMOCR
244
- with st.spinner("MMOCR reader initialization in progress ..."):
245
- reader_mmocr = init_mmocr(in_list_params[2])
246
-
247
- out_list_readers = [reader_easyocr, reader_ppocr, reader_mmocr]
248
-
249
- return out_list_readers
250
-
251
- ###
252
- def load_image(in_image_file):
253
- """Load input file and open it
254
-
255
- Args:
256
- in_image_file (string or Streamlit UploadedFile): image to consider
257
-
258
- Returns:
259
- string : locally saved image path (img.)
260
- PIL.Image : input file opened with Pillow
261
- matrix : input file opened with Opencv
262
- """
263
-
264
- #if isinstance(in_image_file, str):
265
- # out_image_path = "img."+in_image_file.split('.')[-1]
266
- #else:
267
- # out_image_path = "img."+in_image_file.name.split('.')[-1]
268
-
269
- if isinstance(in_image_file, str):
270
- out_image_path = "tmp_"+in_image_file
271
- else:
272
- out_image_path = "tmp_"+in_image_file.name
273
-
274
- img = Image.open(in_image_file)
275
- img_saved = img.save(out_image_path)
276
-
277
- # Read image
278
- out_image_orig = Image.open(out_image_path)
279
- out_image_cv2 = cv2.cvtColor(cv2.imread(out_image_path), cv2.COLOR_BGR2RGB)
280
-
281
- return out_image_path, out_image_orig, out_image_cv2
282
-
283
- ###
284
- @st.experimental_memo(show_spinner=False)
285
- def easyocr_detect(_in_reader, in_image_path, in_params):
286
- """Detection with EasyOCR
287
-
288
- Args:
289
- _in_reader (EasyOCR reader) : the previously initialized instance
290
- in_image_path (string ) : locally saved image path
291
- in_params (list) : list with the parameters for detection
292
-
293
- Returns:
294
- list : list of the boxes coordinates
295
- exception on error, string 'OK' otherwise
296
- """
297
- try:
298
- dict_param = in_params[1]
299
- detection_result = _in_reader.detect(in_image_path,
300
- #width_ths=0.7,
301
- #mag_ratio=1.5
302
- **dict_param
303
- )
304
- easyocr_coordinates = detection_result[0][0]
305
-
306
- # The format of the coordinate is as follows: [x_min, x_max, y_min, y_max]
307
- # Format boxes coordinates for draw
308
- out_easyocr_boxes_coordinates = list(map(easyocr_coord_convert, easyocr_coordinates))
309
- out_status = 'OK'
310
- except Exception as e:
311
- out_easyocr_boxes_coordinates = []
312
- out_status = e
313
-
314
- return out_easyocr_boxes_coordinates, out_status
315
-
316
- ###
317
- @st.experimental_memo(show_spinner=False)
318
- def ppocr_detect(_in_reader, in_image_path):
319
- """Detection with PPOCR
320
-
321
- Args:
322
- _in_reader (PPOCR reader) : the previously initialized instance
323
- in_image_path (string ) : locally saved image path
324
-
325
- Returns:
326
- list : list of the boxes coordinates
327
- exception on error, string 'OK' otherwise
328
- """
329
- # PPOCR detection method
330
- try:
331
- out_ppocr_boxes_coordinates = _in_reader.ocr(in_image_path, rec=False)
332
- out_status = 'OK'
333
- except Exception as e:
334
- out_ppocr_boxes_coordinates = []
335
- out_status = e
336
-
337
- return out_ppocr_boxes_coordinates, out_status
338
-
339
- ###
340
- @st.experimental_memo(show_spinner=False)
341
- def mmocr_detect(_in_reader, in_image_path):
342
- """Detection with MMOCR
343
-
344
- Args:
345
- _in_reader (EasyORC reader) : the previously initialized instance
346
- in_image_path (string) : locally saved image path
347
- in_params (list) : list with the parameters
348
-
349
- Returns:
350
- list : list of the boxes coordinates
351
- exception on error, string 'OK' otherwise
352
- """
353
- # MMOCR detection method
354
- out_mmocr_boxes_coordinates = []
355
- try:
356
- det_result = _in_reader.readtext(in_image_path, details=True)
357
- bboxes_list = [res['boundary_result'] for res in det_result]
358
- for bboxes in bboxes_list:
359
- for bbox in bboxes:
360
- if len(bbox) > 9:
361
- min_x = min(bbox[0:-1:2])
362
- min_y = min(bbox[1:-1:2])
363
- max_x = max(bbox[0:-1:2])
364
- max_y = max(bbox[1:-1:2])
365
- #box = [min_x, min_y, max_x, min_y, max_x, max_y, min_x, max_y]
366
- else:
367
- min_x = min(bbox[0:-1:2])
368
- min_y = min(bbox[1::2])
369
- max_x = max(bbox[0:-1:2])
370
- max_y = max(bbox[1::2])
371
- box4 = [ [min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y] ]
372
- out_mmocr_boxes_coordinates.append(box4)
373
- out_status = 'OK'
374
- except Exception as e:
375
- out_status = e
376
-
377
- return out_mmocr_boxes_coordinates, out_status
378
-
379
- ###
380
- def cropped_1box(in_box, in_img):
381
- """Construction of an cropped image corresponding to an area of the initial image
382
-
383
- Args:
384
- in_box (list) : box with coordinates
385
- in_img (matrix) : image
386
-
387
- Returns:
388
- matrix : cropped image
389
- """
390
- box_ar = np.array(in_box).astype(np.int64)
391
- x_min = box_ar[:, 0].min()
392
- x_max = box_ar[:, 0].max()
393
- y_min = box_ar[:, 1].min()
394
- y_max = box_ar[:, 1].max()
395
- out_cropped = in_img[y_min:y_max, x_min:x_max]
396
-
397
- return out_cropped
398
-
399
- ###
400
- @st.experimental_memo(show_spinner=False)
401
- def tesserocr_detect(in_image_path, _in_img, in_params):
402
- """Detection with Tesseract
403
-
404
- Args:
405
- in_image_path (string) : locally saved image path
406
- _in_img (PIL.Image) : image to consider
407
- in_params (list) : list with the parameters for detection
408
-
409
- Returns:
410
- list : list of the boxes coordinates
411
- exception on error, string 'OK' otherwise
412
- """
413
- try:
414
- dict_param = in_params[1]
415
- df_res = pytesseract.image_to_data(_in_img, **dict_param, output_type=Output.DATAFRAME)
416
-
417
- df_res['box'] = df_res.apply(lambda d: [[d['left'], d['top']], \
418
- [d['left'] + d['width'], d['top']], \
419
- [d['left'] + d['width'], d['top'] + d['height']], \
420
- [d['left'], d['top'] + d['height']], \
421
- ], axis=1)
422
- out_tesserocr_boxes_coordinates = df_res[df_res.word_num > 0]['box'].to_list()
423
- out_status = 'OK'
424
- except Exception as e:
425
- out_tesserocr_boxes_coordinates = []
426
- out_status = e
427
-
428
- return out_tesserocr_boxes_coordinates, out_status
429
-
430
- ###
431
- @st.experimental_memo(show_spinner=False)
432
- def process_detect(in_image_path, _in_list_images, _in_list_readers, in_list_params, in_color):
433
- """Detection process for each OCR solution
434
-
435
- Args:
436
- in_image_path (string) : locally saved image path
437
- _in_list_images (list) : list of original image
438
- _in_list_readers (list) : list with previously initialized reader's instances
439
- in_list_params (list) : list with dict parameters for each OCR solution
440
- in_color (tuple) : color for boxes around text
441
-
442
- Returns:
443
- list: list of detection results images
444
- list: list of boxes coordinates
445
- """
446
- ## ------- EasyOCR Text detection
447
- with st.spinner('EasyOCR Text detection in progress ...'):
448
- easyocr_boxes_coordinates,easyocr_status = easyocr_detect(_in_list_readers[0], \
449
- in_image_path, in_list_params[0])
450
- # Visualization
451
- if easyocr_boxes_coordinates:
452
- easyocr_image_detect = draw_detected(_in_list_images[0], easyocr_boxes_coordinates, \
453
- in_color, 'None', 3)
454
- else:
455
- easyocr_boxes_coordinates = easyocr_status
456
- ##
457
-
458
- ## ------- PPOCR Text detection
459
- with st.spinner('PPOCR Text detection in progress ...'):
460
- ppocr_boxes_coordinates, ppocr_status = ppocr_detect(_in_list_readers[1], in_image_path)
461
- # Visualization
462
- if ppocr_boxes_coordinates:
463
- ppocr_image_detect = draw_detected(_in_list_images[0], ppocr_boxes_coordinates, \
464
- in_color, 'None', 3)
465
- else:
466
- ppocr_image_detect = ppocr_status
467
- ##
468
-
469
- ## ------- MMOCR Text detection
470
- with st.spinner('MMOCR Text detection in progress ...'):
471
- mmocr_boxes_coordinates, mmocr_status = mmocr_detect(_in_list_readers[2], in_image_path)
472
- # Visualization
473
- if mmocr_boxes_coordinates:
474
- mmocr_image_detect = draw_detected(_in_list_images[0], mmocr_boxes_coordinates, \
475
- in_color, 'None', 3)
476
- else:
477
- mmocr_image_detect = mmocr_status
478
- ##
479
-
480
- ## ------- Tesseract Text detection
481
- with st.spinner('Tesseract Text detection in progress ...'):
482
- tesserocr_boxes_coordinates, tesserocr_status = tesserocr_detect(in_image_path, \
483
- _in_list_images[0], \
484
- in_list_params[3])
485
- # Visualization
486
- if tesserocr_status == 'OK':
487
- tesserocr_image_detect = draw_detected(_in_list_images[0],tesserocr_boxes_coordinates,\
488
- in_color, 'None', 3)
489
  else:
490
- tesserocr_image_detect = tesserocr_status
491
- ##
492
- #
493
- out_list_images = _in_list_images + [easyocr_image_detect, ppocr_image_detect, \
494
- mmocr_image_detect, tesserocr_image_detect]
495
- out_list_coordinates = [easyocr_boxes_coordinates, ppocr_boxes_coordinates, \
496
- mmocr_boxes_coordinates, tesserocr_boxes_coordinates]
497
- #
498
-
499
- return out_list_images, out_list_coordinates
500
-
501
- ###
502
- def draw_detected(in_image, in_boxes_coordinates, in_color, posit='None', in_thickness=4):
503
- """Draw boxes around detected text
504
-
505
- Args:
506
- in_image (PIL.Image) : original image
507
- in_boxes_coordinates (list) : boxes coordinates, from top to bottom and from left to right
508
- [ [ [x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max] ],
509
- [ ... ]
510
- ]
511
- in_color (tuple) : color for boxes around text
512
- posit (str, optional) : position for text. Defaults to 'None'.
513
- in_thickness (int, optional): thickness of the box. Defaults to 4.
514
-
515
- Returns:
516
- PIL.Image : original image with detected areas
517
- """
518
- work_img = in_image.copy()
519
- if in_boxes_coordinates:
520
- font = cv2.FONT_HERSHEY_SIMPLEX
521
- for ind_box, box in enumerate(in_boxes_coordinates):
522
- box = np.reshape(np.array(box), [-1, 1, 2]).astype(np.int64)
523
- work_img = cv2.polylines(np.array(work_img), [box], True, in_color, in_thickness)
524
- if posit != 'None':
525
- if posit == 'top_left':
526
- pos = tuple(box[0][0])
527
- elif posit == 'top_right':
528
- pos = tuple(box[1][0])
529
- work_img = cv2.putText(work_img, str(ind_box+1), pos, font, 5.5, color, \
530
- in_thickness,cv2.LINE_AA)
531
-
532
- out_image_drawn = Image.fromarray(work_img)
533
- else:
534
- out_image_drawn = work_img
535
-
536
- return out_image_drawn
537
-
538
- ###
539
- @st.experimental_memo(show_spinner=False)
540
- def get_cropped(in_boxes_coordinates, in_image_cv):
541
- """Construct list of cropped images corresponding of the input boxes coordinates list
542
-
543
- Args:
544
- in_boxes_coordinates (list) : list of boxes coordinates
545
- in_image_cv (matrix) : original image
546
-
547
- Returns:
548
- list : list with cropped images
549
- """
550
- out_list_images = []
551
- for box in in_boxes_coordinates:
552
- cropped = cropped_1box(box, in_image_cv)
553
- out_list_images.append(cropped)
554
- return out_list_images
555
-
556
- ###
557
- def process_recog(in_list_readers, in_image_cv, in_boxes_coordinates, in_list_dict_params):
558
- """Recognition process for each OCR solution
559
-
560
- Args:
561
- in_list_readers (list) : list with previously initialized reader's instances
562
- in_image_cv (matrix) : original image
563
- in_boxes_coordinates (list) : list of boxes coordinates
564
- in_list_dict_params (list) : list with dict parameters for each OCR solution
565
-
566
- Returns:
567
- data frame : results for each OCR solution, except Tesseract
568
- data frame : results for Tesseract
569
- list : status for each recognition (exception or 'OK')
570
- """
571
- out_df_results = pd.DataFrame([])
572
-
573
- list_text_easyocr = []
574
- list_confidence_easyocr = []
575
- list_text_ppocr = []
576
- list_confidence_ppocr = []
577
- list_text_mmocr = []
578
- list_confidence_mmocr = []
579
-
580
- # Create cropped images from detection
581
- list_cropped_images = get_cropped(in_boxes_coordinates, in_image_cv)
582
-
583
- # Recognize with EasyOCR
584
- with st.spinner('EasyOCR Text recognition in progress ...'):
585
- list_text_easyocr, list_confidence_easyocr, status_easyocr = \
586
- easyocr_recog(list_cropped_images, in_list_readers[0], in_list_dict_params[0])
587
- ##
588
-
589
- # Recognize with PPOCR
590
- with st.spinner('PPOCR Text recognition in progress ...'):
591
- list_text_ppocr, list_confidence_ppocr, status_ppocr = \
592
- ppocr_recog(list_cropped_images, in_list_dict_params[1])
593
- ##
594
-
595
- # Recognize with MMOCR
596
- with st.spinner('MMOCR Text recognition in progress ...'):
597
- list_text_mmocr, list_confidence_mmocr, status_mmocr = \
598
- mmocr_recog(list_cropped_images, in_list_dict_params[2])
599
- ##
600
-
601
- # Recognize with Tesseract
602
- with st.spinner('Tesseract Text recognition in progress ...'):
603
- out_df_results_tesseract, status_tesseract = \
604
- tesserocr_recog(in_image_cv, in_list_dict_params[3], len(list_cropped_images))
605
- ##
606
-
607
- # Create results data frame
608
- out_df_results = pd.DataFrame({'cropped_image': list_cropped_images,
609
- 'text_easyocr': list_text_easyocr,
610
- 'confidence_easyocr': list_confidence_easyocr,
611
- 'text_ppocr': list_text_ppocr,
612
- 'confidence_ppocr': list_confidence_ppocr,
613
- 'text_mmocr': list_text_mmocr,
614
- 'confidence_mmocr': list_confidence_mmocr
615
- }
616
- )
617
-
618
- out_list_reco_status = [status_easyocr, status_ppocr, status_mmocr, status_tesseract]
619
-
620
- return out_df_results, out_df_results_tesseract, out_list_reco_status
621
-
622
- ###
623
- @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
624
- def easyocr_recog(in_list_images, _in_reader_easyocr, in_params):
625
- """Recognition with EasyOCR
626
-
627
- Args:
628
- in_list_images (list) : list of cropped images
629
- _in_reader_easyocr (EasyOCR reader) : the previously initialized instance
630
- in_params (dict) : parameters for recognition
631
-
632
- Returns:
633
- list : list of recognized text
634
- list : list of recognition confidence
635
- string/Exception : recognition status
636
- """
637
- progress_bar = st.progress(0)
638
- out_list_text_easyocr = []
639
- out_list_confidence_easyocr = []
640
- ## ------- EasyOCR Text recognition
641
- try:
642
- step = 0*len(in_list_images) # first recognition process
643
- nb_steps = 4 * len(in_list_images)
644
- for ind_img, cropped in enumerate(in_list_images):
645
- result = _in_reader_easyocr.recognize(cropped, **in_params)
646
- try:
647
- out_list_text_easyocr.append(result[0][1])
648
- out_list_confidence_easyocr.append(np.round(100*result[0][2], 1))
649
- except:
650
- out_list_text_easyocr.append('Not recognize')
651
- out_list_confidence_easyocr.append(100.)
652
- progress_bar.progress((step+ind_img+1)/nb_steps)
653
- out_status = 'OK'
654
- except Exception as e:
655
- out_status = e
656
- progress_bar.empty()
657
-
658
- return out_list_text_easyocr, out_list_confidence_easyocr, out_status
659
-
660
- ###
661
- @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
662
- def ppocr_recog(in_list_images, in_params):
663
- """Recognition with PPOCR
664
-
665
- Args:
666
- in_list_images (list) : list of cropped images
667
- in_params (dict) : parameters for recognition
668
-
669
- Returns:
670
- list : list of recognized text
671
- list : list of recognition confidence
672
- string/Exception : recognition status
673
- """
674
- ## ------- PPOCR Text recognition
675
- out_list_text_ppocr = []
676
- out_list_confidence_ppocr = []
677
- try:
678
- reader_ppocr = PaddleOCR(**in_params)
679
- step = 1*len(in_list_images) # second recognition process
680
- nb_steps = 4 * len(in_list_images)
681
- progress_bar = st.progress(step/nb_steps)
682
-
683
- for ind_img, cropped in enumerate(in_list_images):
684
- result = reader_ppocr.ocr(cropped, det=False, cls=False)
685
- try:
686
- out_list_text_ppocr.append(result[0][0])
687
- out_list_confidence_ppocr.append(np.round(100*result[0][1], 1))
688
- except:
689
- out_list_text_ppocr.append('Not recognize')
690
- out_list_confidence_ppocr.append(100.)
691
- progress_bar.progress((step+ind_img+1)/nb_steps)
692
- out_status = 'OK'
693
- except Exception as e:
694
- out_status = e
695
- progress_bar.empty()
696
-
697
- return out_list_text_ppocr, out_list_confidence_ppocr, out_status
698
-
699
- ###
700
- @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
701
- def mmocr_recog(in_list_images, in_params):
702
- """Recognition with MMOCR
703
-
704
- Args:
705
- in_list_images (list) : list of cropped images
706
- in_params (dict) : parameters for recognition
707
-
708
- Returns:
709
- list : list of recognized text
710
- list : list of recognition confidence
711
- string/Exception : recognition status
712
- """
713
- ## ------- MMOCR Text recognition
714
- out_list_text_mmocr = []
715
- out_list_confidence_mmocr = []
716
- try:
717
- reader_mmocr = MMOCR(det=None, **in_params)
718
- step = 2*len(in_list_images) # third recognition process
719
- nb_steps = 4 * len(in_list_images)
720
- progress_bar = st.progress(step/nb_steps)
721
-
722
- for ind_img, cropped in enumerate(in_list_images):
723
- result = reader_mmocr.readtext(cropped, details=True)
724
- try:
725
- out_list_text_mmocr.append(result[0]['text'])
726
- out_list_confidence_mmocr.append(np.round(100* \
727
- (np.array(result[0]['score']).mean()), 1))
728
- except:
729
- out_list_text_mmocr.append('Not recognize')
730
- out_list_confidence_mmocr.append(100.)
731
- progress_bar.progress((step+ind_img+1)/nb_steps)
732
- out_status = 'OK'
733
- except Exception as e:
734
- out_status = e
735
- progress_bar.empty()
736
-
737
- return out_list_text_mmocr, out_list_confidence_mmocr, out_status
738
-
739
- ###
740
- @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
741
- def tesserocr_recog(in_img, in_params, in_nb_images):
742
- """Recognition with Tesseract
743
-
744
- Args:
745
- in_image_cv (matrix) : original image
746
- in_params (dict) : parameters for recognition
747
- in_nb_images : nb cropped images (used for progress bar)
748
-
749
- Returns:
750
- Pandas data frame : recognition results
751
- string/Exception : recognition status
752
- """
753
- ## ------- Tesseract Text recognition
754
- step = 3*in_nb_images # fourth recognition process
755
- nb_steps = 4 * in_nb_images
756
- progress_bar = st.progress(step/nb_steps)
757
-
758
- try:
759
- out_df_result = pytesseract.image_to_data(in_img, **in_params,output_type=Output.DATAFRAME)
760
-
761
- out_df_result['box'] = out_df_result.apply(lambda d: [[d['left'], d['top']], \
762
  [d['left'] + d['width'], d['top']], \
763
- [d['left']+d['width'], d['top']+d['height']], \
764
  [d['left'], d['top'] + d['height']], \
765
- ], axis=1)
766
- out_df_result['cropped'] = out_df_result['box'].apply(lambda b: cropped_1box(b, in_img))
767
- out_df_result = out_df_result[(out_df_result.word_num > 0) & (out_df_result.text != ' ')] \
768
- .reset_index(drop=True)
769
- out_status = 'OK'
770
- except Exception as e:
771
- out_df_result = pd.DataFrame([])
772
- out_status = e
773
-
774
- progress_bar.progress(1.)
775
-
776
- return out_df_result, out_status
777
-
778
- ###
779
- def draw_reco_images(in_image, in_boxes_coordinates, in_list_texts, in_list_confid, \
780
- in_dict_back_colors, in_df_results_tesseract, in_reader_type_list, \
781
- in_font_scale=1, in_conf_threshold=65):
782
- """Draw recognized text on original image, for each OCR solution used
783
-
784
- Args:
785
- in_image (matrix) : original image
786
- in_boxes_coordinates (list) : list of boxes coordinates
787
- in_list_texts (list): list of recognized text for each recognizer (except Tesseract)
788
- in_list_confid (list): list of recognition confidence for each recognizer (except Tesseract)
789
- in_df_results_tesseract (Pandas data frame): Tesseract recognition results
790
- in_font_scale (int, optional): text font scale. Defaults to 3.
791
-
792
- Returns:
793
- shows the results container
794
- """
795
- img = in_image.copy()
796
- nb_readers = len(in_reader_type_list)
797
- list_reco_images = [img.copy() for i in range(nb_readers)]
798
-
799
- for num, box_ in enumerate(in_boxes_coordinates):
800
- box = np.array(box_).astype(np.int64)
801
-
802
- # For each box : draw the results of each recognizer
803
- for ind_r in range(nb_readers-1):
804
- confid = np.round(in_list_confid[ind_r][num], 0)
805
- rgb_color = ImageColor.getcolor(in_dict_back_colors[confid], "RGB")
806
- if confid < in_conf_threshold:
807
- text_color = (0, 0, 0)
808
  else:
809
- text_color = (255, 255, 255)
810
-
811
- list_reco_images[ind_r] = cv2.rectangle(list_reco_images[ind_r], \
812
- (box[0][0], box[0][1]), \
813
- (box[2][0], box[2][1]), rgb_color, -1)
814
- list_reco_images[ind_r] = cv2.putText(list_reco_images[ind_r], \
815
- in_list_texts[ind_r][num], \
816
- (box[0][0],int(np.round((box[0][1]+box[2][1])/2,0))), \
817
- cv2.FONT_HERSHEY_DUPLEX, in_font_scale, text_color, 2)
818
-
819
- # Add Tesseract process
820
- if not in_df_results_tesseract.empty:
821
- ind_tessocr = nb_readers-1
822
- for num, box_ in enumerate(in_df_results_tesseract['box'].to_list()):
823
- box = np.array(box_).astype(np.int64)
824
- confid = np.round(in_df_results_tesseract.iloc[num]['conf'], 0)
825
- rgb_color = ImageColor.getcolor(in_dict_back_colors[confid], "RGB")
826
- if confid < in_conf_threshold:
827
- text_color = (0, 0, 0)
 
 
828
  else:
829
- text_color = (255, 255, 255)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
830
 
831
- list_reco_images[ind_tessocr] = \
832
- cv2.rectangle(list_reco_images[ind_tessocr], (box[0][0], box[0][1]), \
833
- (box[2][0], box[2][1]), rgb_color, -1)
834
- try:
835
  list_reco_images[ind_tessocr] = \
836
- cv2.putText(list_reco_images[ind_tessocr], \
837
- in_df_results_tesseract.iloc[num]['text'], \
838
- (box[0][0],int(np.round((box[0][1]+box[2][1])/2,0))), \
839
- cv2.FONT_HERSHEY_DUPLEX, in_font_scale, text_color, 2)
840
-
841
- except:
842
-
843
- pass
844
-
845
- with show_reco.container():
846
- # Draw the results, 2 images per line
847
- reco_lines = math.ceil(len(in_reader_type_list) / 2)
848
- column_width = 500
849
- for ind_lig in range(0, reco_lines+1, 2):
850
- cols = st.columns(2)
851
- for ind_col in range(2):
852
- ind = ind_lig + ind_col
853
- if ind <= len(in_reader_type_list):
854
- if in_reader_type_list[ind] == 'Tesseract':
855
- column_title = '<p style="font-size: 20px;color:rgb(0,0,0); \
856
- ">Recognition with ' + in_reader_type_list[ind] + \
857
- '<sp style="font-size: 17px"> (with its own detector) \
858
- </sp></p>'
859
- else:
860
- column_title = '<p style="font-size: 20px;color:rgb(0,0,0); \
861
- ">Recognition with ' + \
862
- in_reader_type_list[ind]+ '</p>'
863
- cols[ind_col].markdown(column_title, unsafe_allow_html=True)
864
- if st.session_state.list_reco_status[ind] == 'OK':
865
- cols[ind_col].image(list_reco_images[ind], \
866
- width=column_width, use_column_width=True)
867
- else:
868
- cols[ind_col].write(list_reco_status[ind], \
869
- use_column_width=True)
870
-
871
- st.markdown(' 💡 Bad font size? you can adjust it below and refresh:')
872
-
873
- ###
874
- def highlight():
875
- """Draw recognized text on original image, for each OCR solution used
876
-
877
- Args:
878
- in_image (matrix) : original image
879
- in_boxes_coordinates (list) : list of boxes coordinates
880
- in_list_texts (list): list of recognized text for each recognizer (except Tesseract)
881
- in_list_confid (list): list of recognition confidence for each recognizer (except Tesseract)
882
- in_df_results_tesseract (Pandas data frame): Tesseract recognition results
883
- in_font_scale (int, optional): text font scale. Defaults to 3.
884
-
885
- Returns:
886
- shows the results container
887
- """
888
- with show_detect.container():
889
- columns_size = [1 for x in reader_type_list]
890
- column_width = [400 for x in reader_type_list]
891
- columns_color = ["rgb(0,0,0)" for x in reader_type_list]
892
- columns_size[reader_type_dict[st.session_state.detect_reader]] = 2
893
- column_width[reader_type_dict[st.session_state.detect_reader]] = 500
894
- columns_color[reader_type_dict[st.session_state.detect_reader]] = "rgb(228,26,28)"
895
- columns = st.columns(columns_size, ) #gap='medium')
896
-
897
- for ind_col, col in enumerate(columns):
898
- column_title = '<p style="font-size: 20px;color:'+columns_color[ind_col] + \
899
- ';">Detection with ' + reader_type_list[ind_col]+ '</p>'
900
- col.markdown(column_title, unsafe_allow_html=True)
901
- if isinstance(list_images[ind_col+2], PIL.Image.Image):
902
- col.image(list_images[ind_col+2], width=column_width[ind_col], \
903
- use_column_width=True)
904
- else:
905
- col.write(list_images[ind_col+2], use_column_width=True)
906
- st.session_state.columns_size = columns_size
907
- st.session_state.column_width = column_width
908
- st.session_state.columns_color = columns_color
909
 
910
- ###
911
- @st.cache(show_spinner=False)
912
- def get_demo():
913
- """Get the demo files
914
 
915
- Returns:
916
- PIL.Image : input file opened with Pillow
917
- PIL.Image : input file opened with Pillow
918
- """
919
 
920
- out_img_demo_1 = Image.open("img_demo_1.jpg")
921
- out_img_demo_2 = Image.open("img_demo_2.jpg")
 
 
 
 
 
 
922
 
923
- return out_img_demo_1, out_img_demo_2
924
 
925
- ###
926
- def raz():
927
- st.session_state.list_coordinates = []
928
 
929
- ###################################################################################################
930
- ## MAIN
931
- ###################################################################################################
932
- def app():
933
  ##----------- Initializations ---------------------------------------------------------------------
934
  #print("PID : ", os.getpid())
935
 
@@ -987,7 +980,7 @@ def app():
987
  with st.form("form1"):
988
  col1, col2 = st.columns(2, ) #gap="medium")
989
  col1.markdown("##### Original image")
990
- col1.image(list_images[0], width=500, use_column_width=True)
991
  col2.markdown("##### Hyperparameters values for detection")
992
 
993
  with col2.expander("Choose detection hyperparameters for " + reader_type_list[0], \
 
19
  import os
20
  from mycolorpy import colorlist as mcp
21
 
22
+
23
  ###################################################################################################
24
+ ## MAIN
25
  ###################################################################################################
26
+ def app():
27
 
28
+ ###################################################################################################
29
+ ## FUNCTIONS
30
+ ###################################################################################################
31
+
32
+ @st.cache
33
+ def convert_df(in_df):
34
+ """Convert data frame function, used by download button
35
+
36
+ Args:
37
+ in_df (data frame): data frame to convert
38
+
39
+ Returns:
40
+ data frame: converted data frame
41
+ """
42
+ # IMPORTANT: Cache the conversion to prevent computation on every rerun
43
+ return in_df.to_csv().encode('utf-8')
44
+
45
+ ###
46
+ def easyocr_coord_convert(in_list_coord):
47
+ """Convert easyocr coordinates to standard format used by others functions
48
+
49
+ Args:
50
+ in_list_coord (list of numbers): format [x_min, x_max, y_min, y_max]
51
+
52
+ Returns:
53
+ list of lists: format [ [x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max] ]
54
+ """
55
+
56
+ coord = in_list_coord
57
+ return [[coord[0], coord[2]], [coord[1], coord[2]], [coord[1], coord[3]], [coord[0], coord[3]]]
58
+
59
+ ###
60
+ @st.cache(show_spinner=False)
61
+ def initializations():
62
+ """Initializations for the app
63
+
64
+ Returns:
65
+ list of strings : list of OCR solutions names
66
+ (['EasyOCR', 'PPOCR', 'MMOCR', 'Tesseract'])
67
+ dict : names and indices of the OCR solutions
68
+ ({'EasyOCR': 0, 'PPOCR': 1, 'MMOCR': 2, 'Tesseract': 3})
69
+ list of dicts : list of languages supported by each OCR solution
70
+ list of int : columns for recognition details results
71
+ dict : confidence color scale
72
+ plotly figure : confidence color scale figure
73
+ """
74
+ # the readers considered
75
+ out_reader_type_list = ['EasyOCR', 'PPOCR', 'MMOCR', 'Tesseract']
76
+ out_reader_type_dict = {'EasyOCR': 0, 'PPOCR': 1, 'MMOCR': 2, 'Tesseract': 3}
77
+
78
+ # Columns for recognition details results
79
+ out_cols_size = [2] + [2,1]*(len(out_reader_type_list)-1) # Except Tesseract
80
+
81
+ # Dicts of laguages supported by each reader
82
+ out_dict_lang_easyocr = {'Abaza': 'abq', 'Adyghe': 'ady', 'Afrikaans': 'af', 'Angika': 'ang', \
83
+ 'Arabic': 'ar', 'Assamese': 'as', 'Avar': 'ava', 'Azerbaijani': 'az', 'Belarusian': 'be', \
84
+ 'Bulgarian': 'bg', 'Bihari': 'bh', 'Bhojpuri': 'bho', 'Bengali': 'bn', 'Bosnian': 'bs', \
85
+ 'Simplified Chinese': 'ch_sim', 'Traditional Chinese': 'ch_tra', 'Chechen': 'che', \
86
+ 'Czech': 'cs', 'Welsh': 'cy', 'Danish': 'da', 'Dargwa': 'dar', 'German': 'de', \
87
+ 'English': 'en', 'Spanish': 'es', 'Estonian': 'et', 'Persian (Farsi)': 'fa', 'French': 'fr', \
88
+ 'Irish': 'ga', 'Goan Konkani': 'gom', 'Hindi': 'hi', 'Croatian': 'hr', 'Hungarian': 'hu', \
89
+ 'Indonesian': 'id', 'Ingush': 'inh', 'Icelandic': 'is', 'Italian': 'it', 'Japanese': 'ja', \
90
+ 'Kabardian': 'kbd', 'Kannada': 'kn', 'Korean': 'ko', 'Kurdish': 'ku', 'Latin': 'la', \
91
+ 'Lak': 'lbe', 'Lezghian': 'lez', 'Lithuanian': 'lt', 'Latvian': 'lv', 'Magahi': 'mah', \
92
+ 'Maithili': 'mai', 'Maori': 'mi', 'Mongolian': 'mn', 'Marathi': 'mr', 'Malay': 'ms', \
93
+ 'Maltese': 'mt', 'Nepali': 'ne', 'Newari': 'new', 'Dutch': 'nl', 'Norwegian': 'no', \
94
+ 'Occitan': 'oc', 'Pali': 'pi', 'Polish': 'pl', 'Portuguese': 'pt', 'Romanian': 'ro', \
95
+ 'Russian': 'ru', 'Serbian (cyrillic)': 'rs_cyrillic', 'Serbian (latin)': 'rs_latin', \
96
+ 'Nagpuri': 'sck', 'Slovak': 'sk', 'Slovenian': 'sl', 'Albanian': 'sq', 'Swedish': 'sv', \
97
+ 'Swahili': 'sw', 'Tamil': 'ta', 'Tabassaran': 'tab', 'Telugu': 'te', 'Thai': 'th', \
98
+ 'Tajik': 'tjk', 'Tagalog': 'tl', 'Turkish': 'tr', 'Uyghur': 'ug', 'Ukranian': 'uk', \
99
+ 'Urdu': 'ur', 'Uzbek': 'uz', 'Vietnamese': 'vi'}
100
+
101
+ out_dict_lang_ppocr = {'Abaza': 'abq', 'Adyghe': 'ady', 'Afrikaans': 'af', 'Albanian': 'sq', \
102
+ 'Angika': 'ang', 'Arabic': 'ar', 'Avar': 'ava', 'Azerbaijani': 'az', 'Belarusian': 'be', \
103
+ 'Bhojpuri': 'bho','Bihari': 'bh','Bosnian': 'bs','Bulgarian': 'bg','Chinese & English': 'ch', \
104
+ 'Chinese Traditional': 'chinese_cht', 'Croatian': 'hr', 'Czech': 'cs', 'Danish': 'da', \
105
+ 'Dargwa': 'dar', 'Dutch': 'nl', 'English': 'en', 'Estonian': 'et', 'French': 'fr', \
106
+ 'German': 'german','Goan Konkani': 'gom','Hindi': 'hi','Hungarian': 'hu','Icelandic': 'is', \
107
+ 'Indonesian': 'id', 'Ingush': 'inh', 'Irish': 'ga', 'Italian': 'it', 'Japan': 'japan', \
108
+ 'Kabardian': 'kbd', 'Korean': 'korean', 'Kurdish': 'ku', 'Lak': 'lbe', 'Latvian': 'lv', \
109
+ 'Lezghian': 'lez', 'Lithuanian': 'lt', 'Magahi': 'mah', 'Maithili': 'mai', 'Malay': 'ms', \
110
+ 'Maltese': 'mt', 'Maori': 'mi', 'Marathi': 'mr', 'Mongolian': 'mn', 'Nagpur': 'sck', \
111
+ 'Nepali': 'ne', 'Newari': 'new', 'Norwegian': 'no', 'Occitan': 'oc', 'Persian': 'fa', \
112
+ 'Polish': 'pl', 'Portuguese': 'pt', 'Romanian': 'ro', 'Russia': 'ru', 'Saudi Arabia': 'sa', \
113
+ 'Serbian(cyrillic)': 'rs_cyrillic', 'Serbian(latin)': 'rs_latin', 'Slovak': 'sk', \
114
+ 'Slovenian': 'sl', 'Spanish': 'es', 'Swahili': 'sw', 'Swedish': 'sv', 'Tabassaran': 'tab', \
115
+ 'Tagalog': 'tl', 'Tamil': 'ta', 'Telugu': 'te', 'Turkish': 'tr', 'Ukranian': 'uk', \
116
+ 'Urdu': 'ur', 'Uyghur': 'ug', 'Uzbek': 'uz', 'Vietnamese': 'vi', 'Welsh': 'cy'}
117
+
118
+ out_dict_lang_mmocr = {'English & Chinese': 'en'}
119
+
120
+ out_dict_lang_tesseract = {'Afrikaans': 'afr','Albanian': 'sqi','Amharic': 'amh', \
121
+ 'Arabic': 'ara', 'Armenian': 'hye','Assamese': 'asm','Azerbaijani - Cyrilic': 'aze_cyrl', \
122
+ 'Azerbaijani': 'aze', 'Basque': 'eus','Belarusian': 'bel','Bengali': 'ben','Bosnian': 'bos', \
123
+ 'Breton': 'bre', 'Bulgarian': 'bul','Burmese': 'mya','Catalan; Valencian': 'cat', \
124
+ 'Cebuano': 'ceb', 'Central Khmer': 'khm','Cherokee': 'chr','Chinese - Simplified': 'chi_sim', \
125
+ 'Chinese - Traditional': 'chi_tra','Corsican': 'cos','Croatian': 'hrv','Czech': 'ces', \
126
+ 'Danish':'dan','Dutch; Flemish':'nld','Dzongkha':'dzo','English, Middle (1100-1500)':'enm', \
127
+ 'English': 'eng','Esperanto': 'epo','Estonian': 'est','Faroese': 'fao', \
128
+ 'Filipino (old - Tagalog)': 'fil','Finnish': 'fin','French, Middle (ca.1400-1600)': 'frm', \
129
+ 'French': 'fra','Galician': 'glg','Georgian - Old': 'kat_old','Georgian': 'kat', \
130
+ 'German - Fraktur': 'frk','German': 'deu','Greek, Modern (1453-)': 'ell','Gujarati': 'guj', \
131
+ 'Haitian; Haitian Creole': 'hat','Hebrew': 'heb','Hindi': 'hin','Hungarian': 'hun', \
132
+ 'Icelandic': 'isl','Indonesian': 'ind','Inuktitut': 'iku','Irish': 'gle', \
133
+ 'Italian - Old': 'ita_old','Italian': 'ita','Japanese': 'jpn','Javanese': 'jav', \
134
+ 'Kannada': 'kan','Kazakh': 'kaz','Kirghiz; Kyrgyz': 'kir','Korean (vertical)': 'kor_vert', \
135
+ 'Korean': 'kor','Kurdish (Arabic Script)': 'kur_ara','Lao': 'lao','Latin': 'lat', \
136
+ 'Latvian':'lav','Lithuanian':'lit','Luxembourgish':'ltz','Macedonian':'mkd','Malay':'msa', \
137
+ 'Malayalam': 'mal','Maltese': 'mlt','Maori': 'mri','Marathi': 'mar','Mongolian': 'mon', \
138
+ 'Nepali': 'nep','Norwegian': 'nor','Occitan (post 1500)': 'oci', \
139
+ 'Orientation and script detection module':'osd','Oriya':'ori','Panjabi; Punjabi':'pan', \
140
+ 'Persian':'fas','Polish':'pol','Portuguese':'por','Pushto; Pashto':'pus','Quechua':'que', \
141
+ 'Romanian; Moldavian; Moldovan': 'ron','Russian': 'rus','Sanskrit': 'san', \
142
+ 'Scottish Gaelic': 'gla','Serbian - Latin': 'srp_latn','Serbian': 'srp','Sindhi': 'snd', \
143
+ 'Sinhala; Sinhalese': 'sin','Slovak': 'slk','Slovenian': 'slv', \
144
+ 'Spanish; Castilian - Old': 'spa_old','Spanish; Castilian': 'spa','Sundanese': 'sun', \
145
+ 'Swahili': 'swa','Swedish': 'swe','Syriac': 'syr','Tajik': 'tgk','Tamil': 'tam', \
146
+ 'Tatar':'tat','Telugu':'tel','Thai':'tha','Tibetan':'bod','Tigrinya':'tir','Tonga':'ton', \
147
+ 'Turkish': 'tur','Uighur; Uyghur': 'uig','Ukrainian': 'ukr','Urdu': 'urd', \
148
+ 'Uzbek - Cyrilic': 'uzb_cyrl','Uzbek': 'uzb','Vietnamese': 'vie','Welsh': 'cym', \
149
+ 'Western Frisian': 'fry','Yiddish': 'yid','Yoruba': 'yor'}
150
+
151
+ out_list_dict_lang = [out_dict_lang_easyocr, out_dict_lang_ppocr, out_dict_lang_mmocr, \
152
+ out_dict_lang_tesseract]
153
+
154
+ # Initialization of detection form
155
+ if 'columns_size' not in st.session_state:
156
+ st.session_state.columns_size = [2] + [1 for x in out_reader_type_list[1:]]
157
+ if 'column_width' not in st.session_state:
158
+ st.session_state.column_width = [500] + [400 for x in out_reader_type_list[1:]]
159
+ if 'columns_color' not in st.session_state:
160
+ st.session_state.columns_color = ["rgb(228,26,28)"] + \
161
+ ["rgb(0,0,0)" for x in out_reader_type_list[1:]]
162
+ if 'list_coordinates' not in st.session_state:
163
+ st.session_state.list_coordinates = []
164
+
165
+ # Confidence color scale
166
+ out_list_confid = list(np.arange(0,101,1))
167
+ out_list_grad = mcp.gen_color_normalized(cmap="Greens",data_arr=np.array(out_list_confid))
168
+ out_dict_back_colors = {out_list_confid[i]: out_list_grad[i] \
169
+ for i in range(len(out_list_confid))}
170
+
171
+ list_y = [1 for i in out_list_confid]
172
+ df_confid = pd.DataFrame({'% confidence scale': out_list_confid, 'y': list_y})
173
+
174
+ out_fig = px.scatter(df_confid, x='% confidence scale', y='y', \
175
+ hover_data={'% confidence scale': True, 'y': False},
176
+ color=out_dict_back_colors.values(), range_y=[0.9,1.1], range_x=[0,100],
177
+ color_discrete_map="identity",height=50,symbol='y',symbol_sequence=['square'])
178
+ out_fig.update_xaxes(showticklabels=False)
179
+ out_fig.update_yaxes(showticklabels=False, range=[0.1, 1.1], visible=False)
180
+ out_fig.update_traces(marker_size=50)
181
+ out_fig.update_layout(paper_bgcolor="white", margin=dict(b=0,r=0,t=0,l=0), xaxis_side="top", \
182
+ showlegend=False)
183
+
184
+ return out_reader_type_list, out_reader_type_dict, out_list_dict_lang, \
185
+ out_cols_size, out_dict_back_colors, out_fig
186
+
187
+ ###
188
+ @st.experimental_memo(show_spinner=False)
189
+ def init_easyocr(in_params):
190
+ """Initialization of easyOCR reader
191
+
192
+ Args:
193
+ in_params (list): list with the language
194
+
195
+ Returns:
196
+ easyocr reader: the easyocr reader instance
197
+ """
198
+ out_ocr = easyocr.Reader(in_params)
199
+ return out_ocr
200
+
201
+ ###
202
+ @st.cache(show_spinner=False)
203
+ def init_ppocr(in_params):
204
+ """Initialization of PPOCR reader
205
+
206
+ Args:
207
+ in_params (dict): dict with parameters
208
+
209
+ Returns:
210
+ ppocr reader: the ppocr reader instance
211
+ """
212
+ out_ocr = PaddleOCR(lang=in_params[0], **in_params[1])
213
+ return out_ocr
214
+
215
+ ###
216
+ @st.experimental_memo(show_spinner=False)
217
+ def init_mmocr(in_params):
218
+ """Initialization of MMOCR reader
219
+
220
+ Args:
221
+ in_params (dict): dict with parameters
222
+
223
+ Returns:
224
+ mmocr reader: the ppocr reader instance
225
+ """
226
+ out_ocr = MMOCR(recog=None, **in_params[1])
227
+ return out_ocr
228
+
229
+ ###
230
+ def init_readers(in_list_params):
231
+ """Initialization of the readers, and return them as list
232
+
233
+ Args:
234
+ in_list_params (list): list of dicts of parameters for each reader
235
+
236
+ Returns:
237
+ list: list of the reader's instances
238
+ """
239
+ # Instantiations of the readers :
240
+ # - EasyOCR
241
+ with st.spinner("EasyOCR reader initialization in progress ..."):
242
+ reader_easyocr = init_easyocr([in_list_params[0][0]])
243
+
244
+ # - PPOCR
245
+ # Paddleocr
246
+ with st.spinner("PPOCR reader initialization in progress ..."):
247
+ reader_ppocr = init_ppocr(in_list_params[1])
248
+
249
+ # - MMOCR
250
+ with st.spinner("MMOCR reader initialization in progress ..."):
251
+ reader_mmocr = init_mmocr(in_list_params[2])
252
+
253
+ out_list_readers = [reader_easyocr, reader_ppocr, reader_mmocr]
254
+
255
+ return out_list_readers
256
+
257
+ ###
258
+ def load_image(in_image_file):
259
+ """Load input file and open it
260
+
261
+ Args:
262
+ in_image_file (string or Streamlit UploadedFile): image to consider
263
+
264
+ Returns:
265
+ string : locally saved image path (img.)
266
+ PIL.Image : input file opened with Pillow
267
+ matrix : input file opened with Opencv
268
+ """
269
+
270
+ #if isinstance(in_image_file, str):
271
+ # out_image_path = "img."+in_image_file.split('.')[-1]
272
+ #else:
273
+ # out_image_path = "img."+in_image_file.name.split('.')[-1]
274
+
275
+ if isinstance(in_image_file, str):
276
+ out_image_path = "tmp_"+in_image_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  else:
278
+ out_image_path = "tmp_"+in_image_file.name
279
+
280
+ img = Image.open(in_image_file)
281
+ img_saved = img.save(out_image_path)
282
+
283
+ # Read image
284
+ out_image_orig = Image.open(out_image_path)
285
+ out_image_cv2 = cv2.cvtColor(cv2.imread(out_image_path), cv2.COLOR_BGR2RGB)
286
+
287
+ return out_image_path, out_image_orig, out_image_cv2
288
+
289
+ ###
290
+ @st.experimental_memo(show_spinner=False)
291
+ def easyocr_detect(_in_reader, in_image_path, in_params):
292
+ """Detection with EasyOCR
293
+
294
+ Args:
295
+ _in_reader (EasyOCR reader) : the previously initialized instance
296
+ in_image_path (string ) : locally saved image path
297
+ in_params (list) : list with the parameters for detection
298
+
299
+ Returns:
300
+ list : list of the boxes coordinates
301
+ exception on error, string 'OK' otherwise
302
+ """
303
+ try:
304
+ dict_param = in_params[1]
305
+ detection_result = _in_reader.detect(in_image_path,
306
+ #width_ths=0.7,
307
+ #mag_ratio=1.5
308
+ **dict_param
309
+ )
310
+ easyocr_coordinates = detection_result[0][0]
311
+
312
+ # The format of the coordinate is as follows: [x_min, x_max, y_min, y_max]
313
+ # Format boxes coordinates for draw
314
+ out_easyocr_boxes_coordinates = list(map(easyocr_coord_convert, easyocr_coordinates))
315
+ out_status = 'OK'
316
+ except Exception as e:
317
+ out_easyocr_boxes_coordinates = []
318
+ out_status = e
319
+
320
+ return out_easyocr_boxes_coordinates, out_status
321
+
322
+ ###
323
+ @st.experimental_memo(show_spinner=False)
324
+ def ppocr_detect(_in_reader, in_image_path):
325
+ """Detection with PPOCR
326
+
327
+ Args:
328
+ _in_reader (PPOCR reader) : the previously initialized instance
329
+ in_image_path (string ) : locally saved image path
330
+
331
+ Returns:
332
+ list : list of the boxes coordinates
333
+ exception on error, string 'OK' otherwise
334
+ """
335
+ # PPOCR detection method
336
+ try:
337
+ out_ppocr_boxes_coordinates = _in_reader.ocr(in_image_path, rec=False)
338
+ out_status = 'OK'
339
+ except Exception as e:
340
+ out_ppocr_boxes_coordinates = []
341
+ out_status = e
342
+
343
+ return out_ppocr_boxes_coordinates, out_status
344
+
345
+ ###
346
+ @st.experimental_memo(show_spinner=False)
347
+ def mmocr_detect(_in_reader, in_image_path):
348
+ """Detection with MMOCR
349
+
350
+ Args:
351
+ _in_reader (EasyORC reader) : the previously initialized instance
352
+ in_image_path (string) : locally saved image path
353
+ in_params (list) : list with the parameters
354
+
355
+ Returns:
356
+ list : list of the boxes coordinates
357
+ exception on error, string 'OK' otherwise
358
+ """
359
+ # MMOCR detection method
360
+ out_mmocr_boxes_coordinates = []
361
+ try:
362
+ det_result = _in_reader.readtext(in_image_path, details=True)
363
+ bboxes_list = [res['boundary_result'] for res in det_result]
364
+ for bboxes in bboxes_list:
365
+ for bbox in bboxes:
366
+ if len(bbox) > 9:
367
+ min_x = min(bbox[0:-1:2])
368
+ min_y = min(bbox[1:-1:2])
369
+ max_x = max(bbox[0:-1:2])
370
+ max_y = max(bbox[1:-1:2])
371
+ #box = [min_x, min_y, max_x, min_y, max_x, max_y, min_x, max_y]
372
+ else:
373
+ min_x = min(bbox[0:-1:2])
374
+ min_y = min(bbox[1::2])
375
+ max_x = max(bbox[0:-1:2])
376
+ max_y = max(bbox[1::2])
377
+ box4 = [ [min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y] ]
378
+ out_mmocr_boxes_coordinates.append(box4)
379
+ out_status = 'OK'
380
+ except Exception as e:
381
+ out_status = e
382
+
383
+ return out_mmocr_boxes_coordinates, out_status
384
+
385
+ ###
386
+ def cropped_1box(in_box, in_img):
387
+ """Construction of an cropped image corresponding to an area of the initial image
388
+
389
+ Args:
390
+ in_box (list) : box with coordinates
391
+ in_img (matrix) : image
392
+
393
+ Returns:
394
+ matrix : cropped image
395
+ """
396
+ box_ar = np.array(in_box).astype(np.int64)
397
+ x_min = box_ar[:, 0].min()
398
+ x_max = box_ar[:, 0].max()
399
+ y_min = box_ar[:, 1].min()
400
+ y_max = box_ar[:, 1].max()
401
+ out_cropped = in_img[y_min:y_max, x_min:x_max]
402
+
403
+ return out_cropped
404
+
405
+ ###
406
+ @st.experimental_memo(show_spinner=False)
407
+ def tesserocr_detect(in_image_path, _in_img, in_params):
408
+ """Detection with Tesseract
409
+
410
+ Args:
411
+ in_image_path (string) : locally saved image path
412
+ _in_img (PIL.Image) : image to consider
413
+ in_params (list) : list with the parameters for detection
414
+
415
+ Returns:
416
+ list : list of the boxes coordinates
417
+ exception on error, string 'OK' otherwise
418
+ """
419
+ try:
420
+ dict_param = in_params[1]
421
+ df_res = pytesseract.image_to_data(_in_img, **dict_param, output_type=Output.DATAFRAME)
422
+
423
+ df_res['box'] = df_res.apply(lambda d: [[d['left'], d['top']], \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  [d['left'] + d['width'], d['top']], \
425
+ [d['left'] + d['width'], d['top'] + d['height']], \
426
  [d['left'], d['top'] + d['height']], \
427
+ ], axis=1)
428
+ out_tesserocr_boxes_coordinates = df_res[df_res.word_num > 0]['box'].to_list()
429
+ out_status = 'OK'
430
+ except Exception as e:
431
+ out_tesserocr_boxes_coordinates = []
432
+ out_status = e
433
+
434
+ return out_tesserocr_boxes_coordinates, out_status
435
+
436
+ ###
437
+ @st.experimental_memo(show_spinner=False)
438
+ def process_detect(in_image_path, _in_list_images, _in_list_readers, in_list_params, in_color):
439
+ """Detection process for each OCR solution
440
+
441
+ Args:
442
+ in_image_path (string) : locally saved image path
443
+ _in_list_images (list) : list of original image
444
+ _in_list_readers (list) : list with previously initialized reader's instances
445
+ in_list_params (list) : list with dict parameters for each OCR solution
446
+ in_color (tuple) : color for boxes around text
447
+
448
+ Returns:
449
+ list: list of detection results images
450
+ list: list of boxes coordinates
451
+ """
452
+ ## ------- EasyOCR Text detection
453
+ with st.spinner('EasyOCR Text detection in progress ...'):
454
+ easyocr_boxes_coordinates,easyocr_status = easyocr_detect(_in_list_readers[0], \
455
+ in_image_path, in_list_params[0])
456
+ # Visualization
457
+ if easyocr_boxes_coordinates:
458
+ easyocr_image_detect = draw_detected(_in_list_images[0], easyocr_boxes_coordinates, \
459
+ in_color, 'None', 3)
 
 
 
 
 
 
 
 
 
 
460
  else:
461
+ easyocr_boxes_coordinates = easyocr_status
462
+ ##
463
+
464
+ ## ------- PPOCR Text detection
465
+ with st.spinner('PPOCR Text detection in progress ...'):
466
+ ppocr_boxes_coordinates, ppocr_status = ppocr_detect(_in_list_readers[1], in_image_path)
467
+ # Visualization
468
+ if ppocr_boxes_coordinates:
469
+ ppocr_image_detect = draw_detected(_in_list_images[0], ppocr_boxes_coordinates, \
470
+ in_color, 'None', 3)
471
+ else:
472
+ ppocr_image_detect = ppocr_status
473
+ ##
474
+
475
+ ## ------- MMOCR Text detection
476
+ with st.spinner('MMOCR Text detection in progress ...'):
477
+ mmocr_boxes_coordinates, mmocr_status = mmocr_detect(_in_list_readers[2], in_image_path)
478
+ # Visualization
479
+ if mmocr_boxes_coordinates:
480
+ mmocr_image_detect = draw_detected(_in_list_images[0], mmocr_boxes_coordinates, \
481
+ in_color, 'None', 3)
482
  else:
483
+ mmocr_image_detect = mmocr_status
484
+ ##
485
+
486
+ ## ------- Tesseract Text detection
487
+ with st.spinner('Tesseract Text detection in progress ...'):
488
+ tesserocr_boxes_coordinates, tesserocr_status = tesserocr_detect(in_image_path, \
489
+ _in_list_images[0], \
490
+ in_list_params[3])
491
+ # Visualization
492
+ if tesserocr_status == 'OK':
493
+ tesserocr_image_detect = draw_detected(_in_list_images[0],tesserocr_boxes_coordinates,\
494
+ in_color, 'None', 3)
495
+ else:
496
+ tesserocr_image_detect = tesserocr_status
497
+ ##
498
+ #
499
+ out_list_images = _in_list_images + [easyocr_image_detect, ppocr_image_detect, \
500
+ mmocr_image_detect, tesserocr_image_detect]
501
+ out_list_coordinates = [easyocr_boxes_coordinates, ppocr_boxes_coordinates, \
502
+ mmocr_boxes_coordinates, tesserocr_boxes_coordinates]
503
+ #
504
+
505
+ return out_list_images, out_list_coordinates
506
+
507
+ ###
508
+ def draw_detected(in_image, in_boxes_coordinates, in_color, posit='None', in_thickness=4):
509
+ """Draw boxes around detected text
510
+
511
+ Args:
512
+ in_image (PIL.Image) : original image
513
+ in_boxes_coordinates (list) : boxes coordinates, from top to bottom and from left to right
514
+ [ [ [x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max] ],
515
+ [ ... ]
516
+ ]
517
+ in_color (tuple) : color for boxes around text
518
+ posit (str, optional) : position for text. Defaults to 'None'.
519
+ in_thickness (int, optional): thickness of the box. Defaults to 4.
520
+
521
+ Returns:
522
+ PIL.Image : original image with detected areas
523
+ """
524
+ work_img = in_image.copy()
525
+ if in_boxes_coordinates:
526
+ font = cv2.FONT_HERSHEY_SIMPLEX
527
+ for ind_box, box in enumerate(in_boxes_coordinates):
528
+ box = np.reshape(np.array(box), [-1, 1, 2]).astype(np.int64)
529
+ work_img = cv2.polylines(np.array(work_img), [box], True, in_color, in_thickness)
530
+ if posit != 'None':
531
+ if posit == 'top_left':
532
+ pos = tuple(box[0][0])
533
+ elif posit == 'top_right':
534
+ pos = tuple(box[1][0])
535
+ work_img = cv2.putText(work_img, str(ind_box+1), pos, font, 5.5, color, \
536
+ in_thickness,cv2.LINE_AA)
537
+
538
+ out_image_drawn = Image.fromarray(work_img)
539
+ else:
540
+ out_image_drawn = work_img
541
+
542
+ return out_image_drawn
543
+
544
+ ###
545
+ @st.experimental_memo(show_spinner=False)
546
+ def get_cropped(in_boxes_coordinates, in_image_cv):
547
+ """Construct list of cropped images corresponding of the input boxes coordinates list
548
+
549
+ Args:
550
+ in_boxes_coordinates (list) : list of boxes coordinates
551
+ in_image_cv (matrix) : original image
552
+
553
+ Returns:
554
+ list : list with cropped images
555
+ """
556
+ out_list_images = []
557
+ for box in in_boxes_coordinates:
558
+ cropped = cropped_1box(box, in_image_cv)
559
+ out_list_images.append(cropped)
560
+ return out_list_images
561
+
562
+ ###
563
+ def process_recog(in_list_readers, in_image_cv, in_boxes_coordinates, in_list_dict_params):
564
+ """Recognition process for each OCR solution
565
+
566
+ Args:
567
+ in_list_readers (list) : list with previously initialized reader's instances
568
+ in_image_cv (matrix) : original image
569
+ in_boxes_coordinates (list) : list of boxes coordinates
570
+ in_list_dict_params (list) : list with dict parameters for each OCR solution
571
+
572
+ Returns:
573
+ data frame : results for each OCR solution, except Tesseract
574
+ data frame : results for Tesseract
575
+ list : status for each recognition (exception or 'OK')
576
+ """
577
+ out_df_results = pd.DataFrame([])
578
+
579
+ list_text_easyocr = []
580
+ list_confidence_easyocr = []
581
+ list_text_ppocr = []
582
+ list_confidence_ppocr = []
583
+ list_text_mmocr = []
584
+ list_confidence_mmocr = []
585
+
586
+ # Create cropped images from detection
587
+ list_cropped_images = get_cropped(in_boxes_coordinates, in_image_cv)
588
+
589
+ # Recognize with EasyOCR
590
+ with st.spinner('EasyOCR Text recognition in progress ...'):
591
+ list_text_easyocr, list_confidence_easyocr, status_easyocr = \
592
+ easyocr_recog(list_cropped_images, in_list_readers[0], in_list_dict_params[0])
593
+ ##
594
+
595
+ # Recognize with PPOCR
596
+ with st.spinner('PPOCR Text recognition in progress ...'):
597
+ list_text_ppocr, list_confidence_ppocr, status_ppocr = \
598
+ ppocr_recog(list_cropped_images, in_list_dict_params[1])
599
+ ##
600
+
601
+ # Recognize with MMOCR
602
+ with st.spinner('MMOCR Text recognition in progress ...'):
603
+ list_text_mmocr, list_confidence_mmocr, status_mmocr = \
604
+ mmocr_recog(list_cropped_images, in_list_dict_params[2])
605
+ ##
606
+
607
+ # Recognize with Tesseract
608
+ with st.spinner('Tesseract Text recognition in progress ...'):
609
+ out_df_results_tesseract, status_tesseract = \
610
+ tesserocr_recog(in_image_cv, in_list_dict_params[3], len(list_cropped_images))
611
+ ##
612
+
613
+ # Create results data frame
614
+ out_df_results = pd.DataFrame({'cropped_image': list_cropped_images,
615
+ 'text_easyocr': list_text_easyocr,
616
+ 'confidence_easyocr': list_confidence_easyocr,
617
+ 'text_ppocr': list_text_ppocr,
618
+ 'confidence_ppocr': list_confidence_ppocr,
619
+ 'text_mmocr': list_text_mmocr,
620
+ 'confidence_mmocr': list_confidence_mmocr
621
+ }
622
+ )
623
+
624
+ out_list_reco_status = [status_easyocr, status_ppocr, status_mmocr, status_tesseract]
625
+
626
+ return out_df_results, out_df_results_tesseract, out_list_reco_status
627
+
628
+ ###
629
+ @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
630
+ def easyocr_recog(in_list_images, _in_reader_easyocr, in_params):
631
+ """Recognition with EasyOCR
632
+
633
+ Args:
634
+ in_list_images (list) : list of cropped images
635
+ _in_reader_easyocr (EasyOCR reader) : the previously initialized instance
636
+ in_params (dict) : parameters for recognition
637
+
638
+ Returns:
639
+ list : list of recognized text
640
+ list : list of recognition confidence
641
+ string/Exception : recognition status
642
+ """
643
+ progress_bar = st.progress(0)
644
+ out_list_text_easyocr = []
645
+ out_list_confidence_easyocr = []
646
+ ## ------- EasyOCR Text recognition
647
+ try:
648
+ step = 0*len(in_list_images) # first recognition process
649
+ nb_steps = 4 * len(in_list_images)
650
+ for ind_img, cropped in enumerate(in_list_images):
651
+ result = _in_reader_easyocr.recognize(cropped, **in_params)
652
+ try:
653
+ out_list_text_easyocr.append(result[0][1])
654
+ out_list_confidence_easyocr.append(np.round(100*result[0][2], 1))
655
+ except:
656
+ out_list_text_easyocr.append('Not recognize')
657
+ out_list_confidence_easyocr.append(100.)
658
+ progress_bar.progress((step+ind_img+1)/nb_steps)
659
+ out_status = 'OK'
660
+ except Exception as e:
661
+ out_status = e
662
+ progress_bar.empty()
663
+
664
+ return out_list_text_easyocr, out_list_confidence_easyocr, out_status
665
+
666
+ ###
667
+ @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
668
+ def ppocr_recog(in_list_images, in_params):
669
+ """Recognition with PPOCR
670
+
671
+ Args:
672
+ in_list_images (list) : list of cropped images
673
+ in_params (dict) : parameters for recognition
674
+
675
+ Returns:
676
+ list : list of recognized text
677
+ list : list of recognition confidence
678
+ string/Exception : recognition status
679
+ """
680
+ ## ------- PPOCR Text recognition
681
+ out_list_text_ppocr = []
682
+ out_list_confidence_ppocr = []
683
+ try:
684
+ reader_ppocr = PaddleOCR(**in_params)
685
+ step = 1*len(in_list_images) # second recognition process
686
+ nb_steps = 4 * len(in_list_images)
687
+ progress_bar = st.progress(step/nb_steps)
688
+
689
+ for ind_img, cropped in enumerate(in_list_images):
690
+ result = reader_ppocr.ocr(cropped, det=False, cls=False)
691
+ try:
692
+ out_list_text_ppocr.append(result[0][0])
693
+ out_list_confidence_ppocr.append(np.round(100*result[0][1], 1))
694
+ except:
695
+ out_list_text_ppocr.append('Not recognize')
696
+ out_list_confidence_ppocr.append(100.)
697
+ progress_bar.progress((step+ind_img+1)/nb_steps)
698
+ out_status = 'OK'
699
+ except Exception as e:
700
+ out_status = e
701
+ progress_bar.empty()
702
+
703
+ return out_list_text_ppocr, out_list_confidence_ppocr, out_status
704
+
705
+ ###
706
+ @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
707
+ def mmocr_recog(in_list_images, in_params):
708
+ """Recognition with MMOCR
709
+
710
+ Args:
711
+ in_list_images (list) : list of cropped images
712
+ in_params (dict) : parameters for recognition
713
+
714
+ Returns:
715
+ list : list of recognized text
716
+ list : list of recognition confidence
717
+ string/Exception : recognition status
718
+ """
719
+ ## ------- MMOCR Text recognition
720
+ out_list_text_mmocr = []
721
+ out_list_confidence_mmocr = []
722
+ try:
723
+ reader_mmocr = MMOCR(det=None, **in_params)
724
+ step = 2*len(in_list_images) # third recognition process
725
+ nb_steps = 4 * len(in_list_images)
726
+ progress_bar = st.progress(step/nb_steps)
727
+
728
+ for ind_img, cropped in enumerate(in_list_images):
729
+ result = reader_mmocr.readtext(cropped, details=True)
730
+ try:
731
+ out_list_text_mmocr.append(result[0]['text'])
732
+ out_list_confidence_mmocr.append(np.round(100* \
733
+ (np.array(result[0]['score']).mean()), 1))
734
+ except:
735
+ out_list_text_mmocr.append('Not recognize')
736
+ out_list_confidence_mmocr.append(100.)
737
+ progress_bar.progress((step+ind_img+1)/nb_steps)
738
+ out_status = 'OK'
739
+ except Exception as e:
740
+ out_status = e
741
+ progress_bar.empty()
742
+
743
+ return out_list_text_mmocr, out_list_confidence_mmocr, out_status
744
+
745
+ ###
746
+ @st.experimental_memo(suppress_st_warning=True, show_spinner=False)
747
+ def tesserocr_recog(in_img, in_params, in_nb_images):
748
+ """Recognition with Tesseract
749
+
750
+ Args:
751
+ in_image_cv (matrix) : original image
752
+ in_params (dict) : parameters for recognition
753
+ in_nb_images : nb cropped images (used for progress bar)
754
+
755
+ Returns:
756
+ Pandas data frame : recognition results
757
+ string/Exception : recognition status
758
+ """
759
+ ## ------- Tesseract Text recognition
760
+ step = 3*in_nb_images # fourth recognition process
761
+ nb_steps = 4 * in_nb_images
762
+ progress_bar = st.progress(step/nb_steps)
763
+
764
+ try:
765
+ out_df_result = pytesseract.image_to_data(in_img, **in_params,output_type=Output.DATAFRAME)
766
+
767
+ out_df_result['box'] = out_df_result.apply(lambda d: [[d['left'], d['top']], \
768
+ [d['left'] + d['width'], d['top']], \
769
+ [d['left']+d['width'], d['top']+d['height']], \
770
+ [d['left'], d['top'] + d['height']], \
771
+ ], axis=1)
772
+ out_df_result['cropped'] = out_df_result['box'].apply(lambda b: cropped_1box(b, in_img))
773
+ out_df_result = out_df_result[(out_df_result.word_num > 0) & (out_df_result.text != ' ')] \
774
+ .reset_index(drop=True)
775
+ out_status = 'OK'
776
+ except Exception as e:
777
+ out_df_result = pd.DataFrame([])
778
+ out_status = e
779
+
780
+ progress_bar.progress(1.)
781
+
782
+ return out_df_result, out_status
783
+
784
+ ###
785
+ def draw_reco_images(in_image, in_boxes_coordinates, in_list_texts, in_list_confid, \
786
+ in_dict_back_colors, in_df_results_tesseract, in_reader_type_list, \
787
+ in_font_scale=1, in_conf_threshold=65):
788
+ """Draw recognized text on original image, for each OCR solution used
789
+
790
+ Args:
791
+ in_image (matrix) : original image
792
+ in_boxes_coordinates (list) : list of boxes coordinates
793
+ in_list_texts (list): list of recognized text for each recognizer (except Tesseract)
794
+ in_list_confid (list): list of recognition confidence for each recognizer (except Tesseract)
795
+ in_df_results_tesseract (Pandas data frame): Tesseract recognition results
796
+ in_font_scale (int, optional): text font scale. Defaults to 3.
797
+
798
+ Returns:
799
+ shows the results container
800
+ """
801
+ img = in_image.copy()
802
+ nb_readers = len(in_reader_type_list)
803
+ list_reco_images = [img.copy() for i in range(nb_readers)]
804
+
805
+ for num, box_ in enumerate(in_boxes_coordinates):
806
+ box = np.array(box_).astype(np.int64)
807
+
808
+ # For each box : draw the results of each recognizer
809
+ for ind_r in range(nb_readers-1):
810
+ confid = np.round(in_list_confid[ind_r][num], 0)
811
+ rgb_color = ImageColor.getcolor(in_dict_back_colors[confid], "RGB")
812
+ if confid < in_conf_threshold:
813
+ text_color = (0, 0, 0)
814
+ else:
815
+ text_color = (255, 255, 255)
816
+
817
+ list_reco_images[ind_r] = cv2.rectangle(list_reco_images[ind_r], \
818
+ (box[0][0], box[0][1]), \
819
+ (box[2][0], box[2][1]), rgb_color, -1)
820
+ list_reco_images[ind_r] = cv2.putText(list_reco_images[ind_r], \
821
+ in_list_texts[ind_r][num], \
822
+ (box[0][0],int(np.round((box[0][1]+box[2][1])/2,0))), \
823
+ cv2.FONT_HERSHEY_DUPLEX, in_font_scale, text_color, 2)
824
+
825
+ # Add Tesseract process
826
+ if not in_df_results_tesseract.empty:
827
+ ind_tessocr = nb_readers-1
828
+ for num, box_ in enumerate(in_df_results_tesseract['box'].to_list()):
829
+ box = np.array(box_).astype(np.int64)
830
+ confid = np.round(in_df_results_tesseract.iloc[num]['conf'], 0)
831
+ rgb_color = ImageColor.getcolor(in_dict_back_colors[confid], "RGB")
832
+ if confid < in_conf_threshold:
833
+ text_color = (0, 0, 0)
834
+ else:
835
+ text_color = (255, 255, 255)
836
 
 
 
 
 
837
  list_reco_images[ind_tessocr] = \
838
+ cv2.rectangle(list_reco_images[ind_tessocr], (box[0][0], box[0][1]), \
839
+ (box[2][0], box[2][1]), rgb_color, -1)
840
+ try:
841
+ list_reco_images[ind_tessocr] = \
842
+ cv2.putText(list_reco_images[ind_tessocr], \
843
+ in_df_results_tesseract.iloc[num]['text'], \
844
+ (box[0][0],int(np.round((box[0][1]+box[2][1])/2,0))), \
845
+ cv2.FONT_HERSHEY_DUPLEX, in_font_scale, text_color, 2)
846
+
847
+ except:
848
+
849
+ pass
850
+
851
+ with show_reco.container():
852
+ # Draw the results, 2 images per line
853
+ reco_lines = math.ceil(len(in_reader_type_list) / 2)
854
+ column_width = 500
855
+ for ind_lig in range(0, reco_lines+1, 2):
856
+ cols = st.columns(2)
857
+ for ind_col in range(2):
858
+ ind = ind_lig + ind_col
859
+ if ind <= len(in_reader_type_list):
860
+ if in_reader_type_list[ind] == 'Tesseract':
861
+ column_title = '<p style="font-size: 20px;color:rgb(0,0,0); \
862
+ ">Recognition with ' + in_reader_type_list[ind] + \
863
+ '<sp style="font-size: 17px"> (with its own detector) \
864
+ </sp></p>'
865
+ else:
866
+ column_title = '<p style="font-size: 20px;color:rgb(0,0,0); \
867
+ ">Recognition with ' + \
868
+ in_reader_type_list[ind]+ '</p>'
869
+ cols[ind_col].markdown(column_title, unsafe_allow_html=True)
870
+ if st.session_state.list_reco_status[ind] == 'OK':
871
+ cols[ind_col].image(list_reco_images[ind], \
872
+ width=column_width, use_column_width=True)
873
+ else:
874
+ cols[ind_col].write(list_reco_status[ind], \
875
+ use_column_width=True)
876
+
877
+ st.markdown(' 💡 Bad font size? you can adjust it below and refresh:')
878
+
879
+ ###
880
+ def highlight():
881
+ """ Highlight choosen detector results
882
+ """
883
+ with show_detect.container():
884
+ columns_size = [1 for x in reader_type_list]
885
+ column_width = [400 for x in reader_type_list]
886
+ columns_color = ["rgb(0,0,0)" for x in reader_type_list]
887
+ columns_size[reader_type_dict[st.session_state.detect_reader]] = 2
888
+ column_width[reader_type_dict[st.session_state.detect_reader]] = 500
889
+ columns_color[reader_type_dict[st.session_state.detect_reader]] = "rgb(228,26,28)"
890
+ columns = st.columns(columns_size, ) #gap='medium')
891
+
892
+ for ind_col, col in enumerate(columns):
893
+ column_title = '<p style="font-size: 20px;color:'+columns_color[ind_col] + \
894
+ ';">Detection with ' + reader_type_list[ind_col]+ '</p>'
895
+ col.markdown(column_title, unsafe_allow_html=True)
896
+ if isinstance(list_images[ind_col+2], PIL.Image.Image):
897
+ col.image(list_images[ind_col+2], width=column_width[ind_col], \
898
+ use_column_width=True)
899
+ else:
900
+ col.write(list_images[ind_col+2], use_column_width=True)
901
+ st.session_state.columns_size = columns_size
902
+ st.session_state.column_width = column_width
903
+ st.session_state.columns_color = columns_color
 
 
 
 
 
 
 
904
 
905
+ ###
906
+ @st.cache(show_spinner=False)
907
+ def get_demo():
908
+ """Get the demo files
909
 
910
+ Returns:
911
+ PIL.Image : input file opened with Pillow
912
+ PIL.Image : input file opened with Pillow
913
+ """
914
 
915
+ out_img_demo_1 = Image.open("img_demo_1.jpg")
916
+ out_img_demo_2 = Image.open("img_demo_2.jpg")
917
+
918
+ return out_img_demo_1, out_img_demo_2
919
+
920
+ ###
921
+ def raz():
922
+ st.session_state.list_coordinates = []
923
 
 
924
 
 
 
 
925
 
 
 
 
 
926
  ##----------- Initializations ---------------------------------------------------------------------
927
  #print("PID : ", os.getpid())
928
 
 
980
  with st.form("form1"):
981
  col1, col2 = st.columns(2, ) #gap="medium")
982
  col1.markdown("##### Original image")
983
+ col1.image(list_images[0], width=400)
984
  col2.markdown("##### Hyperparameters values for detection")
985
 
986
  with col2.expander("Choose detection hyperparameters for " + reader_type_list[0], \