Flask快速上手及目标检测服务端接口示例

最简单的Flask应用示例

创建一个flask实例,使用route构建路由,app.run开启服务

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
if __name__ == '__main__':
    app.run()

 

Flask相关知识点提要

路由

使用装饰器把URL映射到视图函数

@app.route('/')
def index():#视图函数
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello, World'

可以在URL中添加变量,变量会作为关键字参数传递给函数。使用<converter:variable_name>

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

URL构建

使用url_for()函数用于构建指定函数的URL。函数名是第一个参数。

没把URL写死在模板中,使用url_for()动态构建的好处:

描述性更好,直观,避免相对路径

from flask import Flask, escape, url_for

app = Flask(__name__)

@app.route('/')
def index():
    return 'index'

@app.route('/login')
def login():
    return 'login'

@app.route('/user/<username>')
def profile(username):
    return '{}\'s profile'.format(escape(username))

with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))

渲染模板

利用Jinja2模板引擎,使用render_template()方法渲染模板,只需要提供模板名称和参数变量即可。模板统一放在template文件夹里面。

 Jinjia2模板语法:

if语句:

{ % if 条件 %}
  语句1
{ % else %}
  语句2
{ % end if %}

 for循环:

{ % for 变量 in 容器 %}
  语句1
{ % end for %}

 注释:

