当前位置:   article > 正文

yolov5 web端部署进行图片和视频检测_yolov5 flask web 部署

yolov5 flask web 部署

目录

1、思路

2、代码结构

3、代码运行

4、api接口代码

5、web ui界面

6、参考资料

7、代码分享 


1、思路

通过搭建flask微型服务器后端,以后通过vue搭建网页前端。flask是第一个第三方库。与其他模块一样,安装时可以直接使用python的pip命令实现。flask是web开发框架,简单易学,因此用flask来搭建web服务也非常简单。

在pycharm新建一个项目,命名为web2020,然后新建一个python文件,命名为main.py。在代码中输入如下代码:

  1. from flask import Flask #导入Flask类
  2. app=Flask(__name__) #实例化并命名为app实例
  3. if __name__=="__main__":
  4. app.run(port=2020,host="127.0.0.1",debug=True) #调用run方法,设定端口号,启动服务

路由定义:

  1. from flask import Flask
  2. app=Flask(__name__)
  3. @app.route('/')
  4. def index():
  5. return 'welcome to my webpage!'
  6. if __name__=="__main__":
  7. app.run(port=2020,host="127.0.0.1",debug=True)

通过这种方式,实现python调用模型,然后通过web服务器进行数据输入输出,最后通过浏览器web页面进行展示。

2、代码结构

前端代码结构

后端代码结构

3、代码运行

