Nepali OCR detector

In this post I am going to present Nepali Optical Character Recognition (OCR) that extracts Nepali text from images and scanned documents so that it can be edited, formatted, indexed, searched, or translated. Below mentioned code are written in python, using easyocr as a heart of this post.

OCR

Optical character recognition or optical character reader (OCR) is the electronic or mechanical conversion of images of typed, handwritten or printed text into machine-encoded text, whether from a scanned document, a photo of a document, a scene-photo (for example the text on signs and billboards in a landscape photo) or from subtitle text superimposed on an image (for example: from a television broadcast).

Widely used as a form of data entry from printed paper data records тАУ whether passport documents, invoices, bank statements, computerized receipts, business cards, mail, printouts of static-data, or any suitable documentation тАУ it is a common method of digitizing printed texts so that they can be electronically edited, searched, stored more compactly, displayed on-line, and used in machine processes such as cognitive computing, machine translation, (extracted) text-to-speech, key data and text mining. OCR is a field of research in pattern recognition, artificial intelligence and computer vision.

Early versions needed to be trained with images of each character, and worked on one font at a time. Advanced systems capable of producing a high degree of recognition accuracy for most fonts are now common, and with support for a variety of digital image file format inputs. Some systems are capable of reproducing formatted output that closely approximates the original page including images, columns, and other non-textual components. More about OCR

Importing Module

#!pip install easyocr #To install easyocr
import matplotlib.pyplot as plt
import cv2
import easyocr
import numpy as np
from pylab import rcParams
from IPython.display import Image
rcParams['figure.figsize'] = 8, 16

Loading pre-trained model

'ne' for Nepali and 'en' for English, and similarly for other languages.

reader = easyocr.Reader(['ne']) #'ne' for Nepali and 'en' for english and simillary for other

Normal image to Scanned image

