Spaces:
Running
Running
Upload 3 files
Browse files- best.pt +3 -0
- lajifenlei.yaml +103 -0
- yemian.py +516 -0
best.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:49331b0753c63458c0b672f769f79f0b12ae375ce6aae240e0543d74a4892b83
|
| 3 |
+
size 5645658
|
lajifenlei.yaml
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 垃圾分类数据集配置文件(YOLOv8 标准格式)
|
| 2 |
+
# 说明:配置文件用于定义数据集路径、类别信息及扩展映射,训练时YOLOv8仅读取path、train、val、nc、names字段
|
| 3 |
+
|
| 4 |
+
# 数据集根目录(建议使用绝对路径,避免因运行目录变化导致路径错误;若路径含空格,可加英文引号包裹)
|
| 5 |
+
path: D:/Study/ultralytics-main/datasets
|
| 6 |
+
|
| 7 |
+
# 图像路径(相对于path的相对路径,支持文件夹下所有图像文件,格式需统一为jpg/png等)
|
| 8 |
+
train: images/train # 训练集图像文件夹
|
| 9 |
+
val: images/val # 验证集图像文件夹
|
| 10 |
+
test: # 测试集图像路径(可选,若无需测试可保持空值或删除此行)
|
| 11 |
+
|
| 12 |
+
# 核心配置(YOLOv8训练必填)
|
| 13 |
+
nc: 40 # 类别总数(共40个小类,与names列表长度一致)
|
| 14 |
+
names: # 小类名称列表(顺序必须与标签文件中的类别索引严格对应,0-39连续无重复)
|
| 15 |
+
0: 污损塑料
|
| 16 |
+
1: 烟蒂
|
| 17 |
+
2: 牙签
|
| 18 |
+
3: 破碎花盆及碟碗
|
| 19 |
+
4: 竹筷
|
| 20 |
+
5: 剩饭剩菜 # 原6号类别前移
|
| 21 |
+
6: 大骨头 # 原7号类别前移
|
| 22 |
+
7: 水果果皮 # 原8号类别前移
|
| 23 |
+
8: 水果果肉 # 原9号类别前移
|
| 24 |
+
9: 茶叶渣 # 原10号类别前移
|
| 25 |
+
10: 菜叶菜根 # 原11号类别前移
|
| 26 |
+
11: 蛋壳 # 原12号类别前移
|
| 27 |
+
12: 鱼骨 # 原13号类别前移
|
| 28 |
+
13: 充电宝 # 原14号类别前移
|
| 29 |
+
14: 包 # 原15号类别前移
|
| 30 |
+
15: 化妆品瓶 # 原16号类别前移
|
| 31 |
+
16: 塑料玩具 # 原17号类别前移
|
| 32 |
+
17: 塑料碗盆 # 原18号类别前移
|
| 33 |
+
18: 塑料衣架 # 原19号类别前移
|
| 34 |
+
19: 快递纸袋 # 原20号类别前移
|
| 35 |
+
20: 插头电线 # 原21号类别前移
|
| 36 |
+
21: 旧衣服 # 原22号类别前移
|
| 37 |
+
22: 易拉罐 # 原23号类别前移
|
| 38 |
+
23: 枕头 # 原24号类别前移
|
| 39 |
+
24: 毛绒玩具 # 原25号类别前移
|
| 40 |
+
25: 洗发水瓶 # 原26号类别前移
|
| 41 |
+
26: 玻璃杯 # 原27号类别前移
|
| 42 |
+
27: 皮鞋 # 原28号类别前移
|
| 43 |
+
28: 砧板 # 原29号类别前移
|
| 44 |
+
29: 纸板箱 # 原30号类别前移
|
| 45 |
+
30: 调料瓶 # 原31号类别前移
|
| 46 |
+
31: 酒瓶 # 原32号类别前移
|
| 47 |
+
32: 金属食品罐 # 原33号类别前移
|
| 48 |
+
33: 锅 # 原34号类别前移
|
| 49 |
+
34: 食用油桶 # 原35号类别前移
|
| 50 |
+
35: 饮料瓶 # 原36号类别前移
|
| 51 |
+
36: 干电池 # 原37号类别前移
|
| 52 |
+
37: 软膏 # 原38号类别前移
|
| 53 |
+
38: 过期药物 # 原39号类别前移
|
| 54 |
+
39: 一次性快餐盒 # 调整后固定在39位
|
| 55 |
+
|
| 56 |
+
# 扩展配置(训练不依赖,用于推理时小类→大类转换,方便结果展示)
|
| 57 |
+
big_category_mapping: # 小类索引→大类索引(0=其他垃圾,1=厨余垃圾,2=可回收物,3=有害垃圾)
|
| 58 |
+
0: 0 # 污损塑料 → 其他垃圾
|
| 59 |
+
1: 0 # 烟蒂 → 其他垃圾
|
| 60 |
+
2: 0 # 牙签 → 其他垃圾
|
| 61 |
+
3: 0 # 破碎花盆及碟碗 → 其他垃圾
|
| 62 |
+
4: 0 # 竹筷 → 其他垃圾
|
| 63 |
+
5: 1 # 剩饭剩菜 → 厨余垃圾(原6号映射同步前移)
|
| 64 |
+
6: 1 # 大骨头 → 厨余垃圾(原7号映射同步前移)
|
| 65 |
+
7: 1 # 水果果皮 → 厨余垃圾(原8号映射同步前移)
|
| 66 |
+
8: 1 # 水果果肉 → 厨余垃圾(原9号映射同步前移)
|
| 67 |
+
9: 1 # 茶叶渣 → 厨余垃圾(原10号映射同步前移)
|
| 68 |
+
10: 1 # 菜叶菜根 → 厨余垃圾(原11号映射同步前移)
|
| 69 |
+
11: 1 # 蛋壳 → 厨余垃圾(原12号映射同步前移)
|
| 70 |
+
12: 1 # 鱼骨 → 厨余垃圾(原13号映射同步前移)
|
| 71 |
+
13: 2 # 充电宝 → 可回收物(原14号映射同步前移)
|
| 72 |
+
14: 2 # 包 → 可回收物(原15号映射同步前移)
|
| 73 |
+
15: 2 # 化妆品瓶 → 可回收物(原16号映射同步前移)
|
| 74 |
+
16: 2 # 塑料玩具 → 可回收物(原17号映射同步前移)
|
| 75 |
+
17: 2 # 塑料碗盆 → 可回收物(原18号映射同步前移)
|
| 76 |
+
18: 2 # 塑料衣架 → 可回收物(原19号映射同步前移)
|
| 77 |
+
19: 2 # 快递纸袋 → 可回收物(原20号映射同步前移)
|
| 78 |
+
20: 2 # 插头电线 → 可回收物(原21号映射同步前移)
|
| 79 |
+
21: 2 # 旧衣服 → 可回收物(原22号映射同步前移)
|
| 80 |
+
22: 2 # 易拉罐 → 可回收物(原23号映射同步前移)
|
| 81 |
+
23: 2 # 枕头 → 可回收物(原24号映射同步前移)
|
| 82 |
+
24: 2 # 毛绒玩具 → 可回收物(原25号映射同步前移)
|
| 83 |
+
25: 2 # 洗发水瓶 → 可回收物(原26号映射同步前移)
|
| 84 |
+
26: 2 # 玻璃杯 → 可回收物(原27号映射同步前移)
|
| 85 |
+
27: 2 # 皮鞋 → 可回收物(原28号映射同步前移)
|
| 86 |
+
28: 2 # 砧板 → 可回收物(原29号映射同步前移)
|
| 87 |
+
29: 2 # 纸板箱 → 可回收物(原30号映射同步前移)
|
| 88 |
+
30: 2 # 调料瓶 → 可回收物(原31号映射同步前移)
|
| 89 |
+
31: 2 # 酒瓶 → 可回收物(原32号映射同步前移)
|
| 90 |
+
32: 2 # 金属食品罐 → 可回收物(原33号映射同步前移)
|
| 91 |
+
33: 2 # 锅 → 可回收物(原34号映射同步前移)
|
| 92 |
+
34: 2 # 食用油桶 → 可回收物(原35号映射同步前移)
|
| 93 |
+
35: 2 # 饮料瓶 → 可回收物(原36号映射同步前移)
|
| 94 |
+
36: 3 # 干电池 → 有害垃圾(原37号映射同步前移)
|
| 95 |
+
37: 3 # 软膏 → 有害垃圾(原38号映射同步前移)
|
| 96 |
+
38: 3 # 过期药物 → 有害垃圾(原39号映射同步前移)
|
| 97 |
+
39: 0 # 一次性快餐盒 → 其他垃圾(原5号映射移至39位)
|
| 98 |
+
|
| 99 |
+
big_category_names: # 大类索引→名称(与big_category_mapping对应,用于结果展示)
|
| 100 |
+
0: 其他垃圾
|
| 101 |
+
1: 厨余垃圾
|
| 102 |
+
2: 可回收物
|
| 103 |
+
3: 有害垃圾
|
yemian.py
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tempfile
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import yaml
|
| 5 |
+
import numpy as np
|
| 6 |
+
from ultralytics import YOLO
|
| 7 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 8 |
+
import os
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
import cv2
|
| 11 |
+
|
| 12 |
+
# === 新增:详细垃圾分类介绍字典(已按要求详细扩展) ===
|
| 13 |
+
GARBAGE_INTRODUCTION = {
|
| 14 |
+
# 其他垃圾(大类0)
|
| 15 |
+
"其他垃圾/污损塑料": "其他垃圾是无法回收且无害的废弃物,长期堆积会占用土地资源。污损塑料指被污染、破损或难以清洁的塑料制品,如沾有油污的塑料袋、食物残渣的塑料容器等。其具体作用是临时包装或盛放物品,但一旦被污染,失去再利用价值。可回收性极低,因为污染会破坏塑料的再利用价值。危害性较低,但填埋占用土地,焚烧可能释放有害气体,长期影响环境质量。对环境影响:填埋占用土地并产生甲烷,对环境有轻微但持续的危害;长期积累可能释放微塑料,影响土壤和水体质量。对人类和动物影响:直接危害较小,但可能通过食物链间接影响健康。",
|
| 16 |
+
|
| 17 |
+
"其他垃圾/烟蒂": "其他垃圾无回收价值,随意丢弃易造成视觉污染。烟蒂是香烟过滤嘴,主要成分是醋酸纤维素,含有尼古丁、焦油等化学物质。其具体作用是过滤吸烟时的有害物质,但本身是不可回收的垃圾。可回收性极低,因为含有化学物质且难以清洁。危害性较高,含有多种有害化学物质,对环境和健康都有潜在危害。对环境影响:烟蒂含尼古丁、焦油等化学物质,可能释放有害物质污染土壤和水体。对人类影响:可能引起火灾,对动物影响:可能造成误食中毒。",
|
| 18 |
+
|
| 19 |
+
"其他垃圾/牙签": "其他垃圾难以回收利用,过量堆积会增加处理压力。牙签多为木质或塑料材质,一次性使用后难以回收。其具体作用是清洁牙齿缝隙,但使用后无法再利用。可回收性低,木质牙签难以回收,塑料牙签也因污染和材质问题难以回收。危害性较低,但可能影响土壤结构,塑料牙签被动物误食可能造成伤害。对环境影响:木质牙签可能影响土壤结构,塑料牙签可能被动物误食造成伤害。对人类影响:可能造成轻微割伤。",
|
| 20 |
+
|
| 21 |
+
"其他垃圾/破碎花盆及碟碗": "其他垃圾回收价值极低,破碎陶瓷易划伤设备。破碎花盆及碟碗是陶瓷制品的废弃物,主要用途是盛放物品或作为装饰。其具体作用是日常使用,但破损后失去功能。可回收性极低,陶瓷制品通常无法回收。危害性较低,但可能造成割伤,填埋占用空间。对环境影响:破碎陶瓷制品难以降解,可能划伤土壤表面,影响土壤微生物活动。对人类和动物影响:可能造成割伤。",
|
| 22 |
+
|
| 23 |
+
"其他垃圾/竹筷": "其他垃圾无法循环利用,易污染且处理成本高。竹筷是一次性竹制品,用于进餐的工具。其具体作用是辅助进食,但使用后无法再利用。可回收性低,竹筷虽然可生物降解,但因污染和材质问题通常不被视为可回收物。危害性较低,但填埋可能影响土壤。对环境影响:竹筷填埋需要较长时间分解,可能影响土壤结构。对人类和动物影响:危害较小,但大量填埋可能影响土壤质量。",
|
| 24 |
+
|
| 25 |
+
"其他垃圾/一次性快餐盒": "其他垃圾难以清洁再生,易造成白色污染。一次性快餐盒是用于盛放快餐的容器,通常为塑料制成。其具体作用是盛放快餐。可回收性低,因为塑料快餐盒通常难以清洁和回收。危害性较低,但填埋占用空间,可能释放微塑料。对环境影响:一次性快餐盒多为塑料,填埋难降解,可能释放微塑料。对人类和动物影响:可能被动物误食。",
|
| 26 |
+
|
| 27 |
+
# 厨余垃圾(大类1)
|
| 28 |
+
"厨余垃圾/剩饭剩菜": "厨余垃圾是易腐烂生物质废弃物,不及时处理易发霉发臭污染环境。剩饭剩菜可通过堆肥或生物降解制有机肥,回收价值较高。投放前需沥干汤汁,挑出非厨余杂物。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生大量渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地,吸引害虫。",
|
| 29 |
+
|
| 30 |
+
"厨余垃圾/大骨头": "厨余垃圾可生物降解,随意丢弃易滋生细菌。大骨头是食物残渣,主要来自肉类食品。其具体作用是提供营养,但食用后成为废弃物。可回收性低,因为大骨头难以降解,不适合常规堆肥。危害性低,但可能影响厨余垃圾处理。对环境影响:大骨头难以堆肥,填埋时可能影响堆肥质量,产生渗滤液。对人类和动物影响:对人类危害小,但可能影响厨余垃圾处理效率。",
|
| 31 |
+
|
| 32 |
+
"厨余垃圾/水果果皮": "厨余垃圾易腐烂可循环,是优质堆肥原料,不分类会浪费资源。水果果皮可堆肥转化为有机肥料,回收价值高。投放时无需去除果蒂,大块可撕碎。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 33 |
+
|
| 34 |
+
"厨余垃圾/水果果肉": "厨余垃圾富含有机物,不处理易变质污染土壤水源。水果果肉可通过生物处理制沼气或有机肥,回收价值高。变质果肉仍属厨余垃圾,及时投放。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 35 |
+
|
| 36 |
+
"厨余垃圾/茶叶渣": "厨余垃圾可自然降解,是环保的生物质资源。茶叶渣富含纤维和矿物质,处理方式为堆肥或作为植物肥料,回收价值中等。无需清洗直接投放。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 37 |
+
|
| 38 |
+
"厨余垃圾/菜叶菜根": "厨余垃圾易分解可循环,不分类会增加填埋压力。菜叶菜根可堆肥制有机肥,回收价值高。投放前去除塑料绳等杂物,沥干水分。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 39 |
+
|
| 40 |
+
"厨余垃圾/蛋壳": "厨余垃圾可生物降解,能调节堆肥酸碱度。蛋壳主要成分为碳酸钙,处理方式为破碎后堆肥,回收价值中等。无需清洗,直接破碎投放。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 41 |
+
|
| 42 |
+
"厨余垃圾/鱼骨": "厨余垃圾易腐烂,是生物处理的优质原料。鱼骨(尤其是小鱼骨)质地较软,处理方式为堆肥或生物降解,回收价值高。与其他厨余垃圾一同投放即可。对环境影响:可生物降解,可堆肥处理,减少填埋污染。对人类和动物影响:若未分类,填埋会产生渗滤液和甲烷,污染水土环境,对人类可能传播疾病,对动物可能破坏栖息地。",
|
| 43 |
+
|
| 44 |
+
# 可回收物(大类2)
|
| 45 |
+
"可回收物/充电宝": "可回收物能循环利用,减少资源开采和环境污染。充电宝含锂、铜等可回收成分,处理方式为专业拆解回收,回收价值较高。投放时保持干燥,不拆解。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋浪费资源,焚烧可能释放有害气体,对环境有轻微危害,对人类有益,减少环境污染,对动物可能造成电池泄漏污染。",
|
| 46 |
+
|
| 47 |
+
"可回收物/包": "可回收物循环利用可节约原材料,降低碳排放。包的皮革、帆布等材质可再生加工,回收价值中等。投放前清理内部杂物,破损严重仍可回收。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 48 |
+
|
| 49 |
+
"可回收物/化妆品瓶": "可回收物再生能减少塑料/玻璃消耗,保护资源。化妆品瓶(玻璃/塑料材质)可重新加工为容器,回收价值较高。投放前清空冲洗,取下非同类部件。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 50 |
+
|
| 51 |
+
"可回收物/塑料玩具": "可回收物循环利用可降低塑料污染,节约石油资源。塑料玩具多为PP/PE材质,处理方式为熔融再生,回收价值中等。投放前去除电池,清洗污渍。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 52 |
+
|
| 53 |
+
"可回收物/塑料碗盆": "可回收物再生能减少白色污染,提升资源利用率。塑料碗盆(PP/PE材质)可熔融重塑,回收价值中等。破损严重的按其他垃圾投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 54 |
+
|
| 55 |
+
"可回收物/塑料衣架": "可回收物循环利用���节约塑料原料,减少环境压力。塑料衣架可熔融再生为新塑料制品,回收价值较低。无需拆解,直接放入可回收物箱。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 56 |
+
|
| 57 |
+
"可回收物/快递纸袋": "可回收物再生能节约木材资源,减少森林砍伐。快递纸袋可重新制成再生纸,回收价值较高。投放前去除胶带,折叠压平,潮湿的需晾干。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 58 |
+
|
| 59 |
+
"可回收物/插头电线": "可回收物回收能节约金属资源,减少矿产开采。插头电线含铜、铝等金属,处理方式为拆解回收金属,回收价值较高。盘绕整齐后投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类可致电击风险,对动物可能造成中毒。",
|
| 60 |
+
|
| 61 |
+
"可回收物/旧衣服": "可回收物再利用能减少纺织业污染,节约能源。旧衣服可捐赠、纤维化再生,回收价值中等。投放前清洗打包,破损的仍可回收。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 62 |
+
|
| 63 |
+
"可回收物/易拉罐": "可回收物再生能耗仅为原生产的5%,节约能源效果显著。易拉罐(铝/铁材质)可100%再生,回收价值高。冲洗干净,压扁投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 64 |
+
|
| 65 |
+
"可回收物/枕头": "可回收物再利用能减少资源浪费,降低填埋压力。枕头的布料、填充物可再生加工,回收价值较低。投放前拆解分类,破损的可整体投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 66 |
+
|
| 67 |
+
"可回收物/毛绒玩具": "可回收物再生能减少塑料和纺织废料污染。毛绒玩具的布料、PP棉可回收,处理方式为分类再生,回收价值较低。去除电池,清洗后投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 68 |
+
|
| 69 |
+
"可回收物/洗发水瓶": "可回收物再生能减少塑料垃圾,节约石油资源。洗发水瓶(PET材质)可制成纤维、塑料管材,回收价值中等。清空冲洗,泵头可一同投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 70 |
+
|
| 71 |
+
"可回收物/玻璃杯": "可回收物可无限循环利用,再生无环境污染。玻璃杯再生能节约石英砂等原料,回收价值较高。避免破碎,破损的需包裹投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 72 |
+
|
| 73 |
+
"可回收物/皮鞋": "可回收物再生能减少皮革和橡胶浪费。皮鞋的皮革、橡胶鞋底可加工为再生制品,回收价值较低。清理鞋内杂物后投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 74 |
+
|
| 75 |
+
"可回收物/砧板": "可回收物再生能提升资源利用率,减少木材/塑料消耗。木质砧板可作生物质原料,塑料砧板可熔融再生,回收价值中等。清洗干净后投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 76 |
+
|
| 77 |
+
"可回收物/纸板箱": "可回收物再生能节约木材,减少造纸污染。纸板箱可制成新纸箱或纸浆制品,回收价值较高。拆除胶带,折叠压平,潮湿的需晾干。对环境影响:可回收再利用,节约资源,减少污��。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 78 |
+
|
| 79 |
+
"可回收物/调料瓶": "可回收物再生能减少玻璃/塑料垃圾,保护资源。调料瓶(玻璃/塑料材质)可重新利用,回收价值中等。清空冲洗,金属盖可一同投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 80 |
+
|
| 81 |
+
"可回收物/酒瓶": "可回收物再生能节约玻璃原料,降低能耗。酒瓶(尤其是无色玻璃)回收价值高,可循环制成新玻璃制品。冲洗干净,去除纸质标签。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 82 |
+
|
| 83 |
+
"可回收物/金属食品罐": "可回收物再生能减少矿产开采,节约能源。金属食品罐(铁/铝材质)再生利用率超90%,回收价值高。冲洗干净,压扁投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 84 |
+
|
| 85 |
+
"可回收物/锅": "可回收物再生能节约金属资源,减少冶炼污染。铁锅、铝锅等金属锅具可回炉重炼,回收价值较高。无需修复,直接投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 86 |
+
|
| 87 |
+
"可回收物/食用油桶": "可回收物再生能减少塑料污染,提升资源循环率。食用油桶(PET/HDPE材质)可制成非食品接触类产品,回收价值中等。彻底清洗后投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 88 |
+
|
| 89 |
+
"可回收物/饮料瓶": "可回收物再生能大幅减少塑料污染,节约资源。饮料瓶(PET材质)回收体系成熟,可制成纤维、容器等,回收价值高。清空冲洗,压扁投放。对环境影响:可回收再利用,节约资源,减少污染。对人类和动物影响:若未回收,填埋占用土地,焚烧可能释放有害气体,对环境有轻微危害,对人类和动物的危害较小。",
|
| 90 |
+
|
| 91 |
+
# 有害垃圾(大类3)
|
| 92 |
+
"有害垃圾/干电池": "有害垃圾含有毒物质,随意丢弃会污染土壤和水源。干电池含汞、镉等重金属,属于有害垃圾。其具体作用是提供电力,但含有有害物质。可回收性低,因为含有有害物质,需特殊处理。危害性高,含有重金属等有害物质。对环境影响:含剧毒物质,如重金属、化学品,污染环境和健康。对人类和动物影响:对人类可致病或中毒,对动物可致死。",
|
| 93 |
+
|
| 94 |
+
"有害垃圾/软膏": "有害垃圾含化学物质,会危害生态环境和人体健康。过期软膏含残留化学成分,属于有害垃圾。其具体作用是治疗皮肤问题,但含有有害成分。可回收性低,因为含有药物成分,需特殊处理。危害性高,含有化学成分。对环境影响:含剧毒物质,如重金属、化学品,污染环境和健康。对人类和动物影响:对人类可致病或中毒,对动物可致死。",
|
| 95 |
+
|
| 96 |
+
"有害垃圾/过期药物": "有害垃圾含变质成分,随意丢弃会污染土壤和水源,危害生物。过期药物需专业无害化处理,属于有害垃圾。其具体作用是治疗疾病,但过期后失去药效。可回收性低,因为含有化学成分,需特殊处理。危害性高,含有化学成分。对环境影响:含剧毒物质,如重金属、化学品,污染环境和健康。对人类和动物影响:对人类可致病或中毒,对动物可致死。"
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
# 固定模型和配置文件路径
|
| 100 |
+
MODEL_PATH = r"best.pt"
|
| 101 |
+
YAML_PATH = r"lajifenlei.yaml"
|
| 102 |
+
|
| 103 |
+
# 全局变量:模型和标签映射
|
| 104 |
+
model = None
|
| 105 |
+
big_category_mapping = None
|
| 106 |
+
big_category_names = None
|
| 107 |
+
small_category_names = None
|
| 108 |
+
|
| 109 |
+
# 历史记录存储
|
| 110 |
+
history_messages = []
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def init_model():
|
| 114 |
+
"""初始化模型和配置(页面加载时执行)"""
|
| 115 |
+
global model, big_category_mapping, big_category_names, small_category_names
|
| 116 |
+
try:
|
| 117 |
+
if not os.path.exists(MODEL_PATH):
|
| 118 |
+
return f"❌ 模型文件不存在:\n{MODEL_PATH}"
|
| 119 |
+
if not os.path.exists(YAML_PATH):
|
| 120 |
+
return f"❌ 配置文件不存在:\n{YAML_PATH}"
|
| 121 |
+
|
| 122 |
+
model = YOLO(MODEL_PATH)
|
| 123 |
+
with open(YAML_PATH, "r", encoding="utf-8") as f:
|
| 124 |
+
cfg = yaml.safe_load(f)
|
| 125 |
+
big_category_mapping = cfg["big_category_mapping"]
|
| 126 |
+
big_category_names = cfg["big_category_names"]
|
| 127 |
+
small_category_names = cfg["names"]
|
| 128 |
+
return "✅ 模型加载成功,点击「开始检测」启用摄像头检测"
|
| 129 |
+
except Exception as e:
|
| 130 |
+
return f"❌ 初始化失败:{str(e)}"
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def get_ai_introduction(big_name, small_name):
|
| 134 |
+
"""获取垃圾分类详细介绍(已替换为本地字典)"""
|
| 135 |
+
key = f"{big_name}/{small_name}"
|
| 136 |
+
intro = GARBAGE_INTRODUCTION.get(key, "未找到该垃圾类别介绍")
|
| 137 |
+
|
| 138 |
+
# 为保持与原格式兼容,返回两个部分(实际介绍已包含完整信息)
|
| 139 |
+
return f"{intro}/"
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def process_frame(frame, is_detecting, last_detected_label, is_mirrored):
|
| 143 |
+
"""处理帧函数,新增镜像参数"""
|
| 144 |
+
global model, big_category_mapping, big_category_names, small_category_names
|
| 145 |
+
|
| 146 |
+
current_introduction = "暂无检测物体"
|
| 147 |
+
current_label = last_detected_label
|
| 148 |
+
|
| 149 |
+
if model is None or frame is None:
|
| 150 |
+
return frame, current_introduction, current_label, ""
|
| 151 |
+
|
| 152 |
+
try:
|
| 153 |
+
# 如果需要镜像,先翻转画面
|
| 154 |
+
if is_mirrored:
|
| 155 |
+
frame = cv2.flip(frame, 1) # 水平翻转
|
| 156 |
+
|
| 157 |
+
# 格式转换:RGB(Gradio输入)→ BGR(模型处理)
|
| 158 |
+
frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
| 159 |
+
# 模型推理
|
| 160 |
+
results = model(frame_bgr, conf=0.5, imgsz=640)
|
| 161 |
+
|
| 162 |
+
# 绘制检测结果
|
| 163 |
+
pil_img = Image.fromarray(cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB))
|
| 164 |
+
draw = ImageDraw.Draw(pil_img)
|
| 165 |
+
try:
|
| 166 |
+
font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 20)
|
| 167 |
+
except:
|
| 168 |
+
font = ImageFont.load_default()
|
| 169 |
+
|
| 170 |
+
detected_objects = []
|
| 171 |
+
|
| 172 |
+
for result in results:
|
| 173 |
+
for box in result.boxes:
|
| 174 |
+
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
| 175 |
+
conf = float(box.conf[0].cpu().numpy())
|
| 176 |
+
small_idx = int(box.cls[0].cpu().numpy())
|
| 177 |
+
big_idx = big_category_mapping.get(small_idx, -1)
|
| 178 |
+
small_name = small_category_names.get(small_idx, "未知小类")
|
| 179 |
+
big_name = big_category_names.get(big_idx, "未知大类") if big_idx != -1 else "未知大类"
|
| 180 |
+
|
| 181 |
+
# 记录检测到的物体
|
| 182 |
+
label = f"{big_name}/{small_name}"
|
| 183 |
+
detected_objects.append({
|
| 184 |
+
"big_name": big_name,
|
| 185 |
+
"small_name": small_name,
|
| 186 |
+
"label": label,
|
| 187 |
+
"confidence": conf
|
| 188 |
+
})
|
| 189 |
+
|
| 190 |
+
# 绘制边界框
|
| 191 |
+
draw.rectangle([(x1, y1), (x2, y2)], outline=(0, 255, 0), width=3)
|
| 192 |
+
|
| 193 |
+
# 绘制标签背景
|
| 194 |
+
display_label = f"{big_name}/{small_name} {conf:.2f}"
|
| 195 |
+
text_bbox = draw.textbbox((x1, y1 - 25), display_label, font=font)
|
| 196 |
+
draw.rectangle(text_bbox, fill=(0, 255, 0))
|
| 197 |
+
|
| 198 |
+
# 绘制标签文字
|
| 199 |
+
draw.text((x1, y1 - 25), display_label, font=font, fill=(0, 0, 0))
|
| 200 |
+
|
| 201 |
+
# 如果有检测到的物体,获取第一个物体的AI介绍
|
| 202 |
+
if detected_objects and is_detecting:
|
| 203 |
+
# 按置信度排序,选择最可信的物体
|
| 204 |
+
detected_objects.sort(key=lambda x: x["confidence"], reverse=True)
|
| 205 |
+
best_object = detected_objects[0]
|
| 206 |
+
current_label = best_object["label"]
|
| 207 |
+
|
| 208 |
+
# 只有当标签变化时才重新获取介绍
|
| 209 |
+
if current_label != last_detected_label:
|
| 210 |
+
current_introduction = get_ai_introduction(best_object["big_name"], best_object["small_name"])
|
| 211 |
+
# 添加到历史记录
|
| 212 |
+
history_messages.append(f"检测到: {best_object['label']}\n{current_introduction}\n")
|
| 213 |
+
else:
|
| 214 |
+
current_introduction = "正在获取介绍..." # 保持原有介绍
|
| 215 |
+
else:
|
| 216 |
+
current_introduction = "暂无检测物体" if is_detecting else "检测已暂停"
|
| 217 |
+
current_label = ""
|
| 218 |
+
|
| 219 |
+
# 生成历史记录文本
|
| 220 |
+
history_text = "\n".join(history_messages[-10:]) # 只保留最近10条
|
| 221 |
+
|
| 222 |
+
return np.array(pil_img), current_introduction, current_label, history_text
|
| 223 |
+
|
| 224 |
+
except Exception as e:
|
| 225 |
+
print(f"处理帧时出错: {e}")
|
| 226 |
+
return frame, f"处理错误: {str(e)}", "", ""
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def process_image(image, is_mirrored):
|
| 230 |
+
"""处理上传的图片,支持镜像设置"""
|
| 231 |
+
if image is None:
|
| 232 |
+
return None, "请上传图片", ""
|
| 233 |
+
|
| 234 |
+
try:
|
| 235 |
+
# 转换图像格式
|
| 236 |
+
if isinstance(image, str):
|
| 237 |
+
# 如果是文件路径
|
| 238 |
+
image = cv2.imread(image)
|
| 239 |
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 240 |
+
else:
|
| 241 |
+
# 如果是numpy数组
|
| 242 |
+
image = np.array(image)
|
| 243 |
+
|
| 244 |
+
# 应用镜像
|
| 245 |
+
if is_mirrored:
|
| 246 |
+
image = cv2.flip(image, 1)
|
| 247 |
+
|
| 248 |
+
# 处理图像
|
| 249 |
+
processed_image, introduction, _, history_text = process_frame(image, True, "", is_mirrored)
|
| 250 |
+
|
| 251 |
+
return processed_image, introduction, history_text
|
| 252 |
+
except Exception as e:
|
| 253 |
+
print(f"处理图片时出错: {e}")
|
| 254 |
+
return image, f"处理图片时出错: {str(e)}", ""
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def process_video(video_path, is_mirrored):
|
| 258 |
+
"""处理上传的视频,支持镜像设置"""
|
| 259 |
+
if video_path is None:
|
| 260 |
+
return None, "请上传视频", ""
|
| 261 |
+
|
| 262 |
+
try:
|
| 263 |
+
# 读取视频
|
| 264 |
+
cap = cv2.VideoCapture(video_path)
|
| 265 |
+
if not cap.isOpened():
|
| 266 |
+
return None, "无法打开视频文件", ""
|
| 267 |
+
|
| 268 |
+
# 获取视频信息
|
| 269 |
+
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 270 |
+
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 271 |
+
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 272 |
+
|
| 273 |
+
# 创建临时输出文件
|
| 274 |
+
temp_dir = tempfile.gettempdir()
|
| 275 |
+
output_path = os.path.join(temp_dir, f"processed_{os.path.basename(video_path)}")
|
| 276 |
+
|
| 277 |
+
# 创建视频写入器
|
| 278 |
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 279 |
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
| 280 |
+
|
| 281 |
+
frame_count = 0
|
| 282 |
+
detected_objects = set()
|
| 283 |
+
|
| 284 |
+
while True:
|
| 285 |
+
ret, frame = cap.read()
|
| 286 |
+
if not ret:
|
| 287 |
+
break
|
| 288 |
+
|
| 289 |
+
# 处理每一帧
|
| 290 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 291 |
+
|
| 292 |
+
# 应用镜像
|
| 293 |
+
if is_mirrored:
|
| 294 |
+
frame_rgb = cv2.flip(frame_rgb, 1)
|
| 295 |
+
|
| 296 |
+
processed_frame, introduction, _, _ = process_frame(frame_rgb, True, "", is_mirrored)
|
| 297 |
+
|
| 298 |
+
# 转换回BGR并写入
|
| 299 |
+
if processed_frame is not None:
|
| 300 |
+
processed_frame_bgr = cv2.cvtColor(processed_frame, cv2.COLOR_RGB2BGR)
|
| 301 |
+
out.write(processed_frame_bgr)
|
| 302 |
+
|
| 303 |
+
frame_count += 1
|
| 304 |
+
|
| 305 |
+
# 每10帧更新一次进度
|
| 306 |
+
if frame_count % 100 == 0:
|
| 307 |
+
yield f"正在处理第 {frame_count} 帧...", ""
|
| 308 |
+
|
| 309 |
+
cap.release()
|
| 310 |
+
out.release()
|
| 311 |
+
|
| 312 |
+
# 生成最终介绍
|
| 313 |
+
if detected_objects:
|
| 314 |
+
objects_str = ", ".join(detected_objects)
|
| 315 |
+
final_introduction = f"视频中检测到的物体: {objects_str}\n请查看处理后的视频文件。"
|
| 316 |
+
else:
|
| 317 |
+
final_introduction = "视频处理完成,但未检测到特定物体。"
|
| 318 |
+
|
| 319 |
+
# 添加到历史记录
|
| 320 |
+
history_messages.append(f"视频处理完成: {os.path.basename(video_path)}\n{final_introduction}\n")
|
| 321 |
+
history_text = "\n".join(history_messages[-10:])
|
| 322 |
+
|
| 323 |
+
return output_path, final_introduction, history_text
|
| 324 |
+
|
| 325 |
+
except Exception as e:
|
| 326 |
+
print(f"处理视频时出错: {e}")
|
| 327 |
+
return None, f"处理视频时出错: {str(e)}", ""
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
# 创建界面
|
| 331 |
+
with gr.Blocks(title="垃圾分类实时检测系统", theme=gr.themes.Soft()) as demo:
|
| 332 |
+
# 状态变量
|
| 333 |
+
is_detecting = gr.State(False)
|
| 334 |
+
last_detected_label = gr.State("")
|
| 335 |
+
is_mirrored = gr.State(False) # 新增镜像状态变量
|
| 336 |
+
|
| 337 |
+
gr.Markdown("""
|
| 338 |
+
# 🗑️ 垃圾分类实时检测系统
|
| 339 |
+
|
| 340 |
+
**使用说明:**
|
| 341 |
+
1. 等待系统状态显示「模型加载成功」
|
| 342 |
+
2. 点击「开始检测」按钮开启实时检测
|
| 343 |
+
3. 检测结果将直接在画面中显示边界框和分类标签
|
| 344 |
+
4. AI会自动介绍检测到的垃圾类别(包含详细环境影响和健康危害分析)
|
| 345 |
+
5. 可以上传图片或视频进行检测
|
| 346 |
+
""")
|
| 347 |
+
|
| 348 |
+
# 系统状态和控制按钮区域
|
| 349 |
+
with gr.Row():
|
| 350 |
+
status = gr.Textbox(
|
| 351 |
+
label="系统状态",
|
| 352 |
+
value="初始化中...",
|
| 353 |
+
interactive=False,
|
| 354 |
+
lines=2,
|
| 355 |
+
scale=4
|
| 356 |
+
)
|
| 357 |
+
with gr.Column(scale=1):
|
| 358 |
+
start_btn = gr.Button("▶️ 开始检测", variant="primary", size="lg")
|
| 359 |
+
stop_btn = gr.Button("⏹️ 停止检测", variant="secondary", size="lg")
|
| 360 |
+
mirror_btn = gr.Button("🔄 切换镜像", variant="secondary", size="lg") # 新增镜像按钮
|
| 361 |
+
|
| 362 |
+
# 主要显示区域
|
| 363 |
+
with gr.Row():
|
| 364 |
+
# 摄像头显示和上传区域
|
| 365 |
+
with gr.Column(scale=2):
|
| 366 |
+
with gr.Tab("实时摄像头"):
|
| 367 |
+
webcam = gr.Image(
|
| 368 |
+
sources=["webcam"],
|
| 369 |
+
streaming=True,
|
| 370 |
+
label="实时检测画面",
|
| 371 |
+
height=400,
|
| 372 |
+
type="numpy",
|
| 373 |
+
show_download_button=False
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
with gr.Tab("图片上传"):
|
| 377 |
+
image_input = gr.Image(
|
| 378 |
+
label="上传图片",
|
| 379 |
+
type="filepath",
|
| 380 |
+
height=400
|
| 381 |
+
)
|
| 382 |
+
image_output = gr.Image(
|
| 383 |
+
label="检测结果",
|
| 384 |
+
height=400,
|
| 385 |
+
show_download_button=True
|
| 386 |
+
)
|
| 387 |
+
image_btn = gr.Button("🔍 检测图片", variant="primary")
|
| 388 |
+
|
| 389 |
+
with gr.Tab("视频上传"):
|
| 390 |
+
video_input = gr.Video(
|
| 391 |
+
label="上传视频",
|
| 392 |
+
height=400
|
| 393 |
+
)
|
| 394 |
+
video_output = gr.Video(
|
| 395 |
+
label="处理后的视频",
|
| 396 |
+
height=400
|
| 397 |
+
)
|
| 398 |
+
video_btn = gr.Button("🎬 处理视频", variant="primary")
|
| 399 |
+
|
| 400 |
+
# AI介绍区域
|
| 401 |
+
with gr.Column(scale=1):
|
| 402 |
+
ai_introduction = gr.Textbox(
|
| 403 |
+
label="🧠 详细垃圾分类介绍",
|
| 404 |
+
value="等待检测物体...",
|
| 405 |
+
interactive=False,
|
| 406 |
+
lines=12,
|
| 407 |
+
max_lines=15
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
ai_history = gr.Textbox(
|
| 411 |
+
label="📜 历史记录",
|
| 412 |
+
value="",
|
| 413 |
+
interactive=False,
|
| 414 |
+
lines=10,
|
| 415 |
+
max_lines=15
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
gr.Markdown("""
|
| 419 |
+
**介绍内容说明:**
|
| 420 |
+
- 包含垃圾类别的详细环境影响分析
|
| 421 |
+
- 说明对人类和动物的健康危害
|
| 422 |
+
- 详细描述垃圾的具体作用和可回收性
|
| 423 |
+
- 两部分信息已整合为完整介绍
|
| 424 |
+
""")
|
| 425 |
+
|
| 426 |
+
# 页面加载时初始化模型
|
| 427 |
+
demo.load(init_model, inputs=[], outputs=status)
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
# 视频流处理函数
|
| 431 |
+
def video_stream(frame, detecting_state, last_label, mirror_state):
|
| 432 |
+
"""处理视频流,新增镜像参数"""
|
| 433 |
+
return process_frame(frame, detecting_state, last_label, mirror_state)
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
# 绑定视频流处理
|
| 437 |
+
webcam.stream(
|
| 438 |
+
video_stream,
|
| 439 |
+
inputs=[webcam, is_detecting, last_detected_label, is_mirrored],
|
| 440 |
+
outputs=[webcam, ai_introduction, last_detected_label, ai_history],
|
| 441 |
+
show_progress="hidden"
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
# 按钮事件处理
|
| 446 |
+
def start_detection():
|
| 447 |
+
"""开始检测"""
|
| 448 |
+
return True, "🔴 检测中...实时识别已开启"
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def stop_detection():
|
| 452 |
+
"""停止检测"""
|
| 453 |
+
return False, "✅ 检测已暂停,点击「开始检测」重新启用"
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def toggle_mirror(current_state):
|
| 457 |
+
"""切换镜像状态"""
|
| 458 |
+
new_state = not current_state
|
| 459 |
+
status_msg = "镜像模式已开启" if new_state else "镜像模式已关闭"
|
| 460 |
+
return new_state, f"ℹ️ {status_msg}"
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
# 图片处理
|
| 464 |
+
def process_image_wrapper(image, mirror_state):
|
| 465 |
+
"""包装图片处理函数,传递镜像参数"""
|
| 466 |
+
return process_image(image, mirror_state)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
# 视频处理
|
| 470 |
+
def process_video_wrapper(video, mirror_state):
|
| 471 |
+
"""包装视频处理函数,传递镜像参数"""
|
| 472 |
+
if video is None:
|
| 473 |
+
return None, "请上传视频", ""
|
| 474 |
+
return process_video(video, mirror_state)
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
# 绑定事件
|
| 478 |
+
start_btn.click(
|
| 479 |
+
start_detection,
|
| 480 |
+
inputs=[],
|
| 481 |
+
outputs=[is_detecting, status]
|
| 482 |
+
)
|
| 483 |
+
|
| 484 |
+
stop_btn.click(
|
| 485 |
+
stop_detection,
|
| 486 |
+
inputs=[],
|
| 487 |
+
outputs=[is_detecting, status]
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
# 绑定镜像切换按钮事件
|
| 491 |
+
mirror_btn.click(
|
| 492 |
+
toggle_mirror,
|
| 493 |
+
inputs=[is_mirrored],
|
| 494 |
+
outputs=[is_mirrored, status]
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
image_btn.click(
|
| 498 |
+
process_image_wrapper,
|
| 499 |
+
inputs=[image_input, is_mirrored],
|
| 500 |
+
outputs=[image_output, ai_introduction, ai_history]
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
video_btn.click(
|
| 504 |
+
process_video_wrapper,
|
| 505 |
+
inputs=[video_input, is_mirrored],
|
| 506 |
+
outputs=[video_output, ai_introduction, ai_history]
|
| 507 |
+
)
|
| 508 |
+
|
| 509 |
+
# 启动界面
|
| 510 |
+
if __name__ == "__main__":
|
| 511 |
+
demo.launch(
|
| 512 |
+
server_name="0.0.0.0",
|
| 513 |
+
server_port=7860,
|
| 514 |
+
share=True,
|
| 515 |
+
inbrowser=True
|
| 516 |
+
)
|