4、api接口代码

  1. import datetime
  2. import logging as rel_log
  3. import os
  4. import shutil
  5. from datetime import timedelta
  6. from flask import *
  7. from flask import Flask, render_template, Response
  8. from processor.AIDetector_pytorch import Detector
  9. import core.main
  10. # import camera driver
  11. if os.environ.get('CAMERA'):
  12. Camera = import_module('camera_' + os.environ['CAMERA']).Camera
  13. else:
  14. from camera import Camera
  15. UPLOAD_FOLDER = r'./uploads'
  16. ALLOWED_EXTENSIONS = set(['png', 'jpg'])
  17. app = Flask(__name__)
  18. app.secret_key = 'secret!'
  19. app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
  20. werkzeug_logger = rel_log.getLogger('werkzeug')
  21. werkzeug_logger.setLevel(rel_log.ERROR)
  22. # 解决缓存刷新问题
  23. app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1)
  24. # 添加header解决跨域
  25. @app.after_request
  26. def after_request(response):
  27. response.headers['Access-Control-Allow-Origin'] = '*'
  28. response.headers['Access-Control-Allow-Credentials'] = 'true'
  29. response.headers['Access-Control-Allow-Methods'] = 'POST'
  30. response.headers['Access-Control-Allow-Headers'] = 'Content-Type, X-Requested-With'
  31. return response
  32. #图片检测接口
  33. def allowed_file(filename):
  34. return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
  35. #@app.route('/')
  36. #def hello_world():
  37. # return redirect(url_for('static', filename='./index.html'))
  38. @app.route('/')
  39. def index():
  40. """Video streaming home page."""
  41. return render_template('index.html')
  42. @app.route('/upload', methods=['GET', 'POST'])
  43. def upload_file():
  44. file = request.files['file']
  45. print(datetime.datetime.now(), file.filename)
  46. #if file and allowed_file(file.filename):
  47. src_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
  48. file.save(src_path)
  49. shutil.copy(src_path, './tmp/ct')
  50. image_path = os.path.join('./tmp/ct', file.filename)
  51. pid, image_info = core.main.c_main(
  52. image_path, current_app.model, file.filename.rsplit('.', 1)[1])
  53. return jsonify({'status': 1,
  54. 'image_url': 'http://127.0.0.1:5003/tmp/ct/' + pid,
  55. 'draw_url': 'http://127.0.0.1:5003/tmp/draw/' + pid,
  56. 'image_info': image_info})
  57. #return jsonify({'status': 0})
  58. @app.route("/download", methods=['GET'])
  59. def download_file():
  60. # 需要知道2个参数, 第1个参数是本地目录的path, 第2个参数是文件名(带扩展名)
  61. return send_from_directory('data', 'testfile.zip', as_attachment=True)
  62. # show photo
  63. @app.route('/tmp/<path:file>', methods=['GET'])
  64. def show_photo(file):
  65. if request.method == 'GET':
  66. if not file is None:
  67. image_data = open(f'tmp/{file}', "rb").read()
  68. response = make_response(image_data)
  69. response.headers['Content-Type'] = 'image/png'
  70. return response
  71. #视频检测接口
  72. def gen(camera):
  73. """Video streaming generator function."""
  74. while True:
  75. frame = camera.get_frame()
  76. yield (b'--frame\r\n'
  77. b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
  78. @app.route('/video_start')
  79. def video_feed():
  80. """Video streaming route. Put this in the src attribute of an img tag."""
  81. return Response(gen(Camera()),
  82. mimetype='multipart/x-mixed-replace; boundary=frame')
  83. #视频流检测接口
  84. #@app.route('/livestream_start')
  85. #程序启动入口
  86. if __name__=='__main__':
  87. files = [
  88. 'uploads', 'tmp/ct', 'tmp/draw',
  89. 'tmp/image', 'tmp/mask', 'tmp/uploads'
  90. ]
  91. for ff in files:
  92. if not os.path.exists(ff):
  93. os.makedirs(ff)
  94. with app.app_context():
  95. current_app.model = Detector()
  96. app.run(host='127.0.0.1', port=5003, debug=True)

5、web ui界面

  1. <template>
  2. <el-tabs stretch=true v-model="activeName" type="card" @tab-click="handleClick">
  3. <el-tab-pane label="图片检测" name="first">
  4. <div id="Content">
  5. <el-dialog
  6. title="AI预测中"
  7. :visible.sync="dialogTableVisible"
  8. :show-close="false"
  9. :close-on-press-escape="false"
  10. :append-to-body="true"
  11. :close-on-click-modal="false"
  12. :center="true"
  13. >
  14. <el-progress :percentage="percentage"></el-progress>
  15. <span slot="footer" class="dialog-footer">请耐心等待约3秒钟</span>
  16. </el-dialog>
  17. <div id="CT">
  18. <div id="CT_image">
  19. <el-card
  20. id="CT_image_1"
  21. class="box-card"
  22. style="
  23. border-radius: 8px;
  24. width: 800px;
  25. height: 360px;
  26. margin-bottom: -30px;
  27. "
  28. >
  29. <div class="demo-image__preview1">
  30. <div
  31. v-loading="loading"
  32. element-loading-text="上传图片中"
  33. element-loading-spinner="el-icon-loading"
  34. >
  35. <el-image
  36. :src="url_1"
  37. class="image_1"
  38. :preview-src-list="srcList"
  39. style="border-radius: 3px 3px 0 0"
  40. >
  41. <div slot="error">
  42. <div slot="placeholder" class="error">
  43. <el-button
  44. v-show="showbutton"
  45. type="primary"
  46. icon="el-icon-upload"
  47. class="download_bt"
  48. v-on:click="true_upload"
  49. >
  50. 上传图像
  51. <input
  52. ref="upload"
  53. style="display: none"
  54. name="file"
  55. type="file"
  56. @change="update"
  57. />
  58. </el-button>
  59. </div>
  60. </div>
  61. </el-image>
  62. </div>
  63. <div class="img_info_1" style="border-radius: 0 0 5px 5px">
  64. <span style="color: white; letter-spacing: 6px">原始图像</span>
  65. </div>
  66. </div>
  67. <div class="demo-image__preview2">
  68. <div
  69. v-loading="loading"
  70. element-loading-text="处理中,请耐心等待"
  71. element-loading-spinner="el-icon-loading"
  72. >
  73. <el-image
  74. :src="url_2"
  75. class="image_1"
  76. :preview-src-list="srcList1"
  77. style="border-radius: 3px 3px 0 0"
  78. >
  79. <div slot="error">
  80. <div slot="placeholder" class="error">{{ wait_return }}</div>
  81. </div>
  82. </el-image>
  83. </div>
  84. <div class="img_info_1" style="border-radius: 0 0 5px 5px">
  85. <span style="color: white; letter-spacing: 4px">检测结果</span>
  86. </div>
  87. </div>
  88. </el-card>
  89. </div>
  90. <div id="info_patient">
  91. <!-- 卡片放置表格 -->
  92. <el-card style="border-radius: 8px">
  93. <div slot="header" class="clearfix">
  94. <span>检测目标</span>
  95. <el-button
  96. style="margin-left: 35px"
  97. v-show="!showbutton"
  98. type="primary"
  99. icon="el-icon-upload"
  100. class="download_bt"
  101. v-on:click="true_upload2"
  102. >
  103. 重新选择图像
  104. <input
  105. ref="upload2"
  106. style="display: none"
  107. name="file"
  108. type="file"
  109. @change="update"
  110. />
  111. </el-button>
  112. </div>
  113. <el-tabs v-model="activeName">
  114. <el-tab-pane label="检测到的目标" name="first">
  115. <!-- 表格存放特征值 -->
  116. <el-table
  117. :data="feature_list"
  118. height="390"
  119. border
  120. style="width: 750px; text-align: center"
  121. v-loading="loading"
  122. element-loading-text="数据正在处理中,请耐心等待"
  123. element-loading-spinner="el-icon-loading"
  124. lazy
  125. >
  126. <el-table-column label="目标类别" width="250px">
  127. <template slot-scope="scope">
  128. <span>{{ scope.row[2] }}</span>
  129. </template>
  130. </el-table-column>
  131. <el-table-column label="目标大小" width="250px">
  132. <template slot-scope="scope">
  133. <span>{{ scope.row[0] }}</span>
  134. </template>
  135. </el-table-column>
  136. <el-table-column label="置信度" width="250px">
  137. <template slot-scope="scope">
  138. <span>{{ scope.row[1] }}</span>
  139. </template>
  140. </el-table-column>
  141. </el-table>
  142. </el-tab-pane>
  143. </el-tabs>
  144. </el-card>
  145. </div>
  146. </div>
  147. </div>
  148. </el-tab-pane>
  149. <el-tab-pane label="视频检测" name="second">
  150. <h3>视频名称</h3>
  151. <img :src="vidoedectetion">
  152. </el-tab-pane>
  153. <el-tab-pane label="视频流检测" name="third">
  154. </el-tab-pane>
  155. <el-tab-pane label="多路视频流检测" name="fourth">
  156. </el-tab-pane>
  157. </el-tabs>
  158. </template>
  159. <script>
  160. import axios from "axios";
  161. export default {
  162. name: "Content",
  163. data() {
  164. return {
  165. vidoedectetion:"http://127.0.0.1:5003" + "/video_start",
  166. server_url: "http://127.0.0.1:5003",
  167. activeName: "first",
  168. active: 0,
  169. centerDialogVisible: true,
  170. url_1: "",
  171. url_2: "",
  172. textarea: "",
  173. srcList: [],
  174. srcList1: [],
  175. feature_list: [],
  176. feature_list_1: [],
  177. feat_list: [],
  178. url: "",
  179. visible: false,
  180. wait_return: "等待上传",
  181. wait_upload: "等待上传",
  182. loading: false,
  183. table: false,
  184. isNav: false,
  185. showbutton: true,
  186. percentage: 0,
  187. fullscreenLoading: false,
  188. opacitys: {
  189. opacity: 0,
  190. },
  191. dialogTableVisible: false,
  192. };
  193. },
  194. created: function () {
  195. document.title = "Yolov5安全帽检测web推理部署";
  196. },
  197. methods: {
  198. true_upload() {
  199. this.$refs.upload.click();
  200. },
  201. true_upload2() {
  202. this.$refs.upload2.click();
  203. },
  204. next() {
  205. this.active++;
  206. },
  207. // 获得目标文件
  208. getObjectURL(file) {
  209. var url = null;
  210. if (window.createObjcectURL != undefined) {
  211. url = window.createOjcectURL(file);
  212. } else if (window.URL != undefined) {
  213. url = window.URL.createObjectURL(file);
  214. } else if (window.webkitURL != undefined) {
  215. url = window.webkitURL.createObjectURL(file);
  216. }
  217. return url;
  218. },
  219. // 上传文件
  220. update(e) {
  221. this.percentage = 0;
  222. this.dialogTableVisible = true;
  223. this.url_1 = "";
  224. this.url_2 = "";
  225. this.srcList = [];
  226. this.srcList1 = [];
  227. this.wait_return = "";
  228. this.wait_upload = "";
  229. this.feature_list = [];
  230. this.feat_list = [];
  231. this.fullscreenLoading = true;
  232. this.loading = true;
  233. this.showbutton = false;
  234. let file = e.target.files[0];
  235. this.url_1 = this.$options.methods.getObjectURL(file);
  236. let param = new FormData(); //创建form对象
  237. param.append("file", file, file.name); //通过append向form对象添加数据
  238. var timer = setInterval(() => {
  239. this.myFunc();
  240. }, 30);
  241. let config = {
  242. headers: { "Content-Type": "multipart/form-data" },
  243. }; //添加请求头
  244. axios
  245. .post(this.server_url + "/upload", param, config)
  246. .then((response) => {
  247. this.percentage = 100;
  248. clearInterval(timer);
  249. this.url_1 = response.data.image_url;
  250. this.srcList.push(this.url_1);
  251. this.url_2 = response.data.draw_url;
  252. this.srcList1.push(this.url_2);
  253. this.fullscreenLoading = false;
  254. this.loading = false;
  255. this.feat_list = Object.keys(response.data.image_info);
  256. for (var i = 0; i < this.feat_list.length; i++) {
  257. response.data.image_info[this.feat_list[i]][2] = this.feat_list[i];
  258. this.feature_list.push(response.data.image_info[this.feat_list[i]]);
  259. }
  260. this.feature_list.push(response.data.image_info);
  261. this.feature_list_1 = this.feature_list[0];
  262. this.dialogTableVisible = false;
  263. this.percentage = 0;
  264. this.notice1();
  265. });
  266. },
  267. myFunc() {
  268. if (this.percentage + 33 < 99) {
  269. this.percentage = this.percentage + 33;
  270. } else {
  271. this.percentage = 99;
  272. }
  273. },
  274. drawChart() {},
  275. notice1() {
  276. this.$notify({
  277. title: "预测成功",
  278. message: "点击图片可以查看大图",
  279. duration: 0,
  280. type: "success",
  281. });
  282. },
  283. },
  284. mounted() {
  285. this.drawChart();
  286. },
  287. };
  288. </script>
  289. <style>
  290. .el-button {
  291. padding: 12px 20px !important;
  292. }
  293. #hello p {
  294. font-size: 15px !important;
  295. /*line-height: 25px;*/
  296. }
  297. .n1 .el-step__description {
  298. padding-right: 20%;
  299. font-size: 14px;
  300. line-height: 20px;
  301. /* font-weight: 400; */
  302. }
  303. </style>
  304. <style scoped>
  305. * {
  306. box-sizing: border-box;
  307. margin: 0;
  308. padding: 0;
  309. }
  310. .dialog_info {
  311. margin: 20px auto;
  312. }
  313. .text {
  314. font-size: 14px;
  315. }
  316. .item {
  317. margin-bottom: 18px;
  318. }
  319. .clearfix:before,
  320. .clearfix:after {
  321. display: table;
  322. content: "";
  323. }
  324. .clearfix:after {
  325. clear: both;
  326. }
  327. .box-card {
  328. width: 680px;
  329. height: 200px;
  330. border-radius: 8px;
  331. margin-top: -20px;
  332. }
  333. .divider {
  334. width: 50%;
  335. }
  336. #CT {
  337. display: flex;
  338. height: 100%;
  339. width: 100%;
  340. flex-wrap: wrap;
  341. justify-content: center;
  342. margin: 0 auto;
  343. margin-right: 0px;
  344. max-width: 1800px;
  345. }
  346. #CT_image_1 {
  347. width: 90%;
  348. height: 40%;
  349. margin: 0px auto;
  350. padding: 0px auto;
  351. margin-right: 180px;
  352. margin-bottom: 0px;
  353. border-radius: 4px;
  354. }
  355. #CT_image {
  356. margin-bottom: 60px;
  357. margin-left: 30px;
  358. margin-top: 5px;
  359. }
  360. .image_1 {
  361. width: 275px;
  362. height: 260px;
  363. background: #ffffff;
  364. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  365. }
  366. .img_info_1 {
  367. height: 30px;
  368. width: 275px;
  369. text-align: center;
  370. background-color: #21b3b9;
  371. line-height: 30px;
  372. }
  373. .demo-image__preview1 {
  374. width: 250px;
  375. height: 290px;
  376. margin: 20px 60px;
  377. float: left;
  378. }
  379. .demo-image__preview2 {
  380. width: 250px;
  381. height: 290px;
  382. margin: 20px 460px;
  383. /* background-color: green; */
  384. }
  385. .error {
  386. margin: 100px auto;
  387. width: 50%;
  388. padding: 10px;
  389. text-align: center;
  390. }
  391. .block-sidebar {
  392. position: fixed;
  393. display: none;
  394. left: 50%;
  395. margin-left: 600px;
  396. top: 350px;
  397. width: 60px;
  398. z-index: 99;
  399. }
  400. .block-sidebar .block-sidebar-item {
  401. font-size: 50px;
  402. color: lightblue;
  403. text-align: center;
  404. line-height: 50px;
  405. margin-bottom: 20px;
  406. cursor: pointer;
  407. display: block;
  408. }
  409. div {
  410. display: block;
  411. }
  412. .block-sidebar .block-sidebar-item:hover {
  413. color: #187aab;
  414. }
  415. .download_bt {
  416. padding: 10px 16px !important;
  417. }
  418. #upfile {
  419. width: 104px;
  420. height: 45px;
  421. background-color: #187aab;
  422. color: #fff;
  423. text-align: center;
  424. line-height: 45px;
  425. border-radius: 3px;
  426. box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1), 0 2px 2px 0 rgba(0, 0, 0, 0.2);
  427. color: #fff;
  428. font-family: "Source Sans Pro", Verdana, sans-serif;
  429. font-size: 0.875rem;
  430. }
  431. .file {
  432. width: 200px;
  433. height: 130px;
  434. position: absolute;
  435. left: -20px;
  436. top: 0;
  437. z-index: 1;
  438. -moz-opacity: 0;
  439. -ms-opacity: 0;
  440. -webkit-opacity: 0;
  441. opacity: 0; /*css属性&mdash;&mdash;opcity不透明度,取值0-1*/
  442. filter: alpha(opacity=0);
  443. cursor: pointer;
  444. }
  445. #upload {
  446. position: relative;
  447. margin: 0px 0px;
  448. }
  449. #Content {
  450. width: 85%;
  451. height: 800px;
  452. background-color: #ffffff;
  453. margin: 15px auto;
  454. display: flex;
  455. min-width: 1200px;
  456. }
  457. .divider {
  458. background-color: #eaeaea !important;
  459. height: 2px !important;
  460. width: 100%;
  461. margin-bottom: 50px;
  462. }
  463. .divider_1 {
  464. background-color: #ffffff;
  465. height: 2px !important;
  466. width: 100%;
  467. margin-bottom: 20px;
  468. margin: 20px auto;
  469. }
  470. .steps {
  471. font-family: "lucida grande", "lucida sans unicode", lucida, helvetica,
  472. "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
  473. color: #21b3b9;
  474. text-align: center;
  475. margin: 15px auto;
  476. font-size: 20px;
  477. font-weight: bold;
  478. text-align: center;
  479. }
  480. .step_1 {
  481. /*color: #303133 !important;*/
  482. margin: 20px 26px;
  483. }
  484. #info_patient {
  485. margin-top: 10px;
  486. margin-right: 160px;
  487. }
  488. </style>

6、参考资料

yolov5-flask-web - 知乎 (zhihu.com)

Flask部署YOLOv5 - 知乎 (zhihu.com)

https://zhuanlan.zhihu.com/p/104273184

特别感谢作者

GitHub - Sharpiless/Yolov5-Flask-VUE: 基于Flask+VUE前后端,在阿里云公网WEB端部署YOLOv5目标检测模型

7、代码分享 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/83216
推荐阅读
相关标签
  

闽ICP备14008679号