def map(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

def highPassFilter(img,kSize):
    if not kSize%2:
        kSize +=1
    kernel = np.ones((kSize,kSize),np.float32)/(kSize*kSize)
    filtered = cv2.filter2D(img,-1,kernel)
    filtered = img.astype('float32') - filtered.astype('float32')
    filtered = filtered + 127*np.ones(img.shape, np.uint8)
    filtered = filtered.astype('uint8')
    return filtered

def blackPointSelect(img, blackPoint):
    img = img.astype('int32')
    img = map(img, blackPoint, 255, 0, 255)
    _, img = cv2.threshold(img, 0, 255, cv2.THRESH_TOZERO)
    img = img.astype('uint8')
    return img

def whitePointSelect(img,whitePoint):
    _,img = cv2.threshold(img, whitePoint, 255, cv2.THRESH_TRUNC)
    img = img.astype('int32')
    img = map(img, 0, whitePoint, 0, 255)
    img = img.astype('uint8')
    return img

def blackAndWhite(img):
    lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    (l,a,b) = cv2.split(lab)
    img = cv2.add( cv2.subtract(l,b), cv2.subtract(l,a) )
    return img

def scan_effect(img):
    blackPoint = 66
    whitePoint = 130
    image = highPassFilter(img,kSize = 51)
    image_white = whitePointSelect(image, whitePoint)
    img_black = blackPointSelect(image_white, blackPoint)
    image=blackPointSelect(img,blackPoint)
    white = whitePointSelect(image,whitePoint)
    img_black = blackAndWhite(white)
    return img_black

Enter the location of image file

loc="2.jpg" #Enter the loction of image
Image(loc)

Input image showing Nepali text

img = cv2.imread(loc)
image = scan_effect(img)
#from google.colab.patches import cv2_imshow
#cv2_imshow(image)
filename = 'scanned.jpg'
cv2.imwrite(filename, image)
True

Loading Image

path=filename
Image(path)

Scanned version of the input image

Detecting character from image

output = reader.readtext(path)

Output

output
[([[123, 1], [667, 1], [667, 67], [123, 67]],
  'рддреНрдпрд╛ рдмрд╛рдВрдЪреНрди рдХреЗ рдвреБрдХреБрд░ рдЭреИрдВ рдирднрдорд╛ рдбреБрд▓реЗрд░',
  0.3381714640956552),
 ([[129, 59], [409, 59], [409, 128], [129, 128]],
  'рддреНрдпреЛ рдорд░реНрдиреБ рдХреЗ рдХреБрдХреБрд░',
  0.6069389645579002),
 ([[424, 60], [663, 60], [663, 116], [424, 116]],
  'рдЭреИрдВ рдкрдердорд╛ рдврд▓реЗрд░',
  0.678598319341647),
 ([[141, 121], [655, 121], [655, 185], [141, 185]],
  'рддреНрдпрд╛реЗ рдЬрд┐рддреНрди рдХреЗ рдордирдкрд░рд┐ рджреБрдирд┐рдпрд╛ рдардЧреЗрд░',
  0.466542709532707),
 ([[134, 180], [656, 180], [656, 236], [134, 236]],
  'рдмрд╛рдВрдЪреМрдВ рдкрд╡рд┐рддреНрд░ рдЬрд▓рдХреЛ рд╕рд░рд┐рддрд╛ рдмрдирд░реЗ ред',
  0.33612146115405894),
 ([[151, 267], [647, 267], [647, 329], [151, 329]],
  'рдЧрд░реНрджреИ рдХрд┐ рдмреБрджреНрдШ рдЬрд╕рд░реА рд╕рдмрдХреЛ рднрд▓рд╛рдЗ',
  0.5689838746811848),
 ([[138, 329], [294, 329], [294, 380], [138, 380]],
  'рд╡рд╛ рдЬреНрдЮрд╛рдирд▓реЗ',
  0.5712557530269956),
 ([[428, 325], [639, 325], [639, 392], [428, 392]],
  'рджреБрдирд┐рдпрд╛рдВ рдЬрд┐рдЧрд╛рдИ',
  0.5282788595967648),
 ([[135, 383], [667, 383], [667, 445], [135, 445]],
  'рд╡рд╛ рдХреГрд╖реНрдг рдЭреИрдВ рдЬрдЧрддреНрдорд╛ рд░рдорд┐рддрд╛ рдЪрд▓рд╛рдИ',
  0.6665105167823989),
 ([[136, 462], [176, 462], [176, 494], [136, 494]], 'рд╡рд╛', 0.41052963629001243),
 ([[181, 441], [673, 441], [673, 501], [181, 501]],
  'рд╡рд┐рд╢реНрд╡рдорд╛рдЭ рд╕рдмрдорд╛ рдордорддрд╛ рдлрд┐рдЬрд╛рдИ',
  0.6406025192944045),
 ([[151, 535], [275, 535], [275, 579], [151, 579]],
  'рд╕рдЧреНрд▓реЛ рд░',
  0.7202154126778443),
 ([[278, 519], [659, 519], [659, 594], [278, 594]],
  'рдкреВрд░реНрдг рд╕рдкрдирд╛ рдордирднрд┐рддреНрд░ рд╕рд╛рдВрдЪреМрдВ',
  0.8457427886612249),
 ([[169, 577], [644, 577], [644, 645], [169, 645]],
  'рдЫреЛрдЯреЛ рдЫ рд╣реИ рд╕рдордпрдореИ рднрд░рдкреБрд░ рдмрд╛рдВрдЪреМрдВ',
  0.4758441227091572),
 ([[151, 637], [659, 637], [659, 701], [151, 701]],
  'рд╣рд╛рдВрд╕реНрдиреЗ рдЧрд░реЛрд╕реН рдорди рд╕рдзреНрд░реИ рджреБрдирд┐рдпрд╛рдВ рднреБрд▓реЗрд░',
  0.5020757275372445),
 ([[134, 698], [676, 698], [676, 754], [134, 754]],
  'рдбрд╛рд╣рд╛ рдЧрд░реЛрд╕реН рдЬрдирддрд╛ рдиреИ рд░рд┐рд╕рдорд╛ рдЬрд▓реЗрд░ ред',
  0.4316845329398307),
 ([[161, 777], [653, 777], [653, 839], [161, 839]],
  'рд╕рддреНрдХрд░реНрдорд▓реЗ рдЬрдЧрддреНрдХреЛ рд╢рд┐рд░рдорд╛ рдкреБрдЧрд┐рдиреНрдЫ',
  0.7593451815519658),
 ([[308, 834], [659, 834], [659, 888], [308, 888]],
  'рд╣рджрдпрдХрд╛ рд╕рдм рджрд╛рдЧ рдмрд┐рд▓реНрдЫ',
  0.8728011010651449),
 ([[159, 889], [661, 889], [661, 949], [159, 949]],
  'рдЖрд░реНрджрд╢ рдЬреАрд╡рди рд╕рдзреИрдВ рдЕрддрд┐ рдкреВрдЬреНрдп рд╣реБрдиреНрдЫ',
  0.5903392799676969),
 ([[135, 946], [683, 946], [683, 1006], [135, 1006]],
  'рд╕рддреНрдХрд░реНрдо рд╕рдиреНрддрддрд┐рд╣рд░реВ рд╕рдмрдорд╛ рдлрд┐рдЬрд┐рдиреНрдЫ редредред',
  0.493018168522814),
 ([[134, 1030], [418, 1030], [418, 1082], [134, 1082]],
  'рдЬреЛ рд░рдореНрдЫ рд╡реНрдпрд░реНрдердкрдирдорд╛',
  0.8048376333582101),
 ([[443, 1024], [702, 1024], [702, 1084], [443, 1084]],
  'рддреНрдпрд╕рдореИ рд╕рдХрд┐рдиреНрдЫрдиреН',
  0.5815147920443708),
 ([[134, 1086], [700, 1086], [700, 1143], [134, 1143]],
  'рдЬреЛ рдмреБрдЭрдЫ рдЬреАрд╡рди рдЙрдиреА рдЗрддрд┐рд╣рд╛рд╕ рдмрдиреНрдЫрдиреН',
  0.7268483165161402),
 ([[163, 1138], [664, 1138], [664, 1192], [163, 1192]],
  'рдЭрдХреНрдиреЗрдЫ рдИрд╢реНрд╡рд░ рдкрдирд┐ рдЙрд╕рдХреЛ рдЕрдЧрд╛рдбрд┐',
  0.47465339469231005),
 ([[124, 1192], [708, 1192], [708, 1246], [124, 1246]],
  'рдЬреЛ рдмрд╛рдВрдЪреНрдЫ рд╕реНрд╡рдЪреНрдЫ рджрд┐рд▓рд▓реЗ рд╕рдмрд▓рд╛рдИ рдмрд╛рдВрдбреА редреж',
  0.41452520111176533),
 ([[295.2103500411843, 334.2731201317898],
   [429.4015153514063, 324.3877917562871],
   [430.7896499588157, 377.7268798682102],
   [296.5984846485937, 388.6122082437129]],
  'рдЬрдирдХрднреИрдВ',
  0.8082762642642637),
 ([[160.20510460345565, 850.3332719689847],
   [304.5957251500903, 837.0304035071042],
   [306.79489539654435, 885.6667280310153],
   [162.4042748499097, 897.9695964928958]],
  'рдореБрд╕реНрдХрд╛рдирд▓реЗ',
  0.9778876236551189)]

Total detection

print(f'Total number of detection',len(output))
Total number of detection 27

Previewing Output

image = cv2.imread(path)
for i in range(len(output)):
  cord = output[i][0]
  x_min, y_min = [int(min(idx)) for idx in zip(*cord)]
  x_max, y_max = [int(max(idx)) for idx in zip(*cord)]
  cv2.rectangle(image,(x_min,y_min),(x_max,y_max),(0,0,255),2)
  print(output[i][1])

plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
рддреНрдпрд╛ рдмрд╛рдВрдЪреНрди рдХреЗ рдвреБрдХреБрд░ рдЭреИрдВ рдирднрдорд╛ рдбреБрд▓реЗрд░
рддреНрдпреЛ рдорд░реНрдиреБ рдХреЗ рдХреБрдХреБрд░
рдЭреИрдВ рдкрдердорд╛ рдврд▓реЗрд░
рддреНрдпрд╛реЗ рдЬрд┐рддреНрди рдХреЗ рдордирдкрд░рд┐ рджреБрдирд┐рдпрд╛ рдардЧреЗрд░
рдмрд╛рдВрдЪреМрдВ рдкрд╡рд┐рддреНрд░ рдЬрд▓рдХреЛ рд╕рд░рд┐рддрд╛ рдмрдирд░реЗ ред
рдЧрд░реНрджреИ рдХрд┐ рдмреБрджреНрдШ рдЬрд╕рд░реА рд╕рдмрдХреЛ рднрд▓рд╛рдЗ
рд╡рд╛ рдЬреНрдЮрд╛рдирд▓реЗ
рджреБрдирд┐рдпрд╛рдВ рдЬрд┐рдЧрд╛рдИ
рд╡рд╛ рдХреГрд╖реНрдг рдЭреИрдВ рдЬрдЧрддреНрдорд╛ рд░рдорд┐рддрд╛ рдЪрд▓рд╛рдИ
рд╡рд╛
рд╡рд┐рд╢реНрд╡рдорд╛рдЭ рд╕рдмрдорд╛ рдордорддрд╛ рдлрд┐рдЬрд╛рдИ
рд╕рдЧреНрд▓реЛ рд░
рдкреВрд░реНрдг рд╕рдкрдирд╛ рдордирднрд┐рддреНрд░ рд╕рд╛рдВрдЪреМрдВ
рдЫреЛрдЯреЛ рдЫ рд╣реИ рд╕рдордпрдореИ рднрд░рдкреБрд░ рдмрд╛рдВрдЪреМрдВ
рд╣рд╛рдВрд╕реНрдиреЗ рдЧрд░реЛрд╕реН рдорди рд╕рдзреНрд░реИ рджреБрдирд┐рдпрд╛рдВ рднреБрд▓реЗрд░
рдбрд╛рд╣рд╛ рдЧрд░реЛрд╕реН рдЬрдирддрд╛ рдиреИ рд░рд┐рд╕рдорд╛ рдЬрд▓реЗрд░ ред
рд╕рддреНрдХрд░реНрдорд▓реЗ рдЬрдЧрддреНрдХреЛ рд╢рд┐рд░рдорд╛ рдкреБрдЧрд┐рдиреНрдЫ
рд╣рджрдпрдХрд╛ рд╕рдм рджрд╛рдЧ рдмрд┐рд▓реНрдЫ
рдЖрд░реНрджрд╢ рдЬреАрд╡рди рд╕рдзреИрдВ рдЕрддрд┐ рдкреВрдЬреНрдп рд╣реБрдиреНрдЫ
рд╕рддреНрдХрд░реНрдо рд╕рдиреНрддрддрд┐рд╣рд░реВ рд╕рдмрдорд╛ рдлрд┐рдЬрд┐рдиреНрдЫ редредред
рдЬреЛ рд░рдореНрдЫ рд╡реНрдпрд░реНрдердкрдирдорд╛
рддреНрдпрд╕рдореИ рд╕рдХрд┐рдиреНрдЫрдиреН
рдЬреЛ рдмреБрдЭрдЫ рдЬреАрд╡рди рдЙрдиреА рдЗрддрд┐рд╣рд╛рд╕ рдмрдиреНрдЫрдиреН
рдЭрдХреНрдиреЗрдЫ рдИрд╢реНрд╡рд░ рдкрдирд┐ рдЙрд╕рдХреЛ рдЕрдЧрд╛рдбрд┐
рдЬреЛ рдмрд╛рдВрдЪреНрдЫ рд╕реНрд╡рдЪреНрдЫ рджрд┐рд▓рд▓реЗ рд╕рдмрд▓рд╛рдИ рдмрд╛рдВрдбреА редреж
рдЬрдирдХрднреИрдВ
рдореБрд╕реНрдХрд╛рдирд▓реЗ

Annotated output image with detected Nepali text bounding boxes

Summary

In this way we successfully completed this short project which was of detecting Nepali words.

Find the GOOGLE COLAB HERE. Feel free to star this code on Github.