赞
踩
目录
通过搭建flask微型服务器后端,以后通过vue搭建网页前端。flask是第一个第三方库。与其他模块一样,安装时可以直接使用python的pip命令实现。flask是web开发框架,简单易学,因此用flask来搭建web服务也非常简单。
在pycharm新建一个项目,命名为web2020,然后新建一个python文件,命名为main.py。在代码中输入如下代码:
- from flask import Flask #导入Flask类
- app=Flask(__name__) #实例化并命名为app实例
- if __name__=="__main__":
- app.run(port=2020,host="127.0.0.1",debug=True) #调用run方法,设定端口号,启动服务
路由定义:
- from flask import Flask
- app=Flask(__name__)
-
- @app.route('/')
- def index():
- return 'welcome to my webpage!'
-
- if __name__=="__main__":
- app.run(port=2020,host="127.0.0.1",debug=True)
通过这种方式,实现python调用模型,然后通过web服务器进行数据输入输出,最后通过浏览器web页面进行展示。
前端代码结构
后端代码结构
- import datetime
- import logging as rel_log
- import os
- import shutil
- from datetime import timedelta
- from flask import *
- from flask import Flask, render_template, Response
- from processor.AIDetector_pytorch import Detector
-
- import core.main
-
- # import camera driver
- if os.environ.get('CAMERA'):
- Camera = import_module('camera_' + os.environ['CAMERA']).Camera
- else:
- from camera import Camera
-
-
- UPLOAD_FOLDER = r'./uploads'
-
- ALLOWED_EXTENSIONS = set(['png', 'jpg'])
- app = Flask(__name__)
- app.secret_key = 'secret!'
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
-
- werkzeug_logger = rel_log.getLogger('werkzeug')
- werkzeug_logger.setLevel(rel_log.ERROR)
-
- # 解决缓存刷新问题
- app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1)
-
-
- # 添加header解决跨域
- @app.after_request
- def after_request(response):
- response.headers['Access-Control-Allow-Origin'] = '*'
- response.headers['Access-Control-Allow-Credentials'] = 'true'
- response.headers['Access-Control-Allow-Methods'] = 'POST'
- response.headers['Access-Control-Allow-Headers'] = 'Content-Type, X-Requested-With'
- return response
-
- #图片检测接口
- def allowed_file(filename):
- return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
-
-
- #@app.route('/')
- #def hello_world():
- # return redirect(url_for('static', filename='./index.html'))
- @app.route('/')
- def index():
- """Video streaming home page."""
- return render_template('index.html')
-
-
-
- @app.route('/upload', methods=['GET', 'POST'])
- def upload_file():
- file = request.files['file']
- print(datetime.datetime.now(), file.filename)
- #if file and allowed_file(file.filename):
- src_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
- file.save(src_path)
- shutil.copy(src_path, './tmp/ct')
- image_path = os.path.join('./tmp/ct', file.filename)
- pid, image_info = core.main.c_main(
- image_path, current_app.model, file.filename.rsplit('.', 1)[1])
- return jsonify({'status': 1,
- 'image_url': 'http://127.0.0.1:5003/tmp/ct/' + pid,
- 'draw_url': 'http://127.0.0.1:5003/tmp/draw/' + pid,
- 'image_info': image_info})
-
- #return jsonify({'status': 0})
-
-
- @app.route("/download", methods=['GET'])
- def download_file():
- # 需要知道2个参数, 第1个参数是本地目录的path, 第2个参数是文件名(带扩展名)
- return send_from_directory('data', 'testfile.zip', as_attachment=True)
-
-
- # show photo
- @app.route('/tmp/<path:file>', methods=['GET'])
- def show_photo(file):
- if request.method == 'GET':
- if not file is None:
- image_data = open(f'tmp/{file}', "rb").read()
- response = make_response(image_data)
- response.headers['Content-Type'] = 'image/png'
- return response
-
-
- #视频检测接口
- def gen(camera):
- """Video streaming generator function."""
- while True:
- frame = camera.get_frame()
- yield (b'--frame\r\n'
- b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
-
-
- @app.route('/video_start')
- def video_feed():
- """Video streaming route. Put this in the src attribute of an img tag."""
- return Response(gen(Camera()),
- mimetype='multipart/x-mixed-replace; boundary=frame')
-
- #视频流检测接口
- #@app.route('/livestream_start')
-
-
- #程序启动入口
- if __name__=='__main__':
- files = [
- 'uploads', 'tmp/ct', 'tmp/draw',
- 'tmp/image', 'tmp/mask', 'tmp/uploads'
- ]
- for ff in files:
- if not os.path.exists(ff):
- os.makedirs(ff)
- with app.app_context():
- current_app.model = Detector()
- app.run(host='127.0.0.1', port=5003, debug=True)
- <template>
- <el-tabs stretch=true v-model="activeName" type="card" @tab-click="handleClick">
- <el-tab-pane label="图片检测" name="first">
- <div id="Content">
- <el-dialog
- title="AI预测中"
- :visible.sync="dialogTableVisible"
- :show-close="false"
- :close-on-press-escape="false"
- :append-to-body="true"
- :close-on-click-modal="false"
- :center="true"
- >
- <el-progress :percentage="percentage"></el-progress>
- <span slot="footer" class="dialog-footer">请耐心等待约3秒钟</span>
- </el-dialog>
-
- <div id="CT">
- <div id="CT_image">
- <el-card
- id="CT_image_1"
- class="box-card"
- style="
- border-radius: 8px;
- width: 800px;
- height: 360px;
- margin-bottom: -30px;
- "
- >
- <div class="demo-image__preview1">
- <div
- v-loading="loading"
- element-loading-text="上传图片中"
- element-loading-spinner="el-icon-loading"
- >
- <el-image
- :src="url_1"
- class="image_1"
- :preview-src-list="srcList"
- style="border-radius: 3px 3px 0 0"
- >
- <div slot="error">
- <div slot="placeholder" class="error">
- <el-button
- v-show="showbutton"
- type="primary"
- icon="el-icon-upload"
- class="download_bt"
- v-on:click="true_upload"
- >
- 上传图像
- <input
- ref="upload"
- style="display: none"
- name="file"
- type="file"
- @change="update"
- />
- </el-button>
- </div>
- </div>
- </el-image>
- </div>
- <div class="img_info_1" style="border-radius: 0 0 5px 5px">
- <span style="color: white; letter-spacing: 6px">原始图像</span>
- </div>
- </div>
- <div class="demo-image__preview2">
- <div
- v-loading="loading"
- element-loading-text="处理中,请耐心等待"
- element-loading-spinner="el-icon-loading"
- >
- <el-image
- :src="url_2"
- class="image_1"
- :preview-src-list="srcList1"
- style="border-radius: 3px 3px 0 0"
- >
- <div slot="error">
- <div slot="placeholder" class="error">{{ wait_return }}</div>
- </div>
- </el-image>
- </div>
- <div class="img_info_1" style="border-radius: 0 0 5px 5px">
- <span style="color: white; letter-spacing: 4px">检测结果</span>
- </div>
- </div>
- </el-card>
- </div>
- <div id="info_patient">
- <!-- 卡片放置表格 -->
- <el-card style="border-radius: 8px">
- <div slot="header" class="clearfix">
- <span>检测目标</span>
- <el-button
- style="margin-left: 35px"
- v-show="!showbutton"
- type="primary"
- icon="el-icon-upload"
- class="download_bt"
- v-on:click="true_upload2"
- >
- 重新选择图像
- <input
- ref="upload2"
- style="display: none"
- name="file"
- type="file"
- @change="update"
- />
- </el-button>
- </div>
- <el-tabs v-model="activeName">
- <el-tab-pane label="检测到的目标" name="first">
- <!-- 表格存放特征值 -->
- <el-table
- :data="feature_list"
- height="390"
- border
- style="width: 750px; text-align: center"
- v-loading="loading"
- element-loading-text="数据正在处理中,请耐心等待"
- element-loading-spinner="el-icon-loading"
- lazy
- >
- <el-table-column label="目标类别" width="250px">
- <template slot-scope="scope">
- <span>{{ scope.row[2] }}</span>
- </template>
- </el-table-column>
- <el-table-column label="目标大小" width="250px">
- <template slot-scope="scope">
- <span>{{ scope.row[0] }}</span>
- </template>
- </el-table-column>
- <el-table-column label="置信度" width="250px">
- <template slot-scope="scope">
- <span>{{ scope.row[1] }}</span>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- </el-tabs>
- </el-card>
- </div>
- </div>
- </div>
- </el-tab-pane>
- <el-tab-pane label="视频检测" name="second">
- <h3>视频名称</h3>
- <img :src="vidoedectetion">
- </el-tab-pane>
- <el-tab-pane label="视频流检测" name="third">
-
- </el-tab-pane>
- <el-tab-pane label="多路视频流检测" name="fourth">
-
- </el-tab-pane>
- </el-tabs>
-
- </template>
-
- <script>
- import axios from "axios";
-
- export default {
- name: "Content",
- data() {
- return {
- vidoedectetion:"http://127.0.0.1:5003" + "/video_start",
- server_url: "http://127.0.0.1:5003",
- activeName: "first",
- active: 0,
- centerDialogVisible: true,
- url_1: "",
- url_2: "",
- textarea: "",
- srcList: [],
- srcList1: [],
- feature_list: [],
- feature_list_1: [],
- feat_list: [],
- url: "",
- visible: false,
- wait_return: "等待上传",
- wait_upload: "等待上传",
- loading: false,
- table: false,
- isNav: false,
- showbutton: true,
- percentage: 0,
- fullscreenLoading: false,
- opacitys: {
- opacity: 0,
- },
- dialogTableVisible: false,
- };
- },
- created: function () {
- document.title = "Yolov5安全帽检测web推理部署";
- },
- methods: {
- true_upload() {
- this.$refs.upload.click();
- },
- true_upload2() {
- this.$refs.upload2.click();
- },
- next() {
- this.active++;
- },
- // 获得目标文件
- getObjectURL(file) {
- var url = null;
- if (window.createObjcectURL != undefined) {
- url = window.createOjcectURL(file);
- } else if (window.URL != undefined) {
- url = window.URL.createObjectURL(file);
- } else if (window.webkitURL != undefined) {
- url = window.webkitURL.createObjectURL(file);
- }
- return url;
- },
- // 上传文件
- update(e) {
- this.percentage = 0;
- this.dialogTableVisible = true;
- this.url_1 = "";
- this.url_2 = "";
- this.srcList = [];
- this.srcList1 = [];
- this.wait_return = "";
- this.wait_upload = "";
- this.feature_list = [];
- this.feat_list = [];
- this.fullscreenLoading = true;
- this.loading = true;
- this.showbutton = false;
- let file = e.target.files[0];
- this.url_1 = this.$options.methods.getObjectURL(file);
- let param = new FormData(); //创建form对象
- param.append("file", file, file.name); //通过append向form对象添加数据
- var timer = setInterval(() => {
- this.myFunc();
- }, 30);
- let config = {
- headers: { "Content-Type": "multipart/form-data" },
- }; //添加请求头
- axios
- .post(this.server_url + "/upload", param, config)
- .then((response) => {
- this.percentage = 100;
- clearInterval(timer);
- this.url_1 = response.data.image_url;
- this.srcList.push(this.url_1);
- this.url_2 = response.data.draw_url;
- this.srcList1.push(this.url_2);
- this.fullscreenLoading = false;
- this.loading = false;
-
- this.feat_list = Object.keys(response.data.image_info);
-
- for (var i = 0; i < this.feat_list.length; i++) {
- response.data.image_info[this.feat_list[i]][2] = this.feat_list[i];
- this.feature_list.push(response.data.image_info[this.feat_list[i]]);
- }
-
- this.feature_list.push(response.data.image_info);
- this.feature_list_1 = this.feature_list[0];
- this.dialogTableVisible = false;
- this.percentage = 0;
- this.notice1();
- });
- },
- myFunc() {
- if (this.percentage + 33 < 99) {
- this.percentage = this.percentage + 33;
- } else {
- this.percentage = 99;
- }
- },
- drawChart() {},
- notice1() {
- this.$notify({
- title: "预测成功",
- message: "点击图片可以查看大图",
- duration: 0,
- type: "success",
- });
- },
- },
- mounted() {
- this.drawChart();
- },
- };
- </script>
-
- <style>
- .el-button {
- padding: 12px 20px !important;
- }
-
- #hello p {
- font-size: 15px !important;
- /*line-height: 25px;*/
- }
-
- .n1 .el-step__description {
- padding-right: 20%;
- font-size: 14px;
- line-height: 20px;
- /* font-weight: 400; */
- }
- </style>
-
- <style scoped>
- * {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
- }
-
- .dialog_info {
- margin: 20px auto;
- }
-
- .text {
- font-size: 14px;
- }
-
- .item {
- margin-bottom: 18px;
- }
-
- .clearfix:before,
- .clearfix:after {
- display: table;
- content: "";
- }
-
- .clearfix:after {
- clear: both;
- }
-
- .box-card {
- width: 680px;
- height: 200px;
- border-radius: 8px;
- margin-top: -20px;
- }
-
- .divider {
- width: 50%;
- }
-
- #CT {
- display: flex;
- height: 100%;
- width: 100%;
- flex-wrap: wrap;
- justify-content: center;
- margin: 0 auto;
- margin-right: 0px;
- max-width: 1800px;
- }
-
- #CT_image_1 {
- width: 90%;
- height: 40%;
- margin: 0px auto;
- padding: 0px auto;
- margin-right: 180px;
- margin-bottom: 0px;
- border-radius: 4px;
- }
-
- #CT_image {
- margin-bottom: 60px;
- margin-left: 30px;
- margin-top: 5px;
- }
-
- .image_1 {
- width: 275px;
- height: 260px;
- background: #ffffff;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
- }
-
- .img_info_1 {
- height: 30px;
- width: 275px;
- text-align: center;
- background-color: #21b3b9;
- line-height: 30px;
- }
-
- .demo-image__preview1 {
- width: 250px;
- height: 290px;
- margin: 20px 60px;
- float: left;
- }
-
- .demo-image__preview2 {
- width: 250px;
- height: 290px;
-
- margin: 20px 460px;
- /* background-color: green; */
- }
-
- .error {
- margin: 100px auto;
- width: 50%;
- padding: 10px;
- text-align: center;
- }
-
- .block-sidebar {
- position: fixed;
- display: none;
- left: 50%;
- margin-left: 600px;
- top: 350px;
- width: 60px;
- z-index: 99;
- }
-
- .block-sidebar .block-sidebar-item {
- font-size: 50px;
- color: lightblue;
- text-align: center;
- line-height: 50px;
- margin-bottom: 20px;
- cursor: pointer;
- display: block;
- }
-
- div {
- display: block;
- }
-
- .block-sidebar .block-sidebar-item:hover {
- color: #187aab;
- }
-
- .download_bt {
- padding: 10px 16px !important;
- }
-
- #upfile {
- width: 104px;
- height: 45px;
- background-color: #187aab;
- color: #fff;
- text-align: center;
- line-height: 45px;
- border-radius: 3px;
- box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1), 0 2px 2px 0 rgba(0, 0, 0, 0.2);
- color: #fff;
- font-family: "Source Sans Pro", Verdana, sans-serif;
- font-size: 0.875rem;
- }
-
- .file {
- width: 200px;
- height: 130px;
- position: absolute;
- left: -20px;
- top: 0;
- z-index: 1;
- -moz-opacity: 0;
- -ms-opacity: 0;
- -webkit-opacity: 0;
- opacity: 0; /*css属性——opcity不透明度,取值0-1*/
- filter: alpha(opacity=0);
- cursor: pointer;
- }
-
- #upload {
- position: relative;
- margin: 0px 0px;
- }
-
- #Content {
- width: 85%;
- height: 800px;
- background-color: #ffffff;
- margin: 15px auto;
- display: flex;
- min-width: 1200px;
- }
-
- .divider {
- background-color: #eaeaea !important;
- height: 2px !important;
- width: 100%;
- margin-bottom: 50px;
- }
-
- .divider_1 {
- background-color: #ffffff;
- height: 2px !important;
- width: 100%;
- margin-bottom: 20px;
- margin: 20px auto;
- }
-
- .steps {
- font-family: "lucida grande", "lucida sans unicode", lucida, helvetica,
- "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
- color: #21b3b9;
- text-align: center;
- margin: 15px auto;
- font-size: 20px;
- font-weight: bold;
- text-align: center;
- }
-
- .step_1 {
- /*color: #303133 !important;*/
- margin: 20px 26px;
- }
-
- #info_patient {
- margin-top: 10px;
- margin-right: 160px;
- }
- </style>
-
-
yolov5-flask-web - 知乎 (zhihu.com)
Flask部署YOLOv5 - 知乎 (zhihu.com)
https://zhuanlan.zhihu.com/p/104273184
特别感谢作者
GitHub - Sharpiless/Yolov5-Flask-VUE: 基于Flask+VUE前后端,在阿里云公网WEB端部署YOLOv5目标检测模型
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。