<think>我们计划使用Flask框架来构建一个简单的网页应用,实现图片上传、调用YOLO模型进行目标检测、返回检测结果(包括带标注的图片和检测结果的JSON数据)的功能。根据引用[4]和引用[5]中的代码片段,我们可以整合出一个解决方案。
步骤:
1. 创建Flask应用。
2. 设置路由:一个用于显示上传页面的路由,一个用于处理上传图片并进行目标检测的路由。
3. 在检测路由中,读取上传的图片,调用YOLO模型进行预测。
4. 解析模型的预测结果,生成带标注的图片和JSON格式的检测结果。
5. 将结果返回给用户(可以返回标注后的图片和JSON数据,或者在一个网页中同时显示图片和JSON)。
由于引用[4]提到了通过`torch.hub.load`加载YOLOv5模型,引用[5]则提供了绘制检测框和生成JSON的代码,我们将结合这两部分。
注意:我们假设已经安装了必要的库(flask, torch, opencv-python, numpy等)。
项目结构:
- app.py: 主程序
- templates/upload.html: 上传页面的模板
- static/: 存放静态文件,如上传的图片和结果图片(可选)
实现细节:
1. 模型加载:使用torch.hub加载YOLOv5模型(可以是官方模型或自定义训练模型)。
2. 图片上传:通过Flask的request.files获取上传的文件。
3. 预测:将图片传入模型进行预测。
4. 结果解析:YOLOv5模型的预测结果是一个包含检测信息的对象,我们可以将其转换为JSON格式,并绘制检测框。
5. 返回结果:我们可以生成一个结果页面,展示带标注的图片和JSON数据,或者分别提供图片和JSON的下载。
为了简化,我们将在一个路由中处理上传和检测,然后返回一个包含结果图片和JSON的页面。
代码实现:
首先,创建Flask应用,并加载模型(注意:模型加载放在全局,避免每次请求重复加载)。
然后,编写上传页面和检测处理。
由于YOLOv5模型预测返回的结果对象有一个`pandas().xyxy[0]`的方法可以方便地获取检测框信息,但为了通用性,我们也可以直接解析模型返回的tensor。不过,引用[5]中的代码是处理JSON的,而我们的模型输出需要转换成类似的JSON结构。
我们参考引用[5]中的JSON结构:
```json
{
"image_id": "filename.jpg",
"width": 1920,
"height": 1080,
"detections": [
{
"class_name": "person",
"confidence": 0.924,
"bbox": [x_center, y_center, width, height], // 注意:这里我们也可以使用[x_min, y_min, x_max, y_max]格式,但引用[5]中似乎是中心坐标和宽高,但绘制时又转换成了角点。这里我们统一使用角点坐标(x_min, y_min, x_max, y_max)或者中心坐标加宽高?需要根据模型输出确定。
"segmentation": null
}
]
}
```
YOLOv5模型默认输出的检测框信息是[x_min, y_min, x_max, y_max, confidence, class_id, class_name](通过`pandas().xyxy[0]`得到)。所以我们可以使用这种格式。
但是注意:引用[5]中的bbox格式是[x_center, y_center, width, height],而我们在绘制时又转换成了角点。为了与引用[5]的JSON结构保持一致,我们可以选择输出中心坐标和宽高,也可以选择输出角点。这里我们按照引用[5]的结构输出中心坐标和宽高,因为这样符合COCO等标准格式。
不过,在绘制检测框时,OpenCV需要的是角点坐标。因此,我们需要在绘制时进行转换,而在JSON中存储中心坐标和宽高。
但是,YOLOv5输出的坐标是角点坐标(x_min, y_min, x_max, y_max)。所以我们需要转换:
中心点x = (x_min + x_max) / 2
中心点y = (y_min + y_min) / 2
宽度 = x_max - x_min
高度 = y_max - y_min
或者,我们也可以直接在JSON中使用角点坐标,这取决于你的需求。为了通用性,我们按照引用[5]的结构使用中心坐标和宽高。
步骤:
1. 读取图片,获取图片的宽高(因为模型预测后得到的坐标是归一化的还是原图的?YOLOv5模型在推理时会自动将图片缩放到模型输入尺寸,但返回的检测框坐标会自动调整到原始图片尺寸,所以我们可以直接使用)。
2. 模型预测:`results = model(img)`
3. 解析结果:获取检测到的每个目标,提取类别名称、置信度、边界框(角点坐标),然后转换为中心坐标和宽高。
4. 绘制检测框:在原始图片上绘制矩形和标签(使用角点坐标绘制)。
5. 生成JSON数据。
6. 将绘制好的图片保存到临时文件或内存中,然后将其编码为base64以便在网页中显示,或者保存到static目录并返回URL。同时,将JSON数据传递给模板。
由于我们想要在网页中同时显示图片和JSON,我们可以将图片转换成base64编码直接嵌入到HTML中,这样就不需要保存到文件系统(当然,如果图片很大,保存到文件系统可能更好,这里为了简单,我们保存到static目录,并返回文件名)。
但是,为了简单和避免文件系统操作,我们使用base64编码图片。
具体实现:
1. 加载模型(在全局加载):
```python
import torch
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # 加载官方模型,如果要加载自定义模型,使用custom参数和路径,如:model = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/best.pt')
```
2. 编写Flask应用:
以下是app.py的代码:
```python
import os
from flask import Flask, render_template, request, jsonify
import cv2
import numpy as np
import base64
import torch
app = Flask(__name__)
# 加载模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # 使用官方模型
@app.route('/')
def upload_page():
return render_template('upload.html')
@app.route('/detect', methods=['POST'])
def detect():
if 'file' not in request.files:
return jsonify({'error': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
# 读取图片
img_bytes = file.read()
img_np = np.frombuffer(img_bytes, np.uint8)
img = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # YOLOv5需要RGB格式
# 使用模型进行预测
results = model(img_rgb)
# 解析预测结果
pred = results.pandas().xyxy[0] # 获取第一个图片的预测结果(因为我们只上传了一张图片)
detections = []
for _, row in pred.iterrows():
x_min, y_min, x_max, y_max, conf, class_id, class_name = row.values[:7]
# 计算中心坐标和宽高
width = x_max - x_min
height = y_max - y_min
x_center = (x_min + x_max) / 2
y_center = (y_min + y_max) / 2
detections.append({
"class_name": class_name,
"confidence": float(conf),
"bbox": [float(x_center), float(y_center), float(width), float(height)],
"segmentation": None
})
# 绘制检测框(使用角点坐标)
for det in detections:
x_center, y_center, width, height = det['bbox']
x_min = int(x_center - width/2)
y_min = int(y_center - height/2)
x_max = int(x_center + width/2)
y_max = int(y_center + height/2)
# 绘制矩形和标签
cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)
label = f"{det['class_name']} {det['confidence']:.2f}"
cv2.putText(img, label, (x_min, y_min - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
# 将处理后的图片转换为base64编码
_, img_encoded = cv2.imencode('.jpg', img)
img_base64 = base64.b64encode(img_encoded).decode('utf-8')
# 构建返回的JSON数据(包括检测结果和图片的base64)
result_json = {
"image_id": file.filename,
"width": img.shape[1],
"height": img.shape[0],
"detections": detections,
"count": len(detections)
}
# 同时返回图片的base64和检测结果,在模板中显示
return render_template('result.html', image_base64=img_base64, result_json=result_json)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8001, debug=True)
```
3. 创建模板文件:
在templates目录下创建upload.html和result.html。
upload.html(上传页面):
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>YOLO Object Detection</title>
</head>
<body>
<h1>Upload an image for object detection</h1>
<form action="/detect" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept="image/*">
<input type="submit" value="Detect">
</form>
</body>
</html>
```
result.html(结果页面):
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Detection Result</title>
</head>
<body>
<h1>Detection Result</h1>
<h2>Processed Image:</h2>
<img src="data:image/jpeg;base64,{{ image_base64 }}" alt="Processed Image">
<h2>Detection Results (JSON):</h2>
<pre>{{ result_json | tojson(indent=4) }}</pre>
</body>
</html>
```
注意:在result.html中,我们使用`tojson`过滤器来格式化JSON,并设置缩进。
4. 运行应用:
将上述代码保存后,运行app.py。
注意:第一次运行会下载yolov5s模型,需要一定时间。
5. 测试:
打开浏览器访问 http://localhost:8001,上传一张图片,即可看到检测结果。
这个实现满足了用户需求:上传图片,调用YOLO模型,返回标注图片和JSON结果。
注意:在实际部署时,可能需要考虑性能(如模型加载、并发处理等)和安全性(如文件上传类型检查、大小限制等)。这里为了简洁,省略了这些细节。
另外,如果使用自定义训练的模型,只需修改模型加载部分,例如:
```python
model = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/your/model.pt')
```
参考文献:
- 模型加载部分参考了引用[4]。
- 检测结果处理和绘制参考了引用[5]。
- Web UI设计参考了引用[2]和引用[3]中提到的上传图片和返回JSON结果的功能。
[^1]: 引用[1]提到了YOLO的可视化图表,但本实现未涉及,可后续扩展。
[^2]: 引用[2]展示了JSON结果格式,本实现采用了类似结构。
[^3]: 引用[3]介绍了YOLO12 WebUI,本实现为一个简化版。
[^4]: 引用[4]提供了Flask部署YOLO模型的代码片段。
[^5]: 引用[5]提供了从图片上传到结果展示的完整代码流程,本实现参考了其JSON结构和绘图方法。
§§相关问题§§
1. 如何在使用Flask部署YOLO模型时提高并发性能?
2. 如何自定义YOLO模型(使用自己训练好的模型)进行目标检测?
3. 除了返回检测框,如何同时返回分割结果(如果模型支持)?
4. 如何限制上传图片的大小和类型?
5. 如何将检测结果保存到数据库?
</think>
### 使用Flask设计YOLO图片检测网页
我将基于引用内容设计一个完整的解决方案,使用Flask框架实现图片上传、YOLO模型调用和结果展示功能。
#### 系统架构设计
```mermaid
graph LR
A[用户浏览器] --> B[上传图片]
B --> C[Flask服务器]
C --> D[YOLO模型推理]
D --> E[结果处理]
E --> F[生成标注图片]
E --> G[生成JSON数据]
F --> C
G --> C
C --> H[返回结果页面]
```
#### 代码实现步骤
1. **创建Flask应用(app.py)**
```python
from flask import Flask, render_template, request, jsonify
import cv2
import numpy as np
import torch
import json
import os
app = Flask(__name__)
UPLOAD_FOLDER = 'static/uploads'
RESULT_FOLDER = 'static/results'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['RESULT_FOLDER'] = RESULT_FOLDER
# 加载YOLO模型(根据引用[4])
def load_model(model_path="models/yolov5s.pt"):
return torch.hub.load("yolo", "custom", path=model_path, source='local')
model = load_model()
@app.route('/')
def index():
return render_template('upload.html')
@app.route('/detect', methods=['POST'])
def detect():
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
# 保存上传图片
filename = file.filename
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
# 执行检测(根据引用[5])
img = cv2.imread(filepath)
results = model(img)
# 解析结果并生成JSON(根据引用[2])
detections = []
for *box, conf, cls in results.xyxy[0]:
class_name = model.names[int(cls)]
x1, y1, x2, y2 = map(float, box)
detections.append({
"class_name": class_name,
"confidence": float(conf),
"bbox": [x1, y1, x2, y2],
"segmentation": None
})
result_json = {
"image_id": filename,
"width": img.shape[1],
"height": img.shape[0],
"detections": detections,
"count": len(detections)
}
# 绘制检测结果(根据引用[5])
for det in detections:
x1, y1, x2, y2 = det["bbox"]
label = f"{det['class_name']} {det['confidence']:.2f}"
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
cv2.putText(img, label, (int(x1), int(y1)-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
# 保存结果图
result_img_path = os.path.join(app.config['RESULT_FOLDER'], f"result_{filename}")
cv2.imwrite(result_img_path, img)
return render_template('result.html',
original=filepath,
result_img=result_img_path,
result_json=json.dumps(result_json, indent=2))
if __name__ == '__main__':
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(RESULT_FOLDER, exist_ok=True)
app.run(host='0.0.0.0', port=8001, debug=True)
```
2. **创建上传页面(templates/upload.html)**
```html
<!DOCTYPE html>
<html>
<head>
<title>YOLO图像检测</title>
<style>
.container { max-width: 800px; margin: 0 auto; padding: 20px; }
.upload-box { border: 2px dashed #ccc; padding: 30px; text-align: center; }
.btn { background: #4CAF50; color: white; padding: 10px 20px; border: none; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>YOLO目标检测系统</h1>
<p>上传图片进行目标检测(支持JPG/PNG格式)</p>
<form action="/detect" method="post" enctype="multipart/form-data" class="upload-box">
<input type="file" name="file" accept="image/*" required>
<br><br>
<button type="submit" class="btn">开始检测</button>
</form>
<p>技术说明:使用YOLOv5模型实现实时目标检测[^3]</p>
</div>
</body>
</html>
```
3. **创建结果页面(templates/result.html)**
```html
<!DOCTYPE html>
<html>
<head>
<title>检测结果</title>
<style>
.container { max-width: 1200px; margin: 0 auto; }
.result-container { display: flex; margin-top: 20px; }
.image-box, .json-box { flex: 1; padding: 10px; }
.original-img, .result-img { max-width: 100%; border: 1px solid #ddd; }
pre { background: #f4f4f4; padding: 15px; overflow: auto; }
</style>
</head>
<body>
<div class="container">
<h1>检测结果</h1>
<a href="/">← 返回上传</a>
<div class="result-container">
<div class="image-box">
<h2>原始图片</h2>
<img src="{{ original }}" class="original-img">
<h2>检测结果</h2>
<img src="{{ result_img }}" class="result-img">
</div>
<div class="json-box">
<h2>JSON结果</h2>
<pre>{{ result_json }}</pre>
<button onclick="copyJSON()">复制JSON</button>
</div>
</div>
</div>
<script>
function copyJSON() {
const el = document.createElement('textarea');
el.value = document.querySelector('pre').innerText;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
alert('JSON已复制到剪贴板!');
}
</script>
</body>
</html>
```
#### 系统部署步骤
1. 安装依赖:
```bash
pip install flask torch opencv-python numpy
```
2. 获取YOLOv5模型:
```bash
git clone https://github.com/ultralytics/yolov5
cd yolov5
pip install -r requirements.txt
```
3. 创建项目结构:
```
project/
├── app.py
├── templates/
│ ├── upload.html
│ └── result.html
├── static/
│ ├── uploads/
│ └── results/
└── yolo/ # yolov5仓库
```
4. 运行应用:
```bash
python app.py
```
#### 功能特点
1. **完整工作流程**:
- 图片上传 → YOLO检测 → 可视化标注 → JSON生成(参考引用[2][5])
- 支持实时显示检测框和类别置信度
2. **结果展示**:
- 并排显示原始图片和标注结果
- 格式化JSON输出(参考引用[2]结构)
- 一键复制JSON功能
3. **模型支持**:
- 默认使用YOLOv5s预训练模型
- 支持自定义训练模型(修改`model_path`参数)
- 可扩展支持YOLOv8等新版模型[^3]
#### 性能优化建议
1. 使用`torch.jit`编译模型加速推理
2. 添加图片尺寸限制(最大1920×1080)
3. 实现异步任务队列(Celery)处理高并发
4. 添加GPU加速支持(`model.cuda()`)
[^1]: 可视化结果保存在`static/results/`目录,支持后续分析
[^2]: JSON结果格式符合标准检测输出规范
[^3]: 基于YOLO系列最新架构实现高效检测
[^4]: 使用`torch.hub.load`加载本地模型确保部署安全
[^5]: 检测流程参考工业级实现方案