Skip to main content

Yolo App - Flask Web Application

Shenzhen, China

  1. Prepare your Images and get Data
  2. Train your Tensorflow Model
  3. Use your Model to do Predictions
  4. Use Tesseract to Read Number Plates
  5. Flask Web Application
  6. 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)

Plate Detection Flask App

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 %}

Plate Detection Flask App