Sitemap
Quantrium.ai

This is Quantrium’s official tech blog. A blog on how technology enables us to develop great software applications for our clients.

QUANTRIUM GUIDES

Extracting text from an image using docTR OCR

Converting coordinate systems for OCR models

7 min readMay 30, 2022

--

Introduction

In one of my previous articles, I explained how to perform detection and recognition of text separately in an image/document using multiple OCR models. The article is found here:-

I used easyOCR for text detection and tested the recognition of text using easyOCR, PPOCR, pytesseract, and MMOCR Robust Scanner models. One of the major benefits that could be found in the above-mentioned OCR models is that they follow a universal coordinate system (which I would like to call the graphical coordinate system). But that’s not the case all the time.

Some OCR models follow a different coordinate system. In such cases, we need to convert it to a universal coordinate system to use the coordinates in a different recognition model. One such model is the docTR OCR model. It is based on Tensorflow 2 and Pytorch and is very useful for extracting data from document/image files. It has pre-trained detection and recognition models and also provides support to build custom models based on the application or dataset used.

In this article, I have explained how to perform text detection and recognition with docTR OCR using pre-trained models, following which I have explained the coordinate system followed by docTR OCR to detect text blocks. Then, I describe the procedure to convert the coordinate system from the docTR OCR geometry system to the graphical coordinate system, which could then be used to perform recognition on a different OCR recognition model if needed. So, let’s get started!

Performing text detection and recognition using docTR OCR

To demonstrate the OCR process using docTR OCR, I have used the following image document which I have generated manually for this purpose (in fact, I had used a similar image document in my previous article too):-

Image document used for testing docTR OCR model

First, let’s install the required packages:

!pip install python-doctr
!pip install "python-doctr[tf]"
!pip install "python-doctr[torch]"

Next, import the packages and define the model:

from doctr.io import DocumentFile
from doctr.models import ocr_predictor
model = ocr_predictor(det_arch = 'db_resnet50',
reco_arch = 'crnn_vgg16_bn',
pretrained = True
)

However, there are different detection and recognition architectures and you can find the detailed information in the docTR OCR documentation.

Here is the link to the docTR OCR repository.

Detection and recognition using docTR OCR

The following section guides you through the steps to perform detection and recognition using docTR OCR.

img_path = "path/image.jpg" #Specify your image path here
img = DocumentFile.from_images(img_path)
result = model(img)
output = result.export()

The variable output contains the output in JSON format. However, the JSON output obtained after execution is too big. Hence I have stored it in a JSON file using the below code:

import json
with open("path/docTR_OCR_output.json", "w") as f:
f.write(json.dumps(output, indent=1))
f.close()

The JSON output can be found here. As you can see, the output format is completely different when compared to the other OCR models. In the upcoming parts, I have shown the coordinates of text in the docTR OCR system along with the corresponding text recognized.

Get Bhargav Sridhar’s stories in your inbox

Join Medium for free to get updates from this writer.

Now let’s view the output.

result.show(img)
Detected text blocks obtained from docTR OCR output

In this case, we have perfect text detection with all text blocks detected, and we can notice that each block is detected separately. Now, let us check the text coordinates and the corresponding text recognition.

for obj1 in output['pages'][0]["blocks"]:
for obj2 in obj1["lines"]:
for obj3 in obj2["words"]:
print("{}: {}".format(obj3["geometry"],obj3["value"]))

Here, we have performed some nested dictionary comprehensions to obtain the coordinates and texts that give the following output.

