Implementing Optical Character Recognition (OCR) using pytesseract | by Nurali Uzakaliyev | Towards Data Science

Greetings fellow python enthusiasts, I would like to share with you a simple, but very effective OCR service, using pytesseract and with a web interface via Flask.
Optical Character Recognition (OCR) can be useful for a variety of purposes, such as credit card scan for payment purposes, or converting .jpeg scan of a document to .pdf
Without selling out the idea of usage OCR, or how it can be used, let’s begin the coding process!
We’re going to use pytesseract (for OCR) & Flask (for a web interface)
pip install pytesseractfrom PIL import Imageimport pytesseract
Note: if you’re facing some problems with importing pytesseract, you may need to download & install pytesseract.exe, which can be found here.
After the installation, you have to include the path to pytesseract executables, which can be done with a single line of code:
pytesseract.pytesseract.tesseract_cmd = r'YOUR-PATH-TO-TESSERACT\tesseract.exe'
Since we have installed & imported pytesseract, let’s create the core function and check if it works as intended:
def ocr_core(filename):    text = pytesseract.image_to_string(Image.open(filename))    return text
Quite simple, right? The ocr_core function takes a path to your image file and returns its text components. According to documentation, image_to_string function uses English language as default recognition, but we’ll talk about specific languages later.
Here are the results:
Once we made sure everything is working, time to create a web interface for our OCR service
Now we’re going to create a simple html form, allowing user to upload an image file, and retrieve the text from it.
from flask import Flask, render_template, requestapp = Flask(__name__)# from ocr_core import ocr_core# uncomment the line above, if your Flask fails to get access to your function, or your OCR & Flask are on different scripts
Keeping it simple, lets create some back-end tests for uploaded files, such as allowed extensions:
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg']def allowed_file(filename):    return '.' in filename and \           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
Keeping it beautiful and simple, as ‘The Zen of Python’ suggests, right?
Now, lets create a path where our user uploaded images will be held:
import ospath = os.getcwd()UPLOAD_FOLDER = os.path.join(path, 'uploads\\')if not os.path.isdir(UPLOAD_FOLDER):    os.mkdir(UPLOAD_FOLDER)app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
In case you’re wondering why would we import os: we’re gonna need it to get the path for the uploaded images to be held (for back-end), and also to create a source for our <img> tag, once the user successfully uploads the file. If you want to specifically indicate a folder, just change the os.getcwd() to any path you would like to use.
If you never created the ‘uploads’ folder, the code above also creates it for you (with love).
Now, lets build a simple form, and name the file upload.html
<!DOCTYPE html><html><head> <title>OCR</title></head><body> {% if msg %} <h1 style="color: green"> {{ msg }} </h1> {% endif %}<h1> Upload your file </h1><form method=post enctype=multipart/form-data>   <input type="file" name=file>  <input type="submit" name=Upload value="Upload"> </form>{% if extracted %} <h1> Results: </h1> {% endif %}{% if img_src %} <img src="{{ img_src }}"> {% endif %}{% if extracted %} <p> {{ extracted }} </p> {% endif %}</body></html>
Hold up, why are there curly brackets? Well, it’s not fully HTML form, I lied. Actually, it’s jinja formatting, which allows us to format our HTML accordingly. Nothing special here, {% if %} stands for if statement, so if there is any message, it will be wrapped in <h1> tag, etc.
The form is pretty simple, but don’t forget enctype=multipart/form-data to allow the form to accept file uploads.
All the variables inside the curly brackets appear once the back-end gets into the action, so just before uploading anything, your upload.html should look like this:
Critical note: don’t forget to save your ‘upload.html’ file to the same path as your main python script is, regardless you’re working on the hub or local computer. If you have no idea where your working directory is, refer to os.getcwd() above.
Once we have completed all the preparations above, time to create a web interface via Flask:
@app.route('/', methods = ['GET', 'POST'])def upload_page():    if request.method == 'POST':        if 'file' not in request.files:            return render_template('upload.html', msg = 'No file selected')        file = request.files['file']if file.filename == '':            return render_template('upload.html', msg = 'No file')if file and allowed_file(file.filename):            file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename))            extracted = ocr_core(file)            return render_template('upload.html',                                     msg = 'OCR completed',                                    extracted = extracted,                                     img_src = UPLOAD_FOLDER + file.filename)    else:        return render_template('upload.html')if __name__ == '__main__':    app.run()
To begin with, we check the request method to be POST, mostly for security measures. Then, we have to check whether files are selected and uploaded (in other words, did the files reach the back-end). Once the entry-level tests are done, we check if the file exists and if its format is allowed.
Once all these conditions are met, we save the uploaded file to our upload folder and assign it to a variable called extracted
Once you run the code, you should see the address where the app is running:
So before uploading, your interface should look like this:
And once you select your file, it should slightly change like this:
Voila! You just have created an OCR web service!
As was promised, we have created a simple, but very effective OCR web service, which allows recognizing text from an image. But there are many ways how to improve it. For instance, you could add options to select which language the OCR should use. both in back-end and front-end like this:
def ocr_core(filename):     text = pytesseract.image_to_string(Image.open(filename), lang=selected_language)    return text
All you have to do is specify the lang property in ocr_core function. And add a <select> tag in your upload.html file. Just don’t forget to fetch the data from the front-end and pass it to your ocr_core function
Hopefully, this article proved to be useful for you and made some things more clear. Thank you for your time, if you have any feedback or any questions, please be free to leave a comment below.