idkWhatToUse commited on
Commit
9a3a6bd
·
verified ·
1 Parent(s): 961ae05

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -0
app.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import gradio as gr
3
+ from PIL import Image
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ from huggingface_hub import hf_hub_download
6
+ import clip
7
+
8
+ # -------------------------------
9
+ # 1. 台灣縣市垃圾分類規則
10
+ # -------------------------------
11
+ city_rules = {
12
+ "台北市": {
13
+ "一般垃圾": "丟一般垃圾袋(白色)。",
14
+ "紙類": "紙類須乾淨並綁好,不可含塑膠膜。",
15
+ "塑膠類": "乾淨塑膠可回收;髒污塑膠丟一般垃圾。",
16
+ "金屬類": "鐵鋁罐可回收。",
17
+ "玻璃類": "玻璃瓶可回收;破碎玻璃需包裹後丟一般垃圾。",
18
+ "食物垃圾": "分可食 / 不可食兩桶。",
19
+ "電池": "丟入電池回收箱。",
20
+ "電子產品": "需交給回收車或清潔隊。"
21
+ },
22
+ "新北市": {
23
+ "一般垃圾": "黑色垃圾袋。",
24
+ "紙類": "紙容器需清洗,紙類需綁好。",
25
+ "塑膃類": "PET 與其他塑膠須分開回收。",
26
+ "金屬類": "鐵與鋁需分類。",
27
+ "玻璃類": "玻璃瓶回收不分顏色。",
28
+ "食物垃圾": "統一廚餘桶。",
29
+ "電池": "請投遞電池回收箱。",
30
+ "電子產品": "請交由回收車或清潔隊。"
31
+ },
32
+ "高雄市": {
33
+ "一般垃圾": "黑色垃圾袋。",
34
+ "紙類": "紙容器屬紙類可回收。",
35
+ "塑膠類": "乾燥可回收;油污塑膠丟一般垃圾。",
36
+ "金屬類": "鐵鋁罐可回收。",
37
+ "玻璃類": "瓶類回收;破損玻璃需包裝。",
38
+ "食物垃圾": "各區規則不同。",
39
+ "電池": "投遞電池專用箱。",
40
+ "電子產品": "屬有害垃圾,需交回收車或清潔隊。"
41
+ }
42
+ }
43
+
44
+ # -------------------------------
45
+ # 2. 載入 CLIP
46
+ # -------------------------------
47
+ device = "cuda" if torch.cuda.is_available() else "cpu"
48
+ clip_model, preprocess = clip.load("ViT-B/32", device=device)
49
+
50
+ labels = ["一般垃圾", "紙類", "塑膠類", "金屬類", "玻璃類", "食物垃圾", "電池", "電子產品"]
51
+
52
+ # -------------------------------
53
+ # 3. LLM (phi-2)
54
+ # -------------------------------
55
+ tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2")
56
+ llm = AutoModelForCausalLM.from_pretrained("microsoft/phi-2").to(device)
57
+
58
+ def generate_reason(item_name, category):
59
+ prompt = f"物品「{item_name}」被歸類為「{category}」。請用一句話簡單解釋原因:"
60
+ inputs = tokenizer(prompt, return_tensors="pt").to(device)
61
+ outputs = llm.generate(**inputs, max_new_tokens=40)
62
+ return tokenizer.decode(outputs[0], skip_special_tokens=True)
63
+
64
+ # -------------------------------
65
+ # 4. GroundingDINO(從 HF 下載權重)
66
+ # -------------------------------
67
+ from groundingdino.util.inference import load_model, load_image, predict, annotate
68
+
69
+ checkpoint = hf_hub_download(
70
+ repo_id="IDEA-Research/GroundingDINO",
71
+ filename="groundingdino_swint_ogc.pth"
72
+ )
73
+
74
+ config_path = hf_hub_download(
75
+ repo_id="IDEA-Research/GroundingDINO",
76
+ filename="GroundingDINO_SwinT_OGC.py"
77
+ )
78
+
79
+ dino_model = load_model(config_path, checkpoint)
80
+
81
+ # -------------------------------
82
+ # 5. 主流程:偵測 → 分類 → 解釋 → 建議
83
+ # -------------------------------
84
+ def classify_item(image, obj_name, city):
85
+ text_inputs = clip.tokenize(labels).to(device)
86
+ img_tensor = preprocess(image).unsqueeze(0).to(device)
87
+
88
+ with torch.no_grad():
89
+ logits, _ = clip_model(img_tensor, text_inputs)
90
+ probs = logits.softmax(dim=-1).cpu().numpy()[0]
91
+
92
+ idx = probs.argmax()
93
+ category = labels[idx]
94
+
95
+ reason = generate_reason(obj_name, category)
96
+ suggestion = city_rules[city][category]
97
+
98
+ return category, reason, suggestion
99
+
100
+
101
+ def pipeline(image, city):
102
+ image.save("temp.jpg")
103
+
104
+ img_np, img_tensor = load_image("temp.jpg")
105
+ boxes, logits, phrases = predict(
106
+ model=dino_model,
107
+ image=img_tensor,
108
+ caption="bottle, can, box, plastic, cup, phone, food, battery",
109
+ box_threshold=0.3,
110
+ text_threshold=0.25
111
+ )
112
+
113
+ annotated = annotate(img_np, boxes, logits, phrases)
114
+
115
+ result = ""
116
+ for obj in phrases:
117
+ category, reason, suggestion = classify_item(image, obj, city)
118
+ result += f"""
119
+ 🧩 偵測到物品:{obj}
120
+ 📦 分類:{category}
121
+ 💡 理由:{reason}
122
+ 🚮 縣市建議({city}):{suggestion}
123
+
124
+ """
125
+
126
+ return annotated, result
127
+
128
+
129
+ # -------------------------------
130
+ # 6. Gradio 介面
131
+ # -------------------------------
132
+ with gr.Blocks() as demo:
133
+ gr.Markdown("# 🗑️ AI 垃圾分類助手(縣市版)")
134
+ gr.Markdown("自動偵測圖片中的垃圾 → 分類 → 產生理由 → 給縣市專屬分類建議")
135
+
136
+ with gr.Row():
137
+ img = gr.Image(label="上傳垃圾照片")
138
+ city = gr.Dropdown(["台北市", "新北市", "高雄市"], value="台北市")
139
+
140
+ btn = gr.Button("開始分析 🚀", variant="primary")
141
+
142
+ output_img = gr.Image(label="偵測結果")
143
+ output_text = gr.Textbox(label="分析結果", lines=12)
144
+
145
+ btn.click(pipeline, inputs=[img, city], outputs=[output_img, output_text])
146
+
147
+ demo.launch()