前言

如果讀者使用過百度等的一些圖像識別的接口,比如百度的細粒度圖像識別接口,應該瞭解這個過程,省略其他的安全方面的考慮。這個接口大體的流程是,我們把圖像上傳到百度的網站上,然後服務器把這些圖像轉換成功矢量數據,最後就是拿這些數據傳給深度學習的預測接口,比如是PaddlePaddle的預測接口,獲取到預測結果,返回給客戶端。這個只是簡單的流程,真實的複雜性遠遠不止這些,但是我們只需要瞭解這些,然後去搭建屬於我們的圖像識別接口。

瞭解Flask

安裝flask很簡單,只要一條命令就可以了:

pip install flask

同時我們也使用到flask_cors,所以我們也要安裝這個庫

pip install flask_cors

創建一個paddle_server.py文件,然後編寫一個簡單的程序,瞭解一些如何使用這個Flask框架,首先導入所需的依賴庫:

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

編寫一個hello_world()函數,使用@app.route('/')是指定訪問的路徑,該函數的返回值是一個字符串Welcome to PaddlePaddle

# 根路徑,返回一個字符串
@app.route('/')
def hello_world():
    return 'Welcome to PaddlePaddle'

然後啓動這個服務,如果是在Ubuntu的話,可能是需要在root下執行這個程序。

if __name__ == '__main__':
    # 啓動服務,並指定端口號
    app.run(port=80)

然後瀏覽器訪問http://127.0.0.1,返回之前寫好的字符串:

Welcome to PaddlePaddle

要預測圖片,上傳圖片是首要的,所以我們來學習如何使用Flask來上傳圖片。

  • secure_filename是爲了能夠正常獲取到上傳文件的文件名
  • /upload指定該函數的訪問地址
  • methods=['POST']指定該路徑只能使用POST方法訪問
  • f = request.files['img']讀取表單名稱爲img的文件
  • f.save(img_path)在指定路徑保存該文件
# 上傳文件
@app.route('/upload', methods=['POST'])
def upload_file():
    f = request.files['img']
    # 設置保存路徑
    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

然後再次啓動服務

if __name__ == '__main__':
    # 啓動服務,並指定端口號
    app.run(port=80)

然後再創建index.html文件,編寫一個表單,指定表單提交的路徑http://127.0.0.1/upload,並設置表單提交數據的格式multipart/form-data,而且支持表單提交方式是POST。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>預測圖像</title>
</head>
<body>
<!--上傳圖片的表單-->
<form action="http://127.0.0.1/upload" enctype="multipart/form-data" method="post">
    選擇上傳的圖像:<input type="file" name="img"><br>
    <input type="submit" value="上傳">
</form>
</body>
</html>

打開我們編寫的網頁,選擇需要上傳的圖片,點擊上傳。成功後返回類似以下的字符串,圖片會保存在images目錄下。

success, save path: images/65d7661a-3892-11e9-a7b7-f44d30185f58jpg

預測服務

paddle_server.py中添加一個圖片文件預處理函數,這個函數的參數是根據已經上傳並保存到服務器上圖片的路徑,根據處理對圖片進行預處理,並返回處理否的圖片數據:

# 預處理圖片
def load_image(file):
    img = Image.open(file)
    # 統一圖像大小
    img = img.resize((224, 224), Image.ANTIALIAS)
    # 轉換成numpy值
    img = np.array(img).astype(np.float32)
    # 轉換成CHW
    img = img.transpose((2, 0, 1))
    # 轉換成BGR
    img = img[(2, 1, 0), :, :] / 255.0
    img = np.expand_dims(img, axis=0)
    return img

以下就是PaddlePaddle代碼,這次我們使用《PaddlePaddle從入門到煉丹》十一——自定義圖像數據集識別訓練保存的預測代碼。

# 創建執行器
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

# 保存預測模型路徑
save_path = 'infer_model/'
# 從模型中獲取預測程序、輸入數據名稱列表、分類器
[infer_program, feeded_var_names, target_var] = fluid.io.load_inference_model(dirname=save_path, executor=exe)

以下就是這個後端服務項目的核心代碼,該函數主要的功能是接受上傳的圖片,對圖片進行預測,最後返回預測的結果。預測的方式和之前的圖片預測的方式一樣的。

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

    # 保存圖片
    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)

    # 開始預測圖片
    img = load_image(img_path)
    result = exe.run(program=infer_program,
                     feed={feeded_var_names[0]: img},
                     fetch_list=target_var)

    # 顯示圖片並輸出結果最大的label
    lab = np.argsort(result)[0][0][-1]

    names = ['蘋果', '哈密瓜', '胡蘿蔔', '櫻桃', '黃瓜', '西瓜']

    # 打印和返回預測結果
    r = '{"label":%d, "name":"%s", "possibility":%f}' % (lab, names[lab], result[0][0][lab])
    print(r)
    return r

啓動服務

if __name__ == '__main__':
    # 啓動服務,並指定端口號
    app.run(port=80)

index.html文件增加一個表單,這個是訪問的地址就是上面服務中的預測函數的訪問路徑:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>預測圖像</title>
</head>
<body>
<!--調用服務器預測接口的表單-->
<form action="http://127.0.0.1/infer" enctype="multipart/form-data" method="post">
    選擇預測的圖像:<input type="file" name="img"><br>
    <input type="submit" value="預測">
</form>
</body>
</html>

打開我們剛纔編寫的網頁,選擇需要預測的圖片,點擊預測。成功後返回類似以下的字符串,識別類別的標籤,名字,還有該類別的概率。

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

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


上一章:《PaddlePaddle從入門到煉丹》十三——自定義圖像數生成
下一章:《PaddlePaddle從入門到煉丹》十五——把預測模型部署到Android手機上


參考資料

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