{ # 注释 #}

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

操作请求数据

客户端网页向服务器传送的数据通过request对象来处理。在视图函数中直接使用可以取到当前本次请求。

通过Method属性可以操作当前请求方法,通过使用form属性处理表单数据,使用file处理文件数据。

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'], #请求的form对象
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    return render_template('login.html', error=error)
@app.route("/upload",methods = ['POST', 'GET'])
def upload():
    if request.method == "POST":
        f = request.files['file'] #请求文件
        basepath = os.path.dirname(__file__)
        f.save(basepath )

处理文件的时候需要在你的 HTML 表单中设置 enctype="multipart/form-data" 属性。通过files属性访问上传的文件,文件有一个保存的方法即save,可以保存到服务器。

重定向

使用redirect()进行重定向

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

消息闪现

客户端进行操作后,可以使用flash来闪现一个消息。

服务器代码中使用flash("消息“),html中使用get_flashed_messages() 来操作消息

数据库交互操作

使用Flask_SQLAlchemy进行对数据库的抽象,不用直接写SQL语句,使用python对象来操作数据库。

①配置数据库:

# 数据库链接地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:密码@127.0.0.1:3306/test'
# 动态追踪修改设置,如未设置只会提示警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

②终端创建数据库:

create database test charset utf8;

③创建对象,添加模型,添加数据

db = SQLAlchemy(app)
#定义模型类即数据库的一个表,模型要继承自db.Model
class Authors(db.Model):
    __tablename__ = 'authors' #表名
    id = db.Column(db.Integer,primary_key=True) #表的属性字段
    name = db.Column(db.String(64),unique=True)
    #关系,使用relationship描述两个属性的关系
    books = db.relationship('Books',backref = 'Authors')

class Books(db.Model):
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)

    author_id = db.Column(db.Integer, db.ForeignKey('authors.id')) #外键

表之间的关系使用realtionship描述,例如上例Authors与Books的关系,realtionship中第一个参数为对应参照的类,

第二个参数backref为反向引用属性。可以使用author.books来调用。

使用Flask-SQLAlchemy时,插入、修改、删除操作,均由数据库会话管理。

会话即db.session。常用的操作如下:

db.session.add(obj) #添加对象
db.session.add_all([obj1,obj2,..]) #添加多个对象
db.session.delete(obj) #删除对象
db.session.commit() #提交会话
db.session.rollback() #回滚
db.session.remove() #移除会话
#添加示例,首先创建所有与db相关联的表
db.create_all() au1
= Authors(name='张三') au2 = Authors(name='李四') db.session.add_all([au1, au2]) db.session.commit() bk1 = Books(name='linux', author_id=au1.id) bk2 = Books(name='python', author_id=au1.id) db.session.add_all([bk1, bk2]) db.session.commit()

④查询数据:

#filter,需要写模型.参数 == 判断
User.query.filter(User.name=='wang').all()
#filter_by,直接写对应参数 = 判断
User.query.filter_by(name='wang').all()
#get,使用主键作为参数
User.get(id)

综合示例1

图书管理系统,实现简单的显示增删查

服务端代码:

from flask import Flask, render_template, redirect, url_for, flash, request
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired


app = Flask(__name__)
#配置数据库 app.config[
'SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:878698@localhost:3306/flask_books' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False CSRFProtect(app) app.config['SECRET_KEY'] = "jfkdjfkdkjf" #初始化数据库对象 db = SQLAlchemy(app) #定义模型类,定义表名、字段、关系 class Authors(db.Model): __tablename__ = 'authors' id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(64),unique=True) books = db.relationship('Books',backref = 'Authors') class Books(db.Model): __tablename__ = 'books' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, index=True) author_id = db.Column(db.Integer, db.ForeignKey('authors.id')) class Authorform(FlaskForm): author = StringField('作者',validators = [DataRequired()]) book = StringField('书名',validators = [DataRequired()]) submit = SubmitField('提交') #使用wtf表单显示在网页 @app.route('/') def view(): authors = Authors.query.all() author_form = Authorform() return render_template('index.html',authors=authors,form = author_form) #从表单位置增加数据 @app.route('/add_book',methods=['POST']) def add_book(): author_form = Authorform() author_name = author_form.author.data book_name = author_form.book.data # 校验参数 if not all([author_name,book_name]): return "作者或者书籍为空" author = Authors.query.filter_by(name = author_name).first() if author: book = Books.query.filter_by(name = book_name).first() if book: flash("已存在该书籍") else: bk = Books(name = book_name,author_id=author.id) db.session.add(bk) db.session.commit() else: at = Authors(name = author_name) db.session.add(at) db.session.commit() bk = Books(name=book_name, author_id=at.id) db.session.add(bk) db.session.commit() return redirect(url_for('view')) @app.route('/delete_book/<int:book_id>') def delete_book(book_id): book = Books.query.get(book_id) db.session.delete(book) db.session.commit() return redirect(url_for('view')) #重定向到view视图函数显示所有数据 @app.route('/delete_author/<int:author_id>') def delete_author(author_id): author = Authors.query.get(author_id) for bk in author.books: db.session.delete(bk) db.session.delete(author) db.session.commit() return redirect(url_for('view')) db.drop_all() db.create_all() au1 = Authors(name='李**') au2 = Authors(name='张**') db.session.add_all([au1, au2]) db.session.commit() bk1 = Books(name='linux', author_id=au1.id) bk2 = Books(name='python', author_id=au1.id) bk3 = Books(name='c++', author_id=au2.id) db.session.add_all([bk1, bk2, bk3]) db.session.commit() if __name__ == '__main__': app.run(debug=True)

HTML文件(index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>book</title>
</head>
<body>
    <h2>添加</h2>
    <form  action='/add_book' method="post">
        {{ form.csrf_token() }}
        {{ form.author.label }}{{ form.author }}<br>
        {{ form.book.label }}{{ form.book }}<br>
        {{ form.submit }}<br>
        {% for message in get_flashed_messages() %}
            <span style="color: red">{{ message }}</span>
        {% endfor %}
    </form>
    <h2>书籍展示</h2>
    <ul>

        {% for author in authors %}
            <li>作者:{{ author.name }}<a href="{{ url_for('delete_author',author_id=author.id) }}">删除</a></li><br>
            <ul>
                {% for book in author.books %}
                    <li>书名:{{ book.name }} <a href="{{ url_for('delete_book',book_id=book.id) }}">删除</a></li><br>
                {% endfor %}
            </ul>

        {% endfor %}

    </ul>
</body>
</html>

 

综合示例2

图像分类服务端接口,接收图片文件送入模型识别

from flask import Flask, request
from werkzeug.utils import secure_filename
import uuid
from PIL import Image
import os
import torch
from torchvision.models import resnet18
from torchvision.transforms import ToTensor

app = Flask(__name__)
net = resnet18(pretrained=True)
net.eval()

@app.route("/",methods=["GET"])
def show():
    return "classifier api"

@app.route("/run",methods = ["GET","POST"])
def run():
    file = request.files['file'] #通过request对象获取文件
    base_path = os.path.dirname(__file__)
    if not os.path.exists(os.path.join(base_path, "temp")):
        os.makedirs(os.path.join(base_path, "temp"))
    file_name = uuid.uuid4().hex
    upload_path = os.path.join(base_path, "temp", file_name)
    file.save(upload_path) #保存到服务器端

    img = Image.open(upload_path)
    img_tensor = ToTensor()(img).unsqueeze(0)
    out = net(img_tensor)
    pred = torch.argmax(out,dim = 1)
return "result_cls : {}".format(pred)

if __name__ == "__main__":
    app.run(host="0.0.0.0",port=5555,debug=True)

另一种接收base64格式转存图片的方式

from flask import request, Flask
import base64
import cv2
import numpy as np
app = Flask(__name__)
@app.route("/", methods=['POST','GET'])
def get_frame():
    # base64编码转为图片并保存
    # 传输的base64编码为转换后整个值中data:image/jpg;base64,后的值
    img = base64.b64decode(str(request.form['image']).split('base64,')[-1])
    image_data = np.fromstring(img, np.uint8)
    image_data = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
    cv2.imwrite('base64_2_img_01.png', image_data)
    print(image_data)

    return 'koukou'
if __name__ == "__main__":
    app.run("0.0.0.0", port=5005)

 

综合示例3

图片文件上传,提供目标检测算法接口及可视化界面

服务端代码:

from flask import Flask, render_template, request, redirect, url_for, make_response, jsonify
from werkzeug.utils import secure_filename
import os,time
from datetime import timedelta
from main_demo import detect_cv2
ALLOWED_EXTENSIONS = set(["png","jpg","JPG","PNG", "bmp"])

def is_allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

app = Flask(__name__)

# 静态文件缓存过期时间
app.send_file_max_age_default = timedelta(seconds=1)

@app.route("/upload",methods = ['POST', 'GET'])
def upload():
    if request.method == "POST":
        f = request.files['file']
        if not ( f and is_allowed_file(f.filename)):
            return jsonify({
                "error":1001, "msg":"请检查上传的图片类型,仅限于png、PNG、jpg、JPG、bmp"
            })
        basepath = os.path.dirname(__file__)
        upload_path = os.path.join(basepath, "static/images",secure_filename(f.filename))
        f.save(upload_path)

        #config
        cfg_file = 'pytorch-YOLOv4-master/cfg/yolov4.cfg'
        weightfile = 'pytorch-YOLOv4-master/checkpoints/yolov4.pth'
        imgfile = upload_path
        detected_path = os.path.join(basepath, "static/images", "output" + secure_filename(f.filename))

        #run & save
        detect_cv2(cfg_file, weightfile, imgfile, detected_path)
        # run(upload_path, conf, detected_path)

        # return render_template("upload_ok.html", userinput = user_input, val1=time.time(), path = detected_path)
        path = "./images/" + "output" + secure_filename(f.filename)
        return render_template("upload_ok.html", path = path, val1 = time.time()) #渲染模板,显示图片检测结果
    return render_template("upload.html")


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5555, debug=True)

HTML文件1(upload.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask上传图片演示</title>
</head>
<body>
    <h1>使用Flask上传本地图片</h1>
    <form action="" enctype='multipart/form-data' method='POST'>
        <input type="file" name="file" style="margin-top:20px;"/>
        <br>
        <input type="submit" value="上传" class="button-new" style="margin-top:15px;"/>
    </form>

</body>
</html>

 

HTML文件2(upload_ok.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask上传图片演示</title>
</head>
<body>
    <h1>使用Flask上传本地图片</h1>
    <form action="" enctype='multipart/form-data' method='POST'>
        <input type="file" name="file" style="margin-top:20px;"/>
        <br>
        <input type="submit" value="上传" class="button-new" style="margin-top:15px;"/>
    </form>
    <img src="{{ url_for('static', filename= path,_t=val1) }}" width="400" height="400" alt="图片识别失败"/>
</body>
</html>

 

 参考:

官方文档

黑马程序员教学

深度学习部署案例

posted @ 2021-01-21 17:45  you-wh  阅读(1072)  评论(0编辑  收藏  举报
Fork me on GitHub