赞
踩
效果图如上
文件结构如下:
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 datetime
- from werkzeug.utils import secure_filename
- app = Flask(__name__)
-
- # 全局变量:模型和模型权重路径
- model = None
-
-
-
- # 提前加载模型
- # 提前加载模型
- def load_model():
- global model
- global new_filename
- # 拼接权重文件的完整路径
- model = torch.hub.load("E:\\pythonProject2\\flaskProject\\yolov5-master", "custom", path='weight/'+new_filename, 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"
- 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.pred[0] # 这里假设只有一张输入图片
- # 在帧上绘制检测结果
- 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)
- print(f'Confidence: {conf:.2f}')
- 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") # 使用 H.264 编码器
- 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("/upload_weight", methods=["POST"])
- def upload_weight():
- global new_filename
- # 获取上传的权重文件
- weight_file = request.files["weight"]
- # 获取上传文件的原始文件名
- original_filename = secure_filename(weight_file.filename)
- # 提取文件名和扩展名
- filename, extension = os.path.splitext(original_filename)
- # 构造新的文件名,加上当前时间戳
- current_time = datetime.now().strftime("%Y%m%d%H%M%S")
- new_filename = f"best_{current_time}.pt"
- # 拼接权重文件的保存路径
- save_path = os.path.join("E:\\pythonProject2\\flaskProject\\weight\\", new_filename)
- # 保存权重文件
- weight_file.save(save_path)
- # 加载模型
- load_model()
-
- return jsonify({"message": "Weight file uploaded successfully and model loaded"})
- @app.route("/")
- def index():
-
- return render_template("index.html")
-
- 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>
- <h2>上传权重文件</h2>
- <!-- 新增按钮用于触发上传权重文件 -->
- <button onclick="document.getElementById('weightFile').click()">选择权重文件</button>
- <input type="file" id="weightFile" accept=".pt" onchange="displaySelectedWeightFile()" style="display: none;">
- <br>
- <!-- 新增模态框 -->
- <div id="myModal" class="modal" onclick="closeModal()">
- <div class="modal-content" id="modalContent" onclick="stopPropagation(event)">
- <!-- 放大后的图片或视频将在这里显示 -->
- <span class="close" onclick="closeModal()">×</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 displaySelectedWeightFile() {
- var fileInput = document.getElementById('weightFile');
- var file = fileInput.files[0];
- console.log('Selected weight file:', file);
- // 上传权重文件
- uploadWeight(file);
- }
-
- // 上传权重文件
- function uploadWeight(file) {
- var formData = new FormData();
- formData.append('weight', file);
-
- fetch('/upload_weight', {
- method: 'POST',
- body: formData
- })
- .then(response => response.json())
- .then(data => {
- console.log('Upload weight response:', data);
- // 可以根据后端返回的响应进行相应的处理
- })
- .catch(error => console.error('Error:', error));
- }
- // 显示选择的图片并添加点击放大功能
- 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("E:\\pythonProject2\\flaskProject\\yolov5-master", "custom", path='weight/'+new_filename, source="local")
注意此处加载模型路径更改为自己的
如果模型读取不到,csdn上有相关解决方法
此处的best.py上传路径也要更改成自己的
save_path = os.path.join("E:\\pythonProject2\\flaskProject\\weight\\", new_filename)
使用说明:
先上传本地的模型,上传成功后等待模型加载,再上传照片/视频
如有问题,可联系作者,随时讨论。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。