((0.3359375, 0.013671875), (0.458984375, 0.0419921875)): Sample ((0.466796875, 0.0126953125), (0.5390625, 0.0380859375)): PDF ((0.5478515625, 0.0146484375), (0.716796875, 0.0361328125)): Document 
((0.0205078125, 0.123046875), (0.07421875, 0.14453125)): This ((0.076171875, 0.123046875), (0.1015625, 0.1455078125)): is ((0.1025390625, 0.1259765625), (0.1220703125, 0.14453125)): a ((0.123046875, 0.123046875), (0.2109375, 0.1474609375)): sample ((0.21484375, 0.123046875), (0.26953125, 0.14453125)): PDF ((0.2744140625, 0.125), (0.3896484375, 0.142578125)): document ((0.39453125, 0.125), (0.4814453125, 0.1435546875)): created ((0.484375, 0.1220703125), (0.5224609375, 0.1455078125)): for ((0.5234375, 0.1240234375), (0.6025390625, 0.1484375)): testing ((0.6083984375, 0.125), (0.703125, 0.1435546875)): ditterent ((0.7080078125, 0.123046875), (0.7685546875, 0.14453125)): OCR ((0.7724609375, 0.125), (0.86328125, 0.1435546875)): models. ((0.0205078125, 0.265625), (0.07421875, 0.287109375)): This ((0.076171875, 0.2685546875), (0.126953125, 0.287109375)): was ((0.130859375, 0.267578125), (0.2177734375, 0.2861328125)): created ((0.2216796875, 0.265625), (0.255859375, 0.2919921875)): by ((0.0224609375, 0.353515625), (0.1171875, 0.3720703125)): NAME:- ((0.4873046875, 0.353515625), (0.6142578125, 0.37109375)): BHARGAV ((0.61328125, 0.3515625), (0.638671875, 0.3740234375)): S ((0.021484375, 0.408203125), (0.0966796875, 0.4296875)): OCRs ((0.1005859375, 0.41015625), (0.1806640625, 0.4287109375)): used:- ((0.4853515625, 0.41015625), (0.5634765625, 0.4287109375)): docIR ((0.5673828125, 0.408203125), (0.6298828125, 0.4296875)): OCR ((0.630859375, 0.4072265625), (0.6689453125, 0.431640625)): for ((0.6708984375, 0.4091796875), (0.919921875, 0.4326171875)): detectionarecognition
((0.0234375, 0.4677734375), (0.189453125, 0.48828125)): Programming ((0.1953125, 0.46484375), (0.2587890625, 0.490234375)): lang. ((0.2646484375, 0.466796875), (0.34375, 0.4853515625)): used:- ((0.4853515625, 0.46484375), (0.568359375, 0.4892578125)): Python ((0.021484375, 0.978515625), (0.1044921875, 0.99609375)): Created ((0.10546875, 0.9794921875), (0.14453125, 0.998046875)): on: ((0.220703125, 0.9794921875), (0.3564453125, 0.994140625)): DD/MM/YYYY
((0.79296875, 0.9775390625), (0.849609375, 1.0)): Page
((0.8515625, 0.978515625), (0.9423828125, 0.99609375)): Number: ((0.9638671875, 0.978515625), (0.978515625, 0.99609375)): 1

You can observe that the overall recognition quality is very good (except in a few places, like “different” is extracted as ditterent, “&” as a, docTR as docIR).

Regarding the text coordinates, we can observe that the coordinates are by default normalized/scaled between 0 and 1, which is completely different from the graphical coordinate system.

The format of a graphical coordinate system is as follows:

[x_min, x_max, y_min, y_max]

It can be visualized as follows:

graphical coordinate system

In the following section, let us discuss how to convert these normalized geometries to a graphical coordinate system.

Converting geometry to the graphical coordinate system

Let us create a few methods to perform this operation.

import math
def convert_coordinates(geometry, page_dim):
len_x = page_dim[1]
len_y = page_dim[0]
(x_min, y_min) = geometry[0]
(x_max, y_max) = geometry[1]
x_min = math.floor(x_min * len_x)
x_max = math.ceil(x_max * len_x)
y_min = math.floor(y_min * len_y)
y_max = math.ceil(y_max * len_y)
return [x_min, x_max, y_min, y_max]
def get_coordinates(output):
page_dim = output['pages'][0]["dimensions"]
text_coordinates = []
for obj1 in output['pages'][0]["blocks"]:
for obj2 in obj1["lines"]:
for obj3 in obj2["words"]:
converted_coordinates = convert_coordinates(
obj3["geometry"],page_dim
)
print("{}: {}".format(converted_coordinates,
obj3["value"]
)
)
text_coordinates.append(converted_coordinates)
return text_coordinates

