pcreem commited on
Commit
ff9a3da
·
1 Parent(s): 3e602f5

video version

Browse files
Files changed (4) hide show
  1. .DS_Store +0 -0
  2. app.py +207 -6
  3. best0625.pt → best0628.pt +2 -2
  4. requirements.txt +3 -0
.DS_Store ADDED
Binary file (6.15 kB). View file
 
app.py CHANGED
@@ -1,12 +1,213 @@
 
1
  import torch
2
  from ultralytics import YOLO
3
  import gradio as gr
 
 
 
 
 
 
 
4
 
5
- model = YOLO("best0625.pt") # 或者是從 hf_hub_download 取得
 
 
 
 
 
6
 
7
- def detect(image):
8
- results = model(image)
9
- return results[0].plot() # 回傳帶框圖像
 
10
 
11
- iface = gr.Interface(fn=detect, inputs="image", outputs="image")
12
- iface.launch(show_api=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
  import torch
3
  from ultralytics import YOLO
4
  import gradio as gr
5
+ import threading
6
+ import time
7
+ import os
8
+ import zipfile
9
+ from datetime import datetime
10
+ import pandas as pd
11
+ import tempfile
12
 
13
+ # === 模型與設定 ===
14
+ model = YOLO("best0628.pt")
15
+ TARGET_CLASS_NAME = "kumay"
16
+ save_dir = "saved_bears"
17
+ log_path = os.path.join(save_dir, "detection_log.csv")
18
+ os.makedirs(save_dir, exist_ok=True)
19
 
20
+ # === 全域狀態 ===
21
+ latest_frame = None
22
+ lock = threading.Lock()
23
+ streaming = False
24
 
25
+ # 初始化 log
26
+ if not os.path.exists(log_path):
27
+ with open(log_path, "w") as f:
28
+ f.write("frame_id,timestamp,timestamp_diff,filename,class,confidence\n")
29
+
30
+ last_detection_time = None
31
+ frame_counter = 0
32
+
33
+ # === Webcam 持續讀取 ===
34
+ def webcam_reader():
35
+ global latest_frame
36
+ cap = cv2.VideoCapture(0)
37
+ while True:
38
+ ret, frame = cap.read()
39
+ if ret:
40
+ with lock:
41
+ latest_frame = frame.copy()
42
+ time.sleep(0.03)
43
+
44
+ # === 偵測與儲存 ===
45
+ def detect_and_save(frame):
46
+ global last_detection_time, frame_counter
47
+ results = model(frame)
48
+ names = results[0].names
49
+ has_bear = False
50
+ best_conf = 0
51
+ best_cls_name = ""
52
+
53
+ for box in results[0].boxes:
54
+ cls_id = int(box.cls[0])
55
+ cls_name = names[cls_id]
56
+ conf = float(box.conf[0])
57
+ if cls_name == TARGET_CLASS_NAME and conf >= 0.85:
58
+ has_bear = True
59
+ if conf > best_conf:
60
+ best_conf = conf
61
+ best_cls_name = cls_name
62
+
63
+ if has_bear:
64
+ timestamp = datetime.now()
65
+ timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S_%f")[:-3]
66
+ filename = os.path.join(save_dir, f"bear_{timestamp_str}.png")
67
+
68
+ for box in results[0].boxes:
69
+ cls_id = int(box.cls[0])
70
+ cls_name = names[cls_id]
71
+ conf = float(box.conf[0])
72
+ if cls_name == TARGET_CLASS_NAME and conf >= 0.85:
73
+ xyxy = box.xyxy[0].cpu().numpy().astype(int)
74
+ cv2.putText(
75
+ frame,
76
+ f"{cls_name}: {conf:.2f}",
77
+ (xyxy[0], xyxy[1] - 10),
78
+ cv2.FONT_HERSHEY_SIMPLEX,
79
+ 0.6,
80
+ (0, 255, 0),
81
+ 2,
82
+ )
83
+ cv2.rectangle(frame, (xyxy[0], xyxy[1]), (xyxy[2], xyxy[3]), (0, 255, 0), 2)
84
+
85
+ cv2.imwrite(filename, frame)
86
+ print(f"📸 偵測到 {best_cls_name},儲存:{filename}")
87
+ assert os.path.exists(filename)
88
+
89
+ diff = (timestamp - last_detection_time).total_seconds() if last_detection_time else 0.0
90
+ with open(log_path, "a") as f:
91
+ f.write(f"{frame_counter},{timestamp},{diff:.3f},{filename},{best_cls_name},{best_conf:.4f}\n")
92
+ last_detection_time = timestamp
93
+
94
+ frame_counter += 1
95
+ return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
96
+
97
+ # === Webcam 處理 ===
98
+ def get_annotated_frame():
99
+ global latest_frame
100
+ with lock:
101
+ frame = latest_frame.copy() if latest_frame is not None else None
102
+ if frame is None:
103
+ return None
104
+ return detect_and_save(frame)
105
+
106
+ def streaming_loop():
107
+ global streaming
108
+ while streaming:
109
+ frame = get_annotated_frame()
110
+ if frame is not None:
111
+ with lock:
112
+ cv2.imwrite("latest_stream.png", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
113
+ time.sleep(0.2)
114
+
115
+ def start_stream():
116
+ global streaming
117
+ streaming = True
118
+ threading.Thread(target=streaming_loop, daemon=True).start()
119
+
120
+ def stop_stream():
121
+ global streaming
122
+ streaming = False
123
+
124
+ # === 影片偵測 ===
125
+ def detect_video(video_path):
126
+ cap = cv2.VideoCapture(video_path)
127
+ fps = cap.get(cv2.CAP_PROP_FPS)
128
+ W = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
129
+ H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
130
+
131
+ output_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
132
+ out = cv2.VideoWriter(output_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (W, H))
133
+
134
+ while cap.isOpened():
135
+ ret, frame = cap.read()
136
+ if not ret:
137
+ break
138
+ annotated = detect_and_save(frame)
139
+ out.write(cv2.cvtColor(annotated, cv2.COLOR_RGB2BGR))
140
+
141
+ cap.release()
142
+ out.release()
143
+ print(f"✅ 影片處理完成:{output_path}")
144
+ return output_path
145
+
146
+ # === ZIP 功能 ===
147
+ def create_zip():
148
+ zip_path = "detection_package.zip"
149
+ with zipfile.ZipFile(zip_path, "w") as zipf:
150
+ for fname in os.listdir(save_dir):
151
+ fpath = os.path.join(save_dir, fname)
152
+ if os.path.isfile(fpath):
153
+ zipf.write(fpath, arcname=os.path.join("saved_bears", fname))
154
+ if os.path.exists(log_path):
155
+ zipf.write(log_path, arcname="detection_log.csv")
156
+ return zip_path
157
+
158
+ def read_csv():
159
+ if os.path.exists(log_path):
160
+ df = pd.read_csv(log_path)
161
+ if "frame_id" in df.columns:
162
+ return df.sort_values(by="frame_id", ascending=False).reset_index(drop=True)
163
+ return df
164
+ return []
165
+
166
+ def get_latest_image():
167
+ return "latest_stream.png" if os.path.exists("latest_stream.png") else None
168
+
169
+ # === 啟動 webcam 執行緒 ===
170
+ threading.Thread(target=webcam_reader, daemon=True).start()
171
+
172
+ # === Gradio UI ===
173
+ with gr.Blocks() as demo:
174
+ gr.Markdown("## 🐻 台灣黑熊偵測系統")
175
+
176
+ with gr.Tab("📹 上傳影片辨識"):
177
+ gr.Markdown("上傳影片,逐幀偵測台灣黑熊,並自動儲存出現畫面")
178
+ video_input = gr.Video()
179
+ video_output = gr.Video()
180
+ video_button = gr.Button("上傳並分析影片")
181
+ video_button.click(fn=detect_video, inputs=video_input, outputs=video_output)
182
+
183
+ with gr.Tab("📷 即時攝影機偵測"):
184
+ gr.Markdown("啟用 webcam 進行即時偵測,若出現台灣黑熊則自動儲存影像")
185
+ webcam_output = gr.Image(
186
+ label="即時辨識結果",
187
+ interactive=False,
188
+ type="filepath",
189
+ value=get_latest_image,
190
+ every=0.2
191
+ )
192
+ with gr.Row():
193
+ start_btn = gr.Button("▶️ 開始直播")
194
+ stop_btn = gr.Button("⏹ 停止直播")
195
+ start_btn.click(fn=start_stream, inputs=[], outputs=[])
196
+ stop_btn.click(fn=stop_stream, inputs=[], outputs=[])
197
+
198
+ with gr.Tab("📁 下載與預覽"):
199
+ gr.Markdown("### 預覽與下載偵測圖片與紀錄檔")
200
+ log_df = gr.Dataframe(label="detection_log.csv 預覽", interactive=False)
201
+ load_log_btn = gr.Button("🔄 重新載入紀錄檔")
202
+ load_log_btn.click(fn=read_csv, outputs=log_df)
203
+
204
+ csv_file = gr.File(value=log_path, label="⬇️ 下載 CSV 檔")
205
+
206
+ gr.Markdown("### 打包圖片與紀錄檔(.zip)")
207
+ zip_btn = gr.Button("📦 產生 ZIP 檔")
208
+ zip_file = gr.File(label="⬇️ 點我下載壓縮檔")
209
+ zip_btn.click(fn=create_zip, outputs=zip_file)
210
+
211
+ # 啟動
212
+ if __name__ == "__main__":
213
+ demo.launch()
best0625.pt → best0628.pt RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:6550b7ee01b41d3e14918c42966f47722ff6530302dc6574994ed67649e057b0
3
- size 6218787
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1536806a99f859860f48938f253c99edd6da12af5de188fb5ecd50ea58f89a90
3
+ size 22516707
requirements.txt CHANGED
@@ -1,3 +1,6 @@
1
  ultralytics
2
  gradio
3
  torch
 
 
 
 
1
  ultralytics
2
  gradio
3
  torch
4
+ opencv-python
5
+ numpy
6
+