yolov5使用flask部署至前端,实现照片\视频识别

初学yolo flask时,需要此功能,Csdn、Github、B站找到许多代码,效果并不满意。
近期,再度尝试,实现简单功能。

实现功能:

  1. 上传图片并识别,可以点击图片放大查看

  2. 上传视频并识别

  3. 识别后的文件下载功能

效果图如上

文件结构如下:

project/
  static/

  空

  templates/
    index.html
    
  app.py
 

相关代码:

app.py

import cv2
import numpy as np
import torch
from flask import Flask, request, jsonify, render_template
import base64
import os
from datetime import datetimeapp = Flask(__name__)# 全局变量:模型
model = None# 提前加载模型
def load_model():global modelmodel = torch.hub.load('', 'custom', path='yolov5s.pt', source='local')# 路由处理图片检测请求
@app.route('/predict_image', methods=['POST'])
def predict_image():global model# 获取图像文件file = request.files['image']# 读取图像数据并转换为RGB格式image_data = file.read()nparr = np.frombuffer(image_data, np.uint8)image = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)results = model(image)image = results.render()[0]# 将图像转换为 base64 编码的字符串_, buffer = cv2.imencode('.png', image)image_str = base64.b64encode(buffer).decode('utf-8')# 获取当前时间,并将其格式化为字符串current_time = datetime.now().strftime('%Y%m%d%H%M%S')# 构建保存路径save_dir = 'static'if not os.path.exists(save_dir):os.makedirs(save_dir)filename, extension = os.path.splitext(file.filename)  # 获取上传文件的文件名和扩展名save_filename = f'{filename}_{current_time}{extension}'save_path = os.path.join(save_dir, save_filename)cv2.imwrite(save_path, image)return jsonify({'image': image_str})# 函数用于在视频帧上绘制检测结果
def detect_objects(frame, model):results = model(frame)detections = results.xyxy[0].cpu().numpy()  # 获取检测结果# 在帧上绘制检测结果for det in detections:# 获取边界框信息x1, y1, x2, y2, conf, class_id = det[:6]# 在帧上绘制边界框cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)# 在帧上绘制类别和置信度label = f'{model.names[int(class_id)]} {conf:.2f}'cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)return frame# 路由处理视频检测请求@app.route("/predict_video", methods=["POST"])
def predict_video():global model# 从请求中获取视频文件video_file = request.files["video"]# 保存视频到临时文件temp_video_path = "temp_video.mp4"video_file.save(temp_video_path)# 逐帧读取视频video = cv2.VideoCapture(temp_video_path)# 获取视频的帧率和尺寸fps = video.get(cv2.CAP_PROP_FPS)width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))# 视频写入对象output_video_filename = f"output_video_{datetime.now().strftime('%Y%m%d%H%M%S')}.mp4"output_video_path = os.path.join("static", output_video_filename)fourcc = cv2.VideoWriter_fourcc(*"avc1")out_video = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))# 逐帧处理视频并进行目标检测while True:ret, frame = video.read()if not ret:break# 进行目标检测detection_result = detect_objects(frame, model)# 将处理后的帧写入输出视频out_video.write(detection_result)# 释放视频对象video.release()out_video.release()return jsonify({"output_video_path": output_video_filename})@app.route('/')
def index():return render_template('index.html')# 初始加载模型
load_model()if __name__ == '__main__':app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Object Detection</title><style>body {font-family: Arial, sans-serif;margin: 0;padding: 0;background-color: #f3f3f3;display: flex;justify-content: center;align-items: center;height: 100vh;flex-direction: column;}#content {text-align: center;max-width: 820px;margin-top: 20px;}h1 {color: #333;}h2 {color: #666;}input[type="file"] {margin-bottom: 10px;}.media-container {display: flex;max-width: 100%;margin-bottom: 20px;}.media-container:first-child {margin-right: 20px; /* 在第一个容器的右侧添加间隔 */}.media-container img,.media-container video {max-width: 100%;height: auto;}.original {width: 400px;overflow: hidden;}.processed {flex: 2; /* 右边容器占据剩余空间 */}button {padding: 10px 20px;background-color: #007bff;color: #fff;border: none;border-radius: 5px;cursor: pointer;margin-bottom: 10px;}/* 新增样式:模态框 */.modal {display: none; /* 默认隐藏 */position: fixed;z-index: 1;left: 0;top: 0;width: 100%;height: 100%;overflow: auto;background-color: rgba(0, 0, 0, 0.9); /* 半透明黑色背景 */}.modal-content {margin: auto;display: block;width: 80%;max-width: 800px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);text-align: center; /* 居中显示图片 */}.close {color: #ccc;font-size: 36px;font-weight: bold;cursor: pointer;position: absolute;top: 10px;right: 10px;}.close:hover,.close:focus {color: #fff;text-decoration: none;}#downloadButton {padding: 10px 20px;background-color: #007bff;color: #fff;border: none;border-radius: 5px;cursor: pointer;margin-bottom: 10px;}/* 新增样式:响应式图片 */.modal-content img,.modal-content video {max-width: 100%;height: auto;}</style>
</head>
<body><!-- 新增模态框 --><div id="myModal" class="modal" onclick="closeModal()"><div class="modal-content" id="modalContent" onclick="stopPropagation(event)"><!-- 放大后的图片或视频将在这里显示 --><span class="close" onclick="closeModal()">&times;</span></div></div><div id="content"><h1>照片/视频检测</h1><!-- 上传图片 --><h2>上传图片</h2><input type="file" id="imageFile" accept="image/*" onchange="displaySelectedImage()"><button onclick="uploadImage()">上传</button><button id="downloadImageButton"  onclick="downloadProcessedImage()">下载</button><br><div class="media-container"><div class="original media-container" onclick="enlargeImage()"><img id="uploadedImage" src="#" alt="Uploaded Image" style="display:none;"><button id="zoomInButton" style="display:none;">Zoom In</button></div><div class="processed media-container" onclick="enlargeImage2()"><img id="processedImage" src="#" alt="Processed Image" style="display:none;"></div></div><br><!-- 上传视频 --><h2>上传视频</h2><input type="file" id="videoFile" accept="video/mp4,video/x-m4v,video/*" onchange="displaySelectedVideo()"><button onclick="uploadVideo()">上传</button><button id="downloadButton" onclick="downloadProcessedVideo()">下载</button><br><div class="media-container"><div class="original media-container" ><video id="uploadedVideo" src="#" controls style="display:none;"></video></div><div class="processed media-container"><video id="processedVideo" controls style="display:none;"></video></div></div><br></div><script>// 显示选择的权重文件// 显示选择的图片并添加点击放大功能function displaySelectedImage() {var fileInput = document.getElementById('imageFile');var file = fileInput.files[0];var imageElement = document.getElementById('uploadedImage');imageElement.src = URL.createObjectURL(file);imageElement.style.display = 'inline';document.getElementById('zoomInButton').style.display = 'inline';}// 显示模态框并放大图片function enlargeImage() {var modal = document.getElementById('myModal');var modalImg = document.getElementById('modalContent');var img = document.getElementById('uploadedImage');modal.style.display = 'block';modalImg.innerHTML = '<img src="' + img.src + '">';}// 显示模态框并放大图片function enlargeImage2() {var modal = document.getElementById('myModal');var modalImg = document.getElementById('modalContent');var img = document.getElementById('processedImage');modal.style.display = 'block';modalImg.innerHTML = '<img src="' + img.src + '">';}// 显示选择的视频并添加点击放大功能function displaySelectedVideo() {var fileInput = document.getElementById('videoFile');var file = fileInput.files[0];var videoElement = document.getElementById('uploadedVideo');videoElement.src = URL.createObjectURL(file);videoElement.style.display = 'block';}// 上传图片并向后端发送请求function uploadImage() {var fileInput = document.getElementById('imageFile');var file = fileInput.files[0];var formData = new FormData();formData.append('image', file);fetch('/predict_image', {method: 'POST',body: formData}).then(response => response.json()).then(data => {var imageElement = document.getElementById('processedImage');imageElement.src = 'data:image/png;base64,' + data.image;imageElement.style.display = 'inline';document.getElementById('downloadImageButton').style.display = 'inline';}).catch(error => console.error('Error:', error));}// 下载处理后的图片function downloadProcessedImage() {var imageElement = document.getElementById('processedImage');var url = imageElement.src;var a = document.createElement('a');a.href = url;a.download = 'processed_image.png';document.body.appendChild(a);a.click();document.body.removeChild(a);}// 上传视频并向后端发送请求function uploadVideo() {var fileInput = document.getElementById('videoFile');var file = fileInput.files[0];var formData = new FormData();formData.append('video', file);fetch('/predict_video', {method: 'POST',body: formData}).then(response => response.json()).then(data => {var videoElement = document.getElementById('processedVideo');// 修改路径为正确的 Flask url_for 生成的路径videoElement.src = '{{ url_for("static", filename="") }}' + data.output_video_path;videoElement.style.display = 'block';var downloadButton = document.getElementById('downloadButton');downloadButton.style.display = 'block';}).catch(error => console.error('Error:', error));}// 下载处理后的视频function downloadProcessedVideo() {var videoElement = document.getElementById('processedVideo');var url = videoElement.src;var a = document.createElement('a');a.href = url;a.download = 'processed_video.mp4';document.body.appendChild(a);a.click();document.body.removeChild(a);}// 关闭模态框function closeModal() {var modal = document.getElementById('myModal');modal.style.display = 'none';}</script>
</body>
</html>

使用说明:

index.html放入templates文件夹中

运行app.py

注:此处加载模型路径更改为自己的

model = torch.hub.load('', 'custom', path='yolov5s.pt', source='local')

如果模型读取不到,显示

FileNotFoundError: [Errno 2] No such file or directory: 'hubconf.py'

去yolov5官网,下载yolov5-master到项目文件夹

并将yolov5s.pt文件复制到yolov5-master文件夹中,修改model路径

model = torch.hub.load('yolov5-master', 'custom', path='yolov5s.pt', source='local')

如有问题,可联系作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows电脑如何启动RTSP服务实现本地摄像头数据共享

技术背景 提起Windows共享本地摄像头&#xff0c;好多人想到的是通过ffmepg或vlc串流到服务器&#xff0c;实际上&#xff0c;用轻量级RTSP服务更简单&#xff0c;本文就介绍下&#xff0c;如何用大牛直播SDK的Windows轻量级RTSP服务&#xff0c;采集摄像头&#xff0c;生成本…

人工智能历史:从梦想到现实的变革之路

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Git 安装教程

1、登录git 官方网站&#xff1a;https://git-scm.com/ 点击左边的 Downloads 或者 右边标识的下载标志&#xff0c;它根据电脑操作系统自动匹配版本 Downloads for Windows 2、以 windows 为例下载对应版本 网络有时可能不大好&#xff0c;阿里镜像下载超快。 下载好以后&a…

Tensorflow中高维矩阵的乘法运算tf.matmul(tf.linalg.matmul)详悉

1.问题由来 在tensorflow框架下&#xff0c;经常会用到矩阵的乘法运算&#xff0c;特别是高&#xff08;多&#xff09;维的矩阵运算&#xff0c;在这些矩阵运算时&#xff0c;经常使用到其中的tf.matmul或tf.linalg.matmul等函数。但高维矩阵在内部怎么运算的&#xff1f;其内…

【深度学习入门】安装conda/miniconda、所需包类、CUDA与conda/Miniconda间的关系

深度学习入门 须知 本教程跟随李沐老师课程随笔&#xff0c;课程链接点击此处。 CUDA和Anaconda的关系 CUDA Toolkit是由Nvidia官方提供的完整工具包&#xff0c;其中提供了Nvidia驱动程序、开发CUDA程序相关的开发工具包等。 Anaconda在安装Pytorch等会用到的CUDA的框架时…

【Air724UG】4G模块

目录 一、实物图 二、原理图 引脚定义 三、简介 基本原理 产品参数 UART1 蓝色指示灯 五、注意&#xff1a; 源文件下载 可访问底部联系方式也可前往电子校园网官网搜索关键词 关键词&#xff1a; Air724UG 一…

wpf中轮询显示图片

本文的需求是&#xff0c;在一个文件夹中&#xff0c;放一堆图片的集合&#xff0c;然后在wpf程序中&#xff0c;按照定时的方式&#xff0c;循序显示照片。 全部代码 1.声明一个PictureInfo类 namespace WpfApp1 {public class PictureInfo{public string? FileName { get; …

uni-app全局文件与常用API

文章目录 rpx响应式单位import导入css样式及scss变量用法与static目录import导入css样式uni.scss变量用法 pages.json页面路由globalStyle的属性pages设置页面路径及窗口表现tabBar设置底部菜单选项及iconfont图标 vite.config中安装插件unplugin-auto-import自动导入vue和unia…

探索Perl的奇妙世界:入门学习与实战指南

一、Perl语言概述 1.1 Perl的起源与发展 Perl&#xff08;Practical Extraction and Reporting Language&#xff09;是一种高级、解释型、动态编程语言&#xff0c;由Larry Wall于1987年发明。Perl的初衷是作为一种文本处理工具&#xff0c;帮助系统管理员在Unix系统中处理报…

Godot游戏制作 04平台设计

新建创景&#xff0c;添加AnimatableBody2D节点。 添加Sprite2D节点 拖动图片 剪裁图片&#xff0c;吸附模式&#xff1a;像素吸附 添加CollisionShape2D&#xff0c;设置实际形状为矩形 重命名AnimatableBody2D节点为Platform&#xff0c;保存场景&#xff0c;拖动platform场景…

数据库(MySQL)-视图、存储过程、触发器

一、视图 视图的定义、作用 视图是从一个或者几个基本表&#xff08;或视图&#xff09;导出的表。它与基本表不同&#xff0c;是一个虚表。但是视图只能用来查看表&#xff0c;不能做增删改查。 视图的作用&#xff1a;①简化查询 ②重写格式化数据 ③频繁访问数据库 ④过…

Photos框架 - 自定义媒体资源选择器(数据部分)

引言 在iOS开发中&#xff0c;系统已经为我们提供了多种便捷的媒体资源选择方式&#xff0c;如UIImagePickerController和PHPickerViewController。这些方式不仅使用方便、界面友好&#xff0c;而且我们完全不需要担心性能和稳定性问题&#xff0c;因为它们是由系统提供的&…

计算机毕业设计django+hadoop+scrapy租房可视化 租房推荐系统 租房大屏可视化 租房爬虫 spark 58同城租房爬虫 房源推荐系统

python scrapy bootstrap jquery css javascript html 租房信息数据展示 租房地址数量分布 租房类型统计 租房价格统计分析 租房面积分析 房屋朝向分析 房屋户型平均价格统计分析 房屋楼层统计分析 房屋楼层与价格统计分析 房屋地址与价格统计分析 房屋相关信息词云展示 租房…

字符指针专题

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 #define _CRT_SECURE_NO_WARNING #include <stdio.h> int main(void) {char a w;char* b &a;*b q;printf("%c\n",*b);return 0; } 这是字符指针的普通用法&#xff0c;和一般指针无…

在服务器上同时训练多个深度学习模型【nohup、后台、重定向】

在服务器上同时训练多个深度学习模型 在服务器上跑深度学习或其他程序时&#xff0c;如果程序没有提供命令行参数设置&#xff0c;我们常常需要多次修改代码后重新部署。本文将介绍如何通过命令行工具和编辑器查看代码特定行的方法&#xff0c;并展示如何同时训练多个基于不同…

.NET程序集编辑器/调试器 dnSpy 使用介绍

原文链接&#xff1a;https://www.cnblogs.com/zhaotianff/p/17352882.html dnSpy dnSpy是一个.NET程序集调试器和编辑器。它可以用它来编辑和调试程序集&#xff0c;即使在没有源码的情况下。 主要功能&#xff1a; 调试.NET和Unity程序集 编辑.NET和Unity程序集 项目地…

【解决方案】华普微汽车智能钥匙解决方案

一、方案概述 1.什么是被动式无钥匙进入 "被动式无钥匙进入"&#xff08;Passive Keyless Entry&#xff09;是一种用于车辆、建筑物或其他设施的访问控制系统。它利用无线射频技术自动判断用户是否接近&#xff0c;并进行身份识别以执行开锁或落锁动作&#xff0c…

【23】Android高级知识之Window(四) - ThreadedRenderer

一、概述 在上一篇文章中已经讲了setView整个流程中&#xff0c;最开始的addToDisplay和WMS跨进程通信的整个过程做了什么。继文章Android基础知识之Window(二)&#xff0c;这算是另外一个分支了&#xff0c;接着讲分析在performTraversals的三个操作中&#xff0c;最后触发pe…

基于Golang+Vue3快速搭建的博客系统

WANLI 博客系统 项目介绍 基于vue3和gin框架开发的前后端分离个人博客系统&#xff0c;包含md格式的文本编辑展示&#xff0c;点赞评论收藏&#xff0c;新闻热点&#xff0c;匿名聊天室&#xff0c;文章搜索等功能。 项目在线访问&#xff1a;http://bloggo.chat/ 或 http:/…

【Web】LitCTF 2024 题解(全)

目录 浏览器也能套娃&#xff1f; 一个....池子&#xff1f; 高亮主题(划掉)背景查看器 百万美元的诱惑 SAS - Serializing Authentication exx 浏览器也能套娃&#xff1f; 随便试一试&#xff0c;一眼ssrf file:///flag直接读本地文件 一个....池子&#xff1f; {…