We can obtain the page dimensions from the docTR OCR output using dictionary comprehensions similar to the ones performed previously. Then we used the length and width of the page to convert the coordinates.

By applying floor for min coordinates and ceil for max coordinates, we get the largest possible bounding box from the geometrical coordinates.

Now, let us use the output obtained in the previous section to convert coordinates.

graphical_coordinates = get_coordinates(output)

Since we have included print() statements in the method, the following will be printed.

[208, 286, 10, 32]: Sample 
[289, 335, 9, 29]: PDF
[340, 446, 10, 27]: Document
[12, 47, 91, 108]: This
[47, 64, 91, 109]: is
[63, 76, 93, 108]: a
[76, 131, 91, 110]: sample
[133, 168, 91, 108]: PDF
[170, 242, 92, 106]: document
[245, 299, 92, 107]: created
[300, 325, 90, 109]: for
[325, 375, 92, 111]: testing
[377, 437, 92, 107]: ditterent
[439, 478, 91, 108]: OCR
[479, 537, 92, 107]: models.
[12, 47, 197, 214]: This
[47, 79, 199, 214]: was
[81, 136, 198, 213]: created
[137, 159, 197, 217]: by
[13, 73, 262, 277]: NAME:-
[302, 382, 262, 276]: BHARGAV
[380, 397, 261, 278]: S
[13, 61, 303, 320]: OCRs
[62, 113, 304, 319]: used:-
[301, 350, 304, 319]: docIR
[352, 392, 303, 320]: OCR
[391, 416, 302, 321]: for
[416, 572, 304, 322]: detectionarecognition
[14, 118, 347, 363]: Programming
[121, 161, 345, 365]: lang.
[164, 214, 346, 361]: used:-
[301, 353, 345, 364]: Python
[13, 65, 727, 741]: Created
[65, 90, 727, 742]: on:
[137, 222, 727, 739]: DD/MM/YYYY
[492, 528, 726, 743]: Page
[528, 586, 727, 741]: Number:
[598, 608, 727, 741]: 1

Now, let us plot the graphical_coordinates .

import PIL
from PIL import ImageDraw
import matplotlib.pyplot as plt
def draw_bounds(image, bound):
draw = ImageDraw.Draw(image)
for b in bound:
p0, p1, p2, p3 = [b[0],b[2]], [b[1],b[2]], \
[b[1],b[3]], [b[0],b[3]]
draw.line([*p0,*p1,*p2,*p3,*p0], fill='blue', width=2)
return image
image = PIL.Image.open(img_path)
result_image = draw_bounds(image, graphical_coordinates)
plt.figure(figsize=(15,15))
plt.imshow(result_image)

The following output image was obtained after drawing bounding boxes.

Coordinates obtained in graphical coordinate system plotted

Now this list of coordinates can be used to perform text recognition using a different OCR model.

Conclusion

To sum up, you have seen the procedure to perform text detection and recognition using docTR OCR. For the test image used, we were able to obtain perfect detection results with all the text blocks in the image. This is a best-case scenario and may not be the situation all the time. If text detection results are moderate or less accurate, we can perform image pre-processing techniques using inbuilt Python modules to improve image quality before performing text detection and recognition.

Now you have an idea about the coordinate systems followed by different OCR models and how to convert one system to another using Python. This similar idea can be followed for other coordinate systems too. This would ensure uniformity in the OCR process if multiple models are deployed.

I hope I have given you a good knowledge of these. I would be more than willing to acknowledge questions that can be posted through comments. Thanks!

--

--

Bhargav Sridhar
Bhargav Sridhar

Written by Bhargav Sridhar

PhD Student. Wants to write about Research, Tech, Math, Philosophy, and Life. Striving to make common knowledge and basic skills accessible to everybody.

Quantrium.ai
Quantrium.ai

Published in Quantrium.ai

This is Quantrium’s official tech blog. A blog on how technology enables us to develop great software applications for our clients.