Yolo App - Flask Web Application
- Prepare your Images and get Data
- Train your Tensorflow Model
- Use your Model to do Predictions
- Use Tesseract to Read Number Plates
- Flask Web Application
- Yolo v5 - Data Prep
Setting Up Flask
Hello World
Install Flask using PIP:
pip install flask
And create a simple Flask app:
from flask import Flask
# Create flask app
app = Flask(__name__)
# Add app routes
@app.route('/')
# Create server response
def index():
return "Hi"
if __name__ == "__main__":
app.run()
Run the app from your console:
python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Verify that the app is running:
curl http://127.0.0.1:5000/
Hi
Rendering HTML Templates
mkdir templates
nano layout.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>Numberplate OCR App</title>
</head>
<body>
<header>
<h1>Numberplate Reader</h1>
</header>
<main></main>
<footer></footer>
</body>
</html>
Add an import for render_template
and a route for your template HTML:
from flask import Flask, render_template
...
@app.route('/app')
def application():
return render_template('layout.html')
...
Restart the web app and verify that the HTML is served by Flask:
curl http://127.0.0.1:5000/app
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>Numberplate OCR</title>
</head>
<body>
<header>
<h1>Numberplate Reader</h1>
</header>
<main></main>
<footer></footer>
</body>
</html>
Template Inheritance
I now want to use this layout file as a parent component for my later HTML content. I will create another HTML file inside the templates
folder that should be loaded as a block into the layout page - we can add this to the layout using the JinJa templating engine:
<main>
{% block body %}
{% endblock %}
</main>
In my child page I can now extend this body
section of the layout:
{% extends 'layout.html' %}
{% block body %}
<div class="container is-fluid">
<h1>Body Content</h1>
</div>
{% endblock %}
In our app we now have to call the child component that is extending the layout instead of the layout itself:
@app.route('/')
def application():
return render_template('index.html')
Create an Image File Upload
To upload a file to our Flask server I need to add a form element that allows me to choose a file, assigned a file name of image_name
, and a submit button that uses the POST method to submit this file:
<form action="#" method="POST" enctype="multipart/form-data">
<input class="file-input" type="file" name="image_name" required>
<a class="button is-info" type="submit" value="Upload">
Submit
</a>
</form>
Now I need a handler for the POST method in the app route that takes the file and saves it inside an upload directory:
BASE_PATH = os.getcwd()
UPLOAD_PATH = os.path.join(BASE_PATH, 'static/upload')
@app.route('/', methods=['GET', 'POST'])
def application():
if request.method == 'POST':
upload_file = request.file['image_name']
filename = upload_file.filename
path_save = os.path.join(UPLOAD_PATH, filename)
upload_file.save(path_save)
return render_template('index.html')
return render_template('index.html')
Number Plate Detection
Integrating the Tensorflow Model
model = tf.keras,models.load_model('./static/models/number_plate_detection.h5')
def plate_detection(path, filename):
# Read image
image = load_img(path) # PIL object
image = np.array(image,dtype=np.uint8) # 8 bit array (0,255)
image1 = load_img(path,target_size=(224,224))
# Data preprocessing
image_arr_224 = img_to_array(image1)/255.0 # convert into array and get the normalized output
h,w,d = image.shape
test_arr = image_arr_224.reshape(1,224,224,3)
# Make predictions
coords = model.predict(test_arr)
# De-normalize the values
denorm = np.array([w,w,h,h])
coords = (coords * denorm).astype(np.int32)
# Draw bounding on top the image
xmin, xmax,ymin,ymax = coords[0]
pt1 =(xmin,ymin)
pt2 =(xmax,ymax)
print(pt1, pt2)
cv2.rectangle(image,pt1,pt2,(0,255,0),3)
# Convert into BGR
image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# Save to prediction folder
cv2.imwrite('./static/predictions/{}'.format(filename), image_bgr)
return coords
Integrating Tesseract OCR
def OCR(path, filename):
# Read image
img = np.array(load_img(path))
# Run plate detection
coords = plate_detection(path, filename)
# Extract bounding box coordinates
xmin ,xmax,ymin,ymax = cods[0]
# Define bounding box
roi = img[ymin:ymax,xmin:xmax]
# Convert into BGR
roi_bgr = cv2.cvtColor(roi, cv2.COLOR_RGB2BGR)
# Turn grayscale
gray_roi = cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2GRAY)
gray_roi = cv2.bitwise_not(gray_roi)
# threshold the image, setting all foreground pixels to
# 255 and all background pixels to 0 (invert)
thresh_roi = cv2.threshold(gray_roi, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# Save cut outs to roi folder
#cv2.imwrite('./static/roi/{}'.format(filename), roi_bgr)
cv2.imwrite('./static/roi/{}'.format(filename), thresh_roi)
# OCR the ROI using Tesseract
text_roi = pt.image_to_string(roi_bgr)
print('Original:',text_roi)
text_thresh = pt.image_to_string(thresh_roi)
print('Threshold:',text_thresh)
return text_roi, text_thresh
Use Detection in Flask
Now I can import the OCR function and call it from my app route:
from tf_detection import OCR
@app.route('/', methods=['GET', 'POST'])
def application():
if request.method == 'POST':
# Take uploaded image
upload_file = request.files['image_name']
filename = str(nowTime) + '_' + upload_file.filename
path_save = os.path.join(UPLOAD_PATH, filename)
# Store image in upload directory
upload_file.save(path_save)
# Take image and perform OCR
text_roi, text_thresh = OCR(path_save, filename)
print(text_roi + '\n' + text_thresh)
return render_template('index.html', upload = True, upload_image = filename)
return render_template('index.html', upload = False)
Display Results on Page
In the IF statement above I am setting upload = True
if an upload was processed and upload_image
is defined. I can now use this variable in the Jinja Template to display the results on my index page.
{% if upload %}
<div class="container">
<br/><br/>
<table>
<tr>
<td>
<img class="float-left img-fluid" src="/static/upload/{{ upload_image }}" alt="Source Image" />
</td>
<td>
<img class="float-right img-fluid" src="/static/predictions/{{ upload_image }}" alt="Prediction Image" />
</td>
</tr>
</table>
<table>
<tr>
<th>Region of Interest</th>
<th>Detected Text</th>
<th>Region of Interest (Threshold)</th>
<th>Detected Text</th>
</tr>
<tr>
<td>
<img class="float-left img-fluid" src="/static/roi/{{ upload_image + '_roi'}} " alt="Region of Interest" />
</td>
<td>
<h3>{{ ocr_roi }}</h3>
</td>
<td>
<img class="float-left img-fluid" src="/static/roi/{{ upload_image + '_threshroi'}} " alt="Region of Interest" />
</td>
<td>
<h3>{{ ocr_thresh }}</h3>
</td>
</tr>
</table>
</div>
{% endif %}