Skip to content

Commit 0e57493

Browse files
committed
Add boilerplate functions to return full probabilities.
1 parent b7ddee3 commit 0e57493

2 files changed

Lines changed: 205 additions & 2 deletions

File tree

easyocr/easyocr.py

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
from .detection import get_detector, get_textbox
4-
from .recognition import get_recognizer, get_text
4+
from .recognition import get_recognizer, get_text, get_text_prob
55
from .utils import group_text_box, get_image_list, calculate_md5, get_paragraph,\
66
download_and_unzip, printProgressBar, diff, reformat_input,\
77
make_rotated_img_list, set_result_with_confidence,\
@@ -298,6 +298,85 @@ def detect(self, img, min_size = 20, text_threshold = 0.7, low_text = 0.4,\
298298

299299
return horizontal_list_agg, free_list_agg
300300

301+
def recognize_prob(self, img_cv_grey, horizontal_list=None, free_list=None,\
302+
decoder = 'greedy', beamWidth= 5, batch_size = 1,\
303+
workers = 0, allowlist = None, blocklist = None, detail = 1,\
304+
rotation_info = None,paragraph = False,\
305+
contrast_ths = 0.1,adjust_contrast = 0.5, filter_ths = 0.003,\
306+
y_ths = 0.5, x_ths = 1.0, reformat=True, output_format='standard'):
307+
308+
if reformat:
309+
img, img_cv_grey = reformat_input(img_cv_grey)
310+
311+
if allowlist:
312+
ignore_char = ''.join(set(self.character)-set(allowlist))
313+
elif blocklist:
314+
ignore_char = ''.join(set(blocklist))
315+
else:
316+
ignore_char = ''.join(set(self.character)-set(self.lang_char))
317+
318+
if self.model_lang in ['chinese_tra','chinese_sim']: decoder = 'greedy'
319+
320+
if (horizontal_list==None) and (free_list==None):
321+
y_max, x_max = img_cv_grey.shape
322+
horizontal_list = [[0, x_max, 0, y_max]]
323+
free_list = []
324+
325+
# without gpu/parallelization, it is faster to process image one by one
326+
if ((batch_size == 1) or (self.device == 'cpu')) and not rotation_info:
327+
result = []
328+
for bbox in horizontal_list:
329+
h_list = [bbox]
330+
f_list = []
331+
image_list, max_width = get_image_list(h_list, f_list, img_cv_grey, model_height = imgH)
332+
result0 = get_text_prob(self.character, imgH, int(max_width), self.recognizer, self.converter, image_list,\
333+
ignore_char, decoder, beamWidth, batch_size, contrast_ths, adjust_contrast, filter_ths,\
334+
workers, self.device)
335+
result += result0
336+
for bbox in free_list:
337+
h_list = []
338+
f_list = [bbox]
339+
image_list, max_width = get_image_list(h_list, f_list, img_cv_grey, model_height = imgH)
340+
result0 = get_text_prob(self.character, imgH, int(max_width), self.recognizer, self.converter, image_list,\
341+
ignore_char, decoder, beamWidth, batch_size, contrast_ths, adjust_contrast, filter_ths,\
342+
workers, self.device)
343+
result += result0
344+
# default mode will try to process multiple boxes at the same time
345+
else:
346+
image_list, max_width = get_image_list(horizontal_list, free_list, img_cv_grey, model_height = imgH)
347+
image_len = len(image_list)
348+
if rotation_info and image_list:
349+
image_list = make_rotated_img_list(rotation_info, image_list)
350+
max_width = max(max_width, imgH)
351+
352+
result = get_text_prob(self.character, imgH, int(max_width), self.recognizer, self.converter, image_list,\
353+
ignore_char, decoder, beamWidth, batch_size, contrast_ths, adjust_contrast, filter_ths,\
354+
workers, self.device)
355+
356+
if rotation_info and (horizontal_list+free_list):
357+
# Reshape result to be a list of lists, each row being for
358+
# one of the rotations (first row being no rotation)
359+
result = set_result_with_confidence(
360+
[result[image_len*i:image_len*(i+1)] for i in range(len(rotation_info) + 1)])
361+
362+
if self.model_lang == 'arabic':
363+
direction_mode = 'rtl'
364+
result = [list(item) for item in result]
365+
for item in result:
366+
item[1] = get_display(item[1])
367+
else:
368+
direction_mode = 'ltr'
369+
370+
if paragraph:
371+
result = get_paragraph(result, x_ths=x_ths, y_ths=y_ths, mode = direction_mode)
372+
373+
if detail == 0:
374+
return [item[1] for item in result]
375+
elif output_format == 'dict':
376+
return [ {'boxes':item[0],'text':item[1],'confident':item[2]} for item in result]
377+
else:
378+
return result
379+
301380
def recognize(self, img_cv_grey, horizontal_list=None, free_list=None,\
302381
decoder = 'greedy', beamWidth= 5, batch_size = 1,\
303382
workers = 0, allowlist = None, blocklist = None, detail = 1,\
@@ -406,6 +485,36 @@ def readtext(self, image, decoder = 'greedy', beamWidth= 5, batch_size = 1,\
406485
filter_ths, y_ths, x_ths, False, output_format)
407486

408487
return result
488+
489+
def readtext_prob(self, image, decoder = 'greedy', beamWidth= 5, batch_size = 1,\
490+
workers = 0, allowlist = None, blocklist = None, detail = 1,\
491+
rotation_info = None, paragraph = False, min_size = 20,\
492+
contrast_ths = 0.1,adjust_contrast = 0.5, filter_ths = 0.003,\
493+
text_threshold = 0.7, low_text = 0.4, link_threshold = 0.4,\
494+
canvas_size = 2560, mag_ratio = 1.,\
495+
slope_ths = 0.1, ycenter_ths = 0.5, height_ths = 0.5,\
496+
width_ths = 0.5, y_ths = 0.5, x_ths = 1.0, add_margin = 0.1, output_format='standard'):
497+
'''
498+
Parameters:
499+
image: file path or numpy-array or a byte stream object
500+
'''
501+
img, img_cv_grey = reformat_input(image)
502+
503+
horizontal_list, free_list = self.detect(img, min_size, text_threshold,\
504+
low_text, link_threshold,\
505+
canvas_size, mag_ratio,\
506+
slope_ths, ycenter_ths,\
507+
height_ths,width_ths,\
508+
add_margin, False)
509+
# get the 1st result from hor & free list as self.detect returns a list of depth 3
510+
horizontal_list, free_list = horizontal_list[0], free_list[0]
511+
result = self.recognize_prob(img_cv_grey, horizontal_list, free_list,\
512+
decoder, beamWidth, batch_size,\
513+
workers, allowlist, blocklist, detail, rotation_info,\
514+
paragraph, contrast_ths, adjust_contrast,\
515+
filter_ths, y_ths, x_ths, False, output_format)
516+
517+
return result
409518

410519
def readtextlang(self, image, decoder = 'greedy', beamWidth= 5, batch_size = 1,\
411520
workers = 0, allowlist = None, blocklist = None, detail = 1,\

easyocr/recognition.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def recognizer_predict(model, converter, test_loader, batch_max_length,\
138138
indices = preds_prob.argmax(axis=2)
139139
preds_max_prob = []
140140
for v,i in zip(values, indices):
141-
max_probs = v[i!=0]
141+
max_probs = v[i!=0] # this removes blanks
142142
if len(max_probs)>0:
143143
preds_max_prob.append(max_probs)
144144
else:
@@ -150,6 +150,49 @@ def recognizer_predict(model, converter, test_loader, batch_max_length,\
150150

151151
return result
152152

153+
154+
def recognizer_predict_prob(model, converter, test_loader, batch_max_length,\
155+
ignore_idx, char_group_idx, decoder = 'greedy', beamWidth= 5, device = 'cpu'):
156+
model.eval()
157+
result = []
158+
with torch.no_grad():
159+
for image_tensors in test_loader:
160+
batch_size = image_tensors.size(0)
161+
image = image_tensors.to(device)
162+
# For max length prediction
163+
length_for_pred = torch.IntTensor([batch_max_length] * batch_size).to(device)
164+
text_for_pred = torch.LongTensor(batch_size, batch_max_length + 1).fill_(0).to(device)
165+
166+
preds = model(image, text_for_pred)
167+
168+
# Select max probabilty (greedy decoding) then decode index to character
169+
preds_size = torch.IntTensor([preds.size(1)] * batch_size)
170+
171+
######## filter ignore_char, rebalance
172+
preds_prob = F.softmax(preds, dim=2)
173+
preds_prob = preds_prob.cpu().detach().numpy()
174+
preds_prob[:,:,ignore_idx] = 0.
175+
pred_norm = preds_prob.sum(axis=2)
176+
preds_prob = preds_prob/np.expand_dims(pred_norm, axis=-1)
177+
preds_prob = torch.from_numpy(preds_prob).float().to(device)
178+
preds_prob = preds_prob.cpu().detach().numpy()
179+
180+
values = preds_prob.max(axis=2)
181+
indices = preds_prob.argmax(axis=2)
182+
preds_max_prob = []
183+
for v,i in zip(values, indices):
184+
max_probs = v[i!=0] # this removes blanks
185+
if len(max_probs)>0:
186+
preds_max_prob.append(max_probs)
187+
else:
188+
preds_max_prob.append(np.array([0]))
189+
190+
for pred_max_prob in preds_max_prob:
191+
confidence_score = custom_mean(pred_max_prob)
192+
result.append([preds_prob, confidence_score])
193+
194+
return result
195+
153196
def get_recognizer(recog_network, network_params, character,\
154197
separator_list, dict_list, model_path,\
155198
device = 'cpu', quantize = True):
@@ -231,3 +274,54 @@ def get_text(character, imgH, imgW, recognizer, converter, image_list,\
231274
result.append( (box, pred1[0], pred1[1]) )
232275

233276
return result
277+
278+
279+
def get_text_prob(character, imgH, imgW, recognizer, converter, image_list,\
280+
ignore_char = '',decoder = 'greedy', beamWidth =5, batch_size=1, contrast_ths=0.1,\
281+
adjust_contrast=0.5, filter_ths = 0.003, workers = 1, device = 'cpu'):
282+
batch_max_length = int(imgW/10)
283+
284+
char_group_idx = {}
285+
ignore_idx = []
286+
for char in ignore_char:
287+
try: ignore_idx.append(character.index(char)+1)
288+
except: pass
289+
290+
coord = [item[0] for item in image_list]
291+
img_list = [item[1] for item in image_list]
292+
AlignCollate_normal = AlignCollate(imgH=imgH, imgW=imgW, keep_ratio_with_pad=True)
293+
test_data = ListDataset(img_list)
294+
test_loader = torch.utils.data.DataLoader(
295+
test_data, batch_size=batch_size, shuffle=False,
296+
num_workers=int(workers), collate_fn=AlignCollate_normal, pin_memory=True)
297+
298+
# predict first round
299+
# NOTE: this now has preds_prob as first arg instead of pred_str
300+
result1 = recognizer_predict_prob(recognizer, converter, test_loader,batch_max_length,\
301+
ignore_idx, char_group_idx, decoder, beamWidth, device = device)
302+
303+
# predict second round
304+
low_confident_idx = [i for i, item in enumerate(result1) if (item[1] < contrast_ths)]
305+
if len(low_confident_idx) > 0:
306+
img_list2 = [img_list[i] for i in low_confident_idx]
307+
AlignCollate_contrast = AlignCollate(imgH=imgH, imgW=imgW, keep_ratio_with_pad=True, adjust_contrast=adjust_contrast)
308+
test_data = ListDataset(img_list2)
309+
test_loader = torch.utils.data.DataLoader(
310+
test_data, batch_size=batch_size, shuffle=False,
311+
num_workers=int(workers), collate_fn=AlignCollate_contrast, pin_memory=True)
312+
result2 = recognizer_predict_prob(recognizer, converter, test_loader, batch_max_length,\
313+
ignore_idx, char_group_idx, decoder, beamWidth, device = device)
314+
315+
result = []
316+
for i, zipped in enumerate(zip(coord, result1)):
317+
box, pred1 = zipped
318+
if i in low_confident_idx:
319+
pred2 = result2[low_confident_idx.index(i)]
320+
if pred1[1] > pred2[1]: # yes still confidence
321+
result.append( (box, pred1[0], pred1[1]) )
322+
else:
323+
result.append( (box, pred2[0], pred2[1]) )
324+
else:
325+
result.append( (box, pred1[0], pred1[1]) )
326+
327+
return result

0 commit comments

Comments
 (0)