Foreword

If readers have used image recognition APIs from Baidu, such as Baidu’s fine-grained image recognition interface, they should be familiar with the process. Here, we’ll omit considerations for security and other aspects. The general process of this interface is: we upload images to Baidu’s server, the server converts these images into vector data, and finally, this data is passed to a deep learning prediction interface (e.g., PaddlePaddle’s prediction interface) to obtain the prediction result, which is then returned to the client. This is just a simplified flow; the actual complexity is far greater, but we only need to understand this to build our own image recognition interface.

Understanding Flask

Installing Flask is straightforward with a single command:

pip install flask

We also use flask_cors, so install this library as well:

pip install flask_cors

Create a paddle_server.py file and write a simple program to learn how to use the Flask framework. First, import the required libraries:

import os
import uuid
import numpy as np
import paddle.fluid as fluid
from PIL import Image
from flask import Flask, request
from flask_cors import CORS
from werkzeug.utils import secure_filename

Write a hello_world() function. Use @app.route('/') to specify the access path, and return the string Welcome to PaddlePaddle:

# Root path, returns a string
@app.route('/')
def hello_world():
    return 'Welcome to PaddlePaddle'

Start the service. On Ubuntu, you may need to run the program as root:

if __name__ == '__main__':
    # Start the service and specify the port number
    app.run(port=80)

Access http://127.0.0.1 in your browser, and you’ll see the previously defined string:

Welcome to PaddlePaddle

To predict images, uploading images is essential. Let’s learn how to use Flask to upload images:
- secure_filename ensures the uploaded file name is correctly retrieved.
- /upload specifies the access address for this function.
- methods=['POST'] restricts this path to POST requests only.
- f = request.files['img'] reads the file with the form name “img”.
- f.save(img_path) saves the file to the specified path.

# Upload file
@app.route('/upload', methods=['POST'])
def upload_file():
    f = request.files['img']
    # Set the save path
    save_father_path = 'images'
    img_path = os.path.join(save_father_path, str(uuid.uuid1()) + secure_filename(f.filename).split('.')[-1])
    if not os.path.exists(save_father_path):
        os.makedirs(save_father_path)
    f.save(img_path)
    return 'success, save path: ' + img_path

Start the service again:

if __name__ == '__main__':
    # Start the service and specify the port number
    app.run(port=80)

Create an index.html file and write a form to submit to the upload path http://127.0.0.1/upload with enctype="multipart/form-data" and method="post":

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Image Prediction</title>
</head>
<body>
<!--Form to upload images-->
<form action="http://127.0.0.1/upload" enctype="multipart/form-data" method="post">
    Select an image to upload: <input type="file" name="img"><br>
    <input type="submit" value="Upload">
</form>
</body>
</html>

Open the webpage, select an image, and click “Upload”. A success message will appear with the saved path, e.g.:

success, save path: images/65d7661a-3892-11e9-a7b7-f44d30185f58.jpg

Prediction Service

Add an image preprocessing function to paddle_server.py. This function takes the saved image path, preprocesses the image, and returns the processed data:

# Preprocess the image
def load_image(file):
    img = Image.open(file)
    # Resize the image to a uniform size
    img = img.resize((224, 224), Image.ANTIALIAS)
    # Convert to numpy array
    img = np.array(img).astype(np.float32)
    # Convert to CHW format
    img = img.transpose((2, 0, 1))
    # Convert to BGR and normalize
    img = img[(2, 1, 0), :, :] / 255.0
    img = np.expand_dims(img, axis=0)
    return img

The following is the PaddlePaddle code using the prediction code from the article PaddlePaddle From Beginner to Expert - Custom Image Dataset Recognition:

# Create an executor
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

# Path to save the prediction model
save_path = 'infer_model/'
# Load inference program, input variable names, and target variables from the model
[infer_program, feeded_var_names, target_var] = fluid.io.load_inference_model(dirname=save_path, executor=exe)

The core code of this backend service accepts uploaded images, performs prediction, and returns results:

@app.route('/infer', methods=['POST'])
def infer():
    f = request.files['img']

    # Save the uploaded image
    save_father_path = 'images'
    img_path = os.path.join(save_father_path, str(uuid.uuid1()) + '.' + secure_filename(f.filename).split('.')[-1])
    if not os.path.exists(save_father_path):
        os.makedirs(save_father_path)
    f.save(img_path)

    # Start image prediction
    img = load_image(img_path)
    result = exe.run(
        program=infer_program,
        feed={feeded_var_names[0]: img},
        fetch_list=target_var
    )

    # Get the label with the highest probability
    lab = np.argsort(result)[0][0][-1]

    # Define category names
    names = ['苹果', '哈密瓜', '胡萝卜', '樱桃', '黄瓜', '西瓜']

    # Format and return the result
    r = '{"label":%d, "name":"%s", "possibility":%f}' % (lab, names[lab], result[0][0][lab])
    print(r)
    return r

Start the service again:

if __name__ == '__main__':
    # Start the service and specify the port number
    app.run(port=80)

Update index.html to add a form for the prediction interface:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Image Prediction</title>
</head>
<body>
<!--Form to call the server's prediction interface-->
<form action="http://127.0.0.1/infer" enctype="multipart/form-data" method="post">
    Select an image to predict: <input type="file" name="img"><br>
    <input type="submit" value="Predict">
</form>
</body>
</html>

After uploading and clicking “Predict”, the server returns a JSON string with the label, name, and probability, e.g.:

{"label":1, "name":"哈密瓜", "possibility":0.982786}

GitHub Repository

GitHub Repository: https://github.com/yeyupiaoling/LearnPaddle2/tree/master/note14


Previous Chapter: PaddlePaddle From Beginner to Expert - Thirteen - Custom Image Data Generation
Next Chapter: PaddlePaddle From Beginner to Expert - Fifteen - Deploying Prediction Models to Android Phones


References

  1. https://blog.csdn.net/qq_33200967/article/details/79571511
  2. http://blog.csdn.net/u011054333/article/details/70151857
Xiaoye