Yuski commited on
Commit
842a433
·
verified ·
1 Parent(s): 919fa9d

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ output_images/1411032040-楊宗祥.jpg filter=lfs diff=lfs merge=lfs -text
__pycache__/gemini_ai.cpython-313.pyc CHANGED
Binary files a/__pycache__/gemini_ai.cpython-313.pyc and b/__pycache__/gemini_ai.cpython-313.pyc differ
 
__pycache__/image_converter.cpython-313.pyc CHANGED
Binary files a/__pycache__/image_converter.cpython-313.pyc and b/__pycache__/image_converter.cpython-313.pyc differ
 
__pycache__/mongo_lib.cpython-313.pyc ADDED
Binary file (1.17 kB). View file
 
__pycache__/target_object.cpython-313.pyc ADDED
Binary file (1.21 kB). View file
 
gemini_ai.py CHANGED
@@ -5,72 +5,52 @@ import image_converter as img_converter
5
  import random
6
  import os
7
  import ast
8
- import target
9
 
10
- # 基本設定都放這邊----------------------------------------
11
  #
12
  #
13
  # 設定圖檔位置 (此處僅為範例,純文字查詢時可忽略)
14
- image_path = r"D:\Practice\Python_YOLO_AI_ENV\test_images\input\CAT1.png"
15
 
16
 
17
  # 要使用的模型種類,免費版一分鐘只能跑最多十筆
18
- gemini_model = "gemini-2.5-flash"
19
 
20
 
21
- # 要求AI的提示語放這邊
22
- # image_prompt = """您現在扮演一位圖片分類大師,擅長解讀圖片中的一些抽象涵義並加以分類。
23
- # 請在各大類中選最近似的一樣,輸出結果如範例:"A[開心],B[學習],C[學校]"。
24
- # 若您覺得,該圖片不具上列特徵,請回覆"A[NIL]",加上NIL表示該類未再提供的選項內。
25
- # 以下是我們要請您分辨的種類:
26
- # A情感類-人物表情: A[面無表情,開心,生氣,悲傷,緊張,輕視,想睡,疲憊,興奮,自信滿滿,臉部遮蔽]。
27
- # B動作類-B[學習,工作,飲食,遊戲,駕駛,睡覺,冥想,醫療行為,會議,團隊討論,聽音樂,看電視,畫畫,騎車,烹飪,走路]。
28
- # C場景類-C[辦公室等工作空間,書房,臥室,客廳,學校,網咖,超現實場景,車內,外太空]。"""
29
 
30
- # image_prompt = """您現在扮演一位圖片分類大師擅長解讀圖片中的一些抽象涵義並加以分類
 
 
31
  # 請在各大類中選最近似的一樣,輸出結果如範例:"物理環境[辦公室],技術應用[人工智慧,虛擬實境,其他],資訊設備[其他]"。
32
- # 若您覺得,該圖片不具上列特徵,請回覆"XXX[NIL]",XXX為該類別,加上NIL表示該類未再提供的選項內。
33
- # 以下是我們要請您分辨的種類,會以JSON標示:
34
- # 物理環境[辦公室,臥室,工作室,工廠]。
35
- # 技術應用[人工智慧,虛擬實境,數據析,其他]
36
- # 社交關係[獨立工作(1人),,團隊合作(2人以上),遠程協(遠端控制)]。
37
- # 職業情感[快樂,睡,壓力/焦慮,成就感]。
38
- # 資訊設備[AI助手,投影儀,手機,眼鏡投影,智慧手錶,機械手臂,平板,電腦,鍵盤,滑鼠,其他]。
39
- # 物體[床,椅子,桌子,書架,PC,肖像,監視器,窗戶,冷氣機,其他]。
40
- # 角色[機器人,教師,學生,動物,工作人員]。
41
- # """
42
-
43
- image_prompt = """您現在扮演一位圖片分類大師,擅長解讀圖片中的一些抽象涵義並加以分類。
44
- 請在各大類中選最近似的一樣,輸出結果如範例:"物理環境[辦公室],技術應用[人工智慧,虛擬實境,其他],資訊設備[其他]"。
45
- 若您覺得,該圖片不具上列特徵,請回覆"XXX[NIL]",XXX為該類別,加上NIL表示該類未再提供的選項內。
46
- 以下是我們要請您分辨的種類,會以JSON標示:""" + str(
47
- target.target_JSON
48
- )
49
-
50
-
51
- # --------------------------------------------------------
52
  ## 替換冒號和逗號為換行符號
53
  def replace_colon_comma_with_newline(input_string):
54
- processed_string = (
55
- input_string.replace(":", "\n").replace(":", "\n").replace("],", "]\n")
56
- )
57
- return processed_string
58
 
59
 
60
  def getApiToken():
61
  try:
62
- my_api_key = os.getenv("my_api_key")
63
- my_list = ast.literal_eval(
64
- my_api_key
65
- ) # Convert string to list因為存在環境變數中是字串格式
66
 
67
- return random.choice(my_list)
68
  except Exception as e:
69
  return ""
70
-
71
-
72
  # function,輸入是文字或是圖檔的位置
73
- def analyze_content_with_gemini(input_content, user_prompt=None):
74
  """
75
  透過 Gemini API 辨識內容,可處理純文字或圖片。
76
 
@@ -85,43 +65,30 @@ def analyze_content_with_gemini(input_content, user_prompt=None):
85
  Returns:
86
  str: 辨識結果的文字描述。
87
  """
88
-
89
- try:
90
- # 請將 'YOUR_API_KEY' 替換為您的實際 API 金鑰。
91
- my_api_key = getApiToken() # 從環境變數中獲取API金鑰
92
- print(my_api_key)
93
- genai.configure(api_key=my_api_key)
94
- except Exception as e:
95
- return f"發生錯誤:{e}"
96
-
97
  # 根據 user_prompt 決定要使用的 prompt
98
- prompt_to_use = (
99
- image_prompt + user_prompt
100
- if user_prompt and user_prompt.strip()
101
- else image_prompt
102
- )
103
-
104
  try:
105
  # 判斷輸入的類型
106
  if isinstance(input_content, str):
107
  # 如果輸入是字串,嘗試判斷是否為圖片路徑
108
- if input_content.lower().endswith(
109
- (".png", ".jpg", ".jpeg", ".gif", ".webp")
110
- ):
111
- if input_content.lower().endswith((".webp")):
112
- input_content = img_converter.convert_webp_to_jpg(
113
- input_content
114
- ) # 如果是 webp 圖片,先轉換為 jpg
115
-
116
  model = genai.GenerativeModel(gemini_model)
117
  image_obj = PIL.Image.open(input_content)
118
  response = model.generate_content([prompt_to_use, image_obj])
119
  else:
120
  # 純文字輸入
121
  model = genai.GenerativeModel(gemini_model)
122
- response = model.generate_content(
123
- input_content
124
- ) # 純文字直接使用輸入內容當 prompt
125
  elif isinstance(input_content, PIL.Image.Image):
126
  model = genai.GenerativeModel(gemini_model)
127
  response = model.generate_content([prompt_to_use, input_content])
@@ -134,7 +101,7 @@ def analyze_content_with_gemini(input_content, user_prompt=None):
134
  return f"發生錯誤:{e}"
135
 
136
 
137
- if __name__ == "__main__":
138
  # --- 程式碼使用範例 ---
139
 
140
  # 範例 1:傳送純文字訊息
@@ -148,12 +115,10 @@ if __name__ == "__main__":
148
  # 範例 2:傳送圖片路徑
149
  # 請確保 image_path 指向有效的圖片檔案
150
  print("正在處理圖片訊息...")
151
- my_prompt =""
152
- # my_prompt = """{
153
- # "物": ["辦公室", "臥室", "工作室", "工廠","","",""]
154
- # }"""
155
-
156
- response_image = analyze_content_with_gemini(image_path)
157
  print("回應結果:")
158
  print(response_image)
159
  print("-" * 20)
 
5
  import random
6
  import os
7
  import ast
8
+ import target_object
9
 
10
+ #基本設定都放這邊----------------------------------------
11
  #
12
  #
13
  # 設定圖檔位置 (此處僅為範例,純文字查詢時可忽略)
14
+ image_path = r'G:\Python\tools\input_images\1411135045-張華桀.jpg'
15
 
16
 
17
  # 要使用的模型種類,免費版一分鐘只能跑最多十筆
18
+ gemini_model = 'gemini-2.5-flash'
19
 
20
 
 
 
 
 
 
 
 
 
21
 
22
+ #要求AI扮演的角色和提示詞這裡的提示詞會用來引導AI進行圖片分類
23
+
24
+ # 給AI的提示詞 = """您現在扮演一位圖片分類大師,擅長解讀圖片中的一些抽象涵義並加以分類。
25
  # 請在各大類中選最近似的一樣,輸出結果如範例:"物理環境[辦公室],技術應用[人工智慧,虛擬實境,其他],資訊設備[其他]"。
26
+ # 若您覺得,該圖片完全不具要辨識的特徵,請回覆"XXX[NIL]",XXX為該類別,加上NIL表示該類未再提供的選項內。
27
+ # 以下是我們要請您分辨的種類,會以JSON標示:"""
28
+
29
+ 給AI的提示詞 = """您現在扮演一位圖片分類師,擅長解讀圖片中的一些抽象涵義並加以
30
+ 請在各大類中選最近似的一樣,輸出結果如範例:[物理環境_辦公室,技術應用_人工智慧,技術應用_大數據分析,社交關係_獨立工作(1人),資訊設備_電腦,資訊設備_鍵盤,資訊設備_滑鼠,資訊設備_手機,物體_桌子,物體_椅子,角色_工人員]。
31
+ 若您得,該圖片完全不具要辨識的特徵,請回覆[NIL]。
32
+ 以下是我們要請您分辨的種類,會以JSON標示:"""
33
+
34
+ #--------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
35
  ## 替換冒號和逗號為換行符號
36
  def replace_colon_comma_with_newline(input_string):
37
+ processed_string = input_string.replace(':', '\n').replace(':', '\n').replace('],', ']\n')
38
+ return processed_string
39
+
 
40
 
41
 
42
  def getApiToken():
43
  try:
44
+ my_api_key = os.getenv('my_api_key')
45
+ my_list = ast.literal_eval(my_api_key) # Convert string to list因為存在環境變數中是字串格式
 
 
46
 
47
+ return random.choice(my_list)
48
  except Exception as e:
49
  return ""
50
+
51
+
52
  # function,輸入是文字或是圖檔的位置
53
+ def analyze_content_with_gemini(input_content, 辨識目標物=None):
54
  """
55
  透過 Gemini API 辨識內容,可處理純文字或圖片。
56
 
 
65
  Returns:
66
  str: 辨識結果的文字描述。
67
  """
68
+ my_api_key = getApiToken() # 從環境變數中獲取API金鑰
69
+ genai.configure(api_key=my_api_key)
70
+
 
 
 
 
 
 
71
  # 根據 user_prompt 決定要使用的 prompt
72
+ prompt_to_use = 給AI的提示詞+辨識目標物 if 辨識目標物 and 辨識目標物.strip() else 給AI的提示詞+ str(target_object.target_JSON)
73
+
74
+ # print("-"*50)
75
+ # print(prompt_to_use)
76
+
 
77
  try:
78
  # 判斷輸入的類型
79
  if isinstance(input_content, str):
80
  # 如果輸入是字串,嘗試判斷是否為圖片路徑
81
+ if input_content.lower().endswith(('.png', '.jpg', '.jpeg', '.gif','.webp')):
82
+ if input_content.lower().endswith(('.webp')):
83
+ input_content = img_converter.convert_webp_to_jpg(input_content) # 如果是 webp 圖片,先轉換為 jpg
84
+
 
 
 
 
85
  model = genai.GenerativeModel(gemini_model)
86
  image_obj = PIL.Image.open(input_content)
87
  response = model.generate_content([prompt_to_use, image_obj])
88
  else:
89
  # 純文字輸入
90
  model = genai.GenerativeModel(gemini_model)
91
+ response = model.generate_content(input_content) # 純文字直接使用輸入內容當 prompt
 
 
92
  elif isinstance(input_content, PIL.Image.Image):
93
  model = genai.GenerativeModel(gemini_model)
94
  response = model.generate_content([prompt_to_use, input_content])
 
101
  return f"發生錯誤:{e}"
102
 
103
 
104
+ if __name__ == '__main__':
105
  # --- 程式碼使用範例 ---
106
 
107
  # 範例 1:傳送純文字訊息
 
115
  # 範例 2:傳送圖片路徑
116
  # 請確保 image_path 指向有效的圖片檔案
117
  print("正在處理圖片訊息...")
118
+ 我要辨識的物體 = ""
119
+ #我要辨識的物體 = '{"物件類別": ["人", "老虎", "獅子", "牛","書架", "PC", "窗戶", "冷氣機","其他", "雞", "車子", "企鵝","長頸鹿"]}'
120
+ #我要辨識的物體 = '{"物件類別": ["", "老虎", "獅子", "","書架", "PC", "窗戶", "冷氣機","其他", "雞", "車子"]}'
121
+ response_image = analyze_content_with_gemini(image_path, 我要辨識的物體)
 
 
122
  print("回應結果:")
123
  print(response_image)
124
  print("-" * 20)
image_converter.py CHANGED
@@ -44,7 +44,7 @@ if __name__ == '__main__':
44
  try:
45
  # 建立一個簡單的白色圖片
46
  #G:\Python\tools\input_images\1411032040-楊宗祥.webp
47
- dummy_webp_path = r"G:\Python\tools\input_images\1411032040-楊宗祥.webp"
48
 
49
  # 測試轉換函數
50
  # 範例 1: 轉換並儲存在相同資料夾
 
44
  try:
45
  # 建立一個簡單的白色圖片
46
  #G:\Python\tools\input_images\1411032040-楊宗祥.webp
47
+ dummy_webp_path = r"G:\Python\tools\input_images\1411032040.webp"
48
 
49
  # 測試轉換函數
50
  # 範例 1: 轉換並儲存在相同資料夾
main.py CHANGED
@@ -15,10 +15,12 @@ from ultralytics import YOLO
15
  import shutil
16
  import zipfile
17
  import uuid # 匯入 uuid 以生成唯一的執行 ID
18
- from pathlib import Path # 匯入 Path 以更方便地操作路徑
19
-
20
- # 假設 gemini_ai.py 在同一個目錄或 Python 路徑中
21
  import gemini_ai as genai
 
 
 
 
22
 
23
 
24
  def create_zip_archive(files, zip_filename):
@@ -32,7 +34,7 @@ def create_zip_archive(files, zip_filename):
32
  Returns:
33
  str: 產生的 zip 檔案路徑。
34
  """
35
- with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
36
  for file in files:
37
  if os.path.exists(file):
38
  # 使用 os.path.basename 確保只寫入檔案名稱,而非完整路徑
@@ -41,14 +43,13 @@ def create_zip_archive(files, zip_filename):
41
  print(f"警告: 檔案 '{file}' 不存在,無法加入壓縮檔。")
42
  return zip_filename
43
 
44
-
45
  def gradio_multi_model_detection(
46
  image_files,
47
  model_files,
48
  conf_threshold,
49
  enable_mllm,
50
  mllm_prompt,
51
- progress=gr.Progress(track_tqdm=True),
52
  ):
53
  """
54
  Gradio 的主要處理函式,使用生成器 (yield) 實現流式輸出。
@@ -64,19 +65,33 @@ def gradio_multi_model_detection(
64
  Yields:
65
  dict: 用於更新 Gradio 介面元件的字典。
66
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  if not image_files:
68
  yield {
69
  output_status: gr.update(value="錯誤:請至少上傳一張圖片。"),
70
  output_gallery: None,
71
  output_text: None,
72
- download_button: None,
73
  }
74
  return
75
 
76
  # --- 1. 初始化設定 ---
77
  # 為本次執行創建一個唯一的子目錄
78
  run_id = str(uuid.uuid4())
79
- base_output_dir = Path("gradio_detection_results")
80
  run_output_dir = base_output_dir / f"run_{run_id[:8]}"
81
  run_output_dir.mkdir(parents=True, exist_ok=True)
82
 
@@ -88,16 +103,12 @@ def gradio_multi_model_detection(
88
  loaded_models = []
89
  if not model_paths:
90
  # 如果沒有上傳模型,使用預設模型
91
- default_model_path = "yolov8n.pt"
92
  try:
93
  model = YOLO(default_model_path)
94
  loaded_models.append((default_model_path, model))
95
  except Exception as e:
96
- yield {
97
- output_status: gr.update(
98
- value=f"錯誤: 無法載入預設模型 '{default_model_path}' - {e}"
99
- )
100
- }
101
  return
102
  else:
103
  for model_path in model_paths:
@@ -125,24 +136,22 @@ def gradio_multi_model_detection(
125
  image_path = Path(image_path_str)
126
  progress(i / total_images, desc=f"處理中: {image_path.name}")
127
  yield {
128
- output_status: gr.update(
129
- value=f"處理中... ({i+1}/{total_images}) - {image_path.name}"
130
- ),
131
- output_gallery: gr.update(value=annotated_image_paths),
132
  }
133
 
134
  original_image = cv2.imread(str(image_path))
135
  if original_image is None:
136
  print(f"警告: 無法讀取圖片 '{image_path}',跳過。")
137
  continue
138
-
139
  annotated_image = original_image.copy()
140
  image_base_name = image_path.stem
141
 
142
  # --- 3a. YOLO 物件偵測 ---
143
  yolo_output_content = [f"--- 檔案: {image_path.name} ---"]
144
  all_detections_for_image = []
145
-
146
  for model_path_str, model_obj in loaded_models:
147
  model_name = Path(model_path_str).name
148
  yolo_output_content.append(f"--- 模型: {model_name} ---")
@@ -155,47 +164,22 @@ def gradio_multi_model_detection(
155
  x1, y1, x2, y2 = map(int, box.xyxy[0])
156
  cls_id = int(box.cls[0])
157
  cls_name = model_obj.names[cls_id]
158
-
159
- detection_info = {
160
- "model_name": model_name,
161
- "class_name": cls_name,
162
- "confidence": conf,
163
- "bbox": (x1, y1, x2, y2),
164
- }
165
  all_detections_for_image.append(detection_info)
166
- yolo_output_content.append(
167
- f" - {cls_name} (信賴度: {conf:.2f}) [座標: {x1},{y1},{x2},{y2}]"
168
- )
169
  else:
170
  yolo_output_content.append(" 未偵測到任何物件。")
171
 
172
  # 繪製偵測框
173
- colors = [
174
- (255, 0, 0),
175
- (0, 255, 0),
176
- (0, 0, 255),
177
- (255, 255, 0),
178
- (255, 0, 255),
179
- (0, 255, 255),
180
- ]
181
- color_map = {
182
- Path(p).name: colors[idx % len(colors)]
183
- for idx, (p, _) in enumerate(loaded_models)
184
- }
185
  for det in all_detections_for_image:
186
- x1, y1, x2, y2 = det["bbox"]
187
- color = color_map.get(det["model_name"], (200, 200, 200))
188
  label = f"{det['class_name']} {det['confidence']:.2f}"
189
  cv2.rectangle(annotated_image, (x1, y1), (x2, y2), color, 2)
190
- cv2.putText(
191
- annotated_image,
192
- label,
193
- (x1, y1 - 10),
194
- cv2.FONT_HERSHEY_SIMPLEX,
195
- 0.5,
196
- color,
197
- 2,
198
- )
199
 
200
  # 儲存 YOLO 標註圖
201
  output_image_path = run_output_dir / f"{image_base_name}_yolo_detected.jpg"
@@ -205,134 +189,110 @@ def gradio_multi_model_detection(
205
 
206
  # 儲存 YOLO 辨識資訊
207
  output_yolo_txt_path = run_output_dir / f"{image_base_name}_yolo_objects.txt"
208
- output_yolo_txt_path.write_text(
209
- "\n".join(yolo_output_content), encoding="utf-8"
210
- )
211
  all_result_files.append(str(output_yolo_txt_path))
212
 
213
  # --- 3b. MLLM 分析 (如果啟用) ---
214
  output_mllm_txt_path = None
 
215
  if enable_mllm:
216
  try:
217
- prompt_to_use = (
218
- mllm_prompt if mllm_prompt and mllm_prompt.strip() else None
219
- )
220
- mllm_str = genai.analyze_content_with_gemini(
221
- str(image_path), prompt_to_use
222
- )
223
  mllm_result_content = f"--- MLLM 分析結果 ---\n{mllm_str}"
224
  except Exception as e:
225
  mllm_result_content = f"--- MLLM 分析失敗 ---\n原因: {e}"
226
-
227
  output_mllm_txt_path = run_output_dir / f"{image_base_name}_mllm_result.txt"
228
- output_mllm_txt_path.write_text(mllm_result_content, encoding="utf-8")
229
  all_result_files.append(str(output_mllm_txt_path))
 
 
 
 
 
 
 
 
 
 
 
230
 
231
  # 將本次圖片的結果加入到總列表中
232
  all_texts.append("\n".join(yolo_output_content))
233
  if output_mllm_txt_path:
234
- all_texts.append(output_mllm_txt_path.read_text(encoding="utf-8"))
 
235
 
236
  # --- 4. 完成處理,打包並更新最終結果 ---
237
  progress(1, desc="打包結果中...")
238
  zip_filename = run_output_dir / f"run_{run_id[:8]}_results.zip"
239
  created_zip_path = create_zip_archive(all_result_files, str(zip_filename))
240
 
241
- final_status = (
242
- f"處理完成!共 {total_images} 張圖片。結果儲存於: {run_output_dir.absolute()}"
243
- )
244
  combined_text_output = "\n\n".join(all_texts)
 
 
 
245
 
246
  yield {
247
  output_status: gr.update(value=final_status),
248
  download_button: gr.update(value=created_zip_path, visible=True),
249
  output_text: gr.update(value=combined_text_output),
250
- output_gallery: gr.update(
251
- value=annotated_image_paths
252
- ), # 確保最終 gallery 也被更新
253
  }
254
 
255
-
256
  def toggle_mllm_prompt(is_enabled):
257
  """
258
  根據 Checkbox 狀態,顯示或隱藏 MLLM prompt 輸入框。
259
  """
260
  return gr.update(visible=is_enabled)
261
 
262
-
263
  # --- Gradio Interface ---
264
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
265
- str1 = os.getenv("mongo_1")
266
- str2 = os.getenv("mongo_2")
267
- str3 = os.getenv("mongo_3")
268
  gr.Markdown("# 智慧影像分析工具 (YOLO + MLLM)")
269
- gr.Markdown("上傳圖片與YOLO模型進行物件偵測,並可選用MLLM進行進階圖像理解。")
270
- gr.Markdown("Str1=" + str1)
271
- gr.Markdown("Str2=" + str2)
272
- gr.Markdown("Str3=" + str3)
273
 
274
  with gr.Row():
275
  with gr.Column(scale=1):
276
  # 輸入元件
277
- image_input = gr.File(
278
- label="上傳圖片", file_count="multiple", file_types=["image"]
279
- )
280
- # model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"], info="若不提供,將使用預設的 yolov8n.pt 模型。")
281
- model_input = gr.File(
282
- label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"]
283
- )
284
-
285
  with gr.Accordion("進階設定", open=False):
286
- conf_slider = gr.Slider(
287
- minimum=0.1, maximum=1, value=0.40, step=0.05, label="信賴度閾值"
288
- )
289
  mllm_enabled_checkbox = gr.Checkbox(label="開啟MLLM辨識", value=False)
290
- mllm_prompt_input = gr.Textbox(
291
- label="自訂 MLLM Prompt (選填)",
292
- placeholder="例如:請描述圖中人物的穿著與場景。",
293
- visible=False,
294
- )
295
 
296
  run_button = gr.Button("開始辨識", variant="primary")
297
 
298
  with gr.Column(scale=2):
299
  # 輸出元件
300
- output_gallery = gr.Gallery(
301
- label="辨識結果預覽",
302
- height=500,
303
- object_fit="contain",
304
- allow_preview=True,
305
- )
306
- output_text = gr.Textbox(
307
- label="詳細辨識資訊",
308
- lines=15,
309
- placeholder="辨識完成後,所有結果將顯示於此。",
310
- )
311
  output_status = gr.Textbox(label="執行狀態", interactive=False)
312
- download_button = gr.File(
313
- label="下載所有結果 (.zip)", file_count="single", visible=False
314
- )
315
 
316
  # --- 事件綁定 ---
317
-
318
  # 點擊 "開始辨識" 按鈕
319
  run_button.click(
320
  fn=gradio_multi_model_detection,
321
- inputs=[
322
- image_input,
323
- model_input,
324
- conf_slider,
325
- mllm_enabled_checkbox,
326
- mllm_prompt_input,
327
- ],
328
- outputs=[output_gallery, output_status, download_button, output_text],
329
  )
330
 
331
  # 勾選/取消 "開啟MLLM辨識"
332
  mllm_enabled_checkbox.change(
333
- fn=toggle_mllm_prompt, inputs=mllm_enabled_checkbox, outputs=mllm_prompt_input
 
 
334
  )
335
 
336
  # 啟動 Gradio 應用
337
  if __name__ == "__main__":
338
  demo.launch(debug=True)
 
 
15
  import shutil
16
  import zipfile
17
  import uuid # 匯入 uuid 以生成唯一的執行 ID
18
+ from pathlib import Path # 匯入 Path 以更方便地操作路徑
 
 
19
  import gemini_ai as genai
20
+ from datetime import datetime
21
+ import mongo_lib as mongo
22
+
23
+
24
 
25
 
26
  def create_zip_archive(files, zip_filename):
 
34
  Returns:
35
  str: 產生的 zip 檔案路徑。
36
  """
37
+ with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
38
  for file in files:
39
  if os.path.exists(file):
40
  # 使用 os.path.basename 確保只寫入檔案名稱,而非完整路徑
 
43
  print(f"警告: 檔案 '{file}' 不存在,無法加入壓縮檔。")
44
  return zip_filename
45
 
 
46
  def gradio_multi_model_detection(
47
  image_files,
48
  model_files,
49
  conf_threshold,
50
  enable_mllm,
51
  mllm_prompt,
52
+ progress=gr.Progress(track_tqdm=True)
53
  ):
54
  """
55
  Gradio 的主要處理函式,使用生成器 (yield) 實現流式輸出。
 
65
  Yields:
66
  dict: 用於更新 Gradio 介面元件的字典。
67
  """
68
+ global_datetime = datetime.now()
69
+
70
+ #寫主表log
71
+ document = {"log_style":"master",
72
+ "create_datetime": str(global_datetime),
73
+ "image_files": image_files,
74
+ "model_files": model_files,
75
+ "conf_threshold":conf_threshold,
76
+ "enable_mllm":enable_mllm,
77
+ "mllm_prompt":mllm_prompt
78
+ }
79
+
80
+ mongo.insert_mongodb_log("multi_model_detection",document) #寫入log方便日後查驗
81
+
82
  if not image_files:
83
  yield {
84
  output_status: gr.update(value="錯誤:請至少上傳一張圖片。"),
85
  output_gallery: None,
86
  output_text: None,
87
+ download_button: None
88
  }
89
  return
90
 
91
  # --- 1. 初始化設定 ---
92
  # 為本次執行創建一個唯一的子目錄
93
  run_id = str(uuid.uuid4())
94
+ base_output_dir = Path('gradio_detection_results')
95
  run_output_dir = base_output_dir / f"run_{run_id[:8]}"
96
  run_output_dir.mkdir(parents=True, exist_ok=True)
97
 
 
103
  loaded_models = []
104
  if not model_paths:
105
  # 如果沒有上傳模型,使用預設模型
106
+ default_model_path = 'yolov8n.pt'
107
  try:
108
  model = YOLO(default_model_path)
109
  loaded_models.append((default_model_path, model))
110
  except Exception as e:
111
+ yield {output_status: gr.update(value=f"錯誤: 無法載入預設模型 '{default_model_path}' - {e}")}
 
 
 
 
112
  return
113
  else:
114
  for model_path in model_paths:
 
136
  image_path = Path(image_path_str)
137
  progress(i / total_images, desc=f"處理中: {image_path.name}")
138
  yield {
139
+ output_status: gr.update(value=f"處理中... ({i+1}/{total_images}) - {image_path.name}"),
140
+ output_gallery: gr.update(value=annotated_image_paths)
 
 
141
  }
142
 
143
  original_image = cv2.imread(str(image_path))
144
  if original_image is None:
145
  print(f"警告: 無法讀取圖片 '{image_path}',跳過。")
146
  continue
147
+
148
  annotated_image = original_image.copy()
149
  image_base_name = image_path.stem
150
 
151
  # --- 3a. YOLO 物件偵測 ---
152
  yolo_output_content = [f"--- 檔案: {image_path.name} ---"]
153
  all_detections_for_image = []
154
+
155
  for model_path_str, model_obj in loaded_models:
156
  model_name = Path(model_path_str).name
157
  yolo_output_content.append(f"--- 模型: {model_name} ---")
 
164
  x1, y1, x2, y2 = map(int, box.xyxy[0])
165
  cls_id = int(box.cls[0])
166
  cls_name = model_obj.names[cls_id]
167
+
168
+ detection_info = {'model_name': model_name, 'class_name': cls_name, 'confidence': conf, 'bbox': (x1, y1, x2, y2)}
 
 
 
 
 
169
  all_detections_for_image.append(detection_info)
170
+ yolo_output_content.append(f" - {cls_name} (信賴度: {conf:.2f}) [座標: {x1},{y1},{x2},{y2}]")
 
 
171
  else:
172
  yolo_output_content.append(" 未偵測到任何物件。")
173
 
174
  # 繪製偵測框
175
+ colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (0, 255, 255)]
176
+ color_map = {Path(p).name: colors[idx % len(colors)] for idx, (p, _) in enumerate(loaded_models)}
 
 
 
 
 
 
 
 
 
 
177
  for det in all_detections_for_image:
178
+ x1, y1, x2, y2 = det['bbox']
179
+ color = color_map.get(det['model_name'], (200, 200, 200))
180
  label = f"{det['class_name']} {det['confidence']:.2f}"
181
  cv2.rectangle(annotated_image, (x1, y1), (x2, y2), color, 2)
182
+ cv2.putText(annotated_image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
 
 
 
 
 
 
 
 
183
 
184
  # 儲存 YOLO 標註圖
185
  output_image_path = run_output_dir / f"{image_base_name}_yolo_detected.jpg"
 
189
 
190
  # 儲存 YOLO 辨識資訊
191
  output_yolo_txt_path = run_output_dir / f"{image_base_name}_yolo_objects.txt"
192
+ output_yolo_txt_path.write_text("\n".join(yolo_output_content), encoding='utf-8')
 
 
193
  all_result_files.append(str(output_yolo_txt_path))
194
 
195
  # --- 3b. MLLM 分析 (如果啟用) ---
196
  output_mllm_txt_path = None
197
+ mllm_result_content = ""
198
  if enable_mllm:
199
  try:
200
+ prompt_to_use = mllm_prompt if mllm_prompt and mllm_prompt.strip() else None
201
+ mllm_str = genai.analyze_content_with_gemini(str(image_path), prompt_to_use)
 
 
 
 
202
  mllm_result_content = f"--- MLLM 分析結果 ---\n{mllm_str}"
203
  except Exception as e:
204
  mllm_result_content = f"--- MLLM 分析失敗 ---\n原因: {e}"
205
+
206
  output_mllm_txt_path = run_output_dir / f"{image_base_name}_mllm_result.txt"
207
+ output_mllm_txt_path.write_text(mllm_result_content, encoding='utf-8')
208
  all_result_files.append(str(output_mllm_txt_path))
209
+
210
+ #寫明細表log
211
+ document = {"log_style":"detail",
212
+ "create_datetime": str(global_datetime),
213
+ "image_path": str(image_path),
214
+ "yolo_result": yolo_output_content,
215
+ "enable_mllm": enable_mllm,
216
+ "mllm_prompt": mllm_prompt,
217
+ "mllm_result": mllm_result_content}
218
+
219
+ mongo.insert_mongodb_log("multi_model_detection",document) #寫入log方便日後查驗
220
 
221
  # 將本次圖片的結果加入到總列表中
222
  all_texts.append("\n".join(yolo_output_content))
223
  if output_mllm_txt_path:
224
+ all_texts.append(output_mllm_txt_path.read_text(encoding='utf-8'))
225
+
226
 
227
  # --- 4. 完成處理,打包並更新最終結果 ---
228
  progress(1, desc="打包結果中...")
229
  zip_filename = run_output_dir / f"run_{run_id[:8]}_results.zip"
230
  created_zip_path = create_zip_archive(all_result_files, str(zip_filename))
231
 
232
+ final_status = f"處理完成!共 {total_images} 張圖片。結果儲存於: {run_output_dir.absolute()}"
 
 
233
  combined_text_output = "\n\n".join(all_texts)
234
+
235
+
236
+
237
 
238
  yield {
239
  output_status: gr.update(value=final_status),
240
  download_button: gr.update(value=created_zip_path, visible=True),
241
  output_text: gr.update(value=combined_text_output),
242
+ output_gallery: gr.update(value=annotated_image_paths) # 確保最終 gallery 也被更新
 
 
243
  }
244
 
 
245
  def toggle_mllm_prompt(is_enabled):
246
  """
247
  根據 Checkbox 狀態,顯示或隱藏 MLLM prompt 輸入框。
248
  """
249
  return gr.update(visible=is_enabled)
250
 
 
251
  # --- Gradio Interface ---
252
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
 
 
253
  gr.Markdown("# 智慧影像分析工具 (YOLO + MLLM)")
254
+ gr.Markdown("上傳圖片與YOLO模型進行物件偵測,並可選用MLLM進行進階圖像理解。 ver.250824.1")
255
+ # mongo_uri = os.getenv('mongo_uri')
256
+ # gr.Markdown(mongo_uri)
 
257
 
258
  with gr.Row():
259
  with gr.Column(scale=1):
260
  # 輸入元件
261
+ image_input = gr.File(label="上傳圖片", file_count="multiple", file_types=["image"])
262
+ #model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"], info="若不提供,將使用預設的 yolov8n.pt 模型。")
263
+ model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"])
264
+
 
 
 
 
265
  with gr.Accordion("進階設定", open=False):
266
+ conf_slider = gr.Slider(minimum=0.1, maximum=1, value=0.40, step=0.05, label="信賴度閾值")
 
 
267
  mllm_enabled_checkbox = gr.Checkbox(label="開啟MLLM辨識", value=False)
268
+ mllm_prompt_input = gr.Textbox(label="自訂 MLLM Prompt (選填)", placeholder="例如:請描述圖中人物的穿著與場景。", visible=False)
 
 
 
 
269
 
270
  run_button = gr.Button("開始辨識", variant="primary")
271
 
272
  with gr.Column(scale=2):
273
  # 輸出元件
274
+ output_gallery = gr.Gallery(label="辨識結果預覽", height=500, object_fit="contain", allow_preview=True)
275
+ output_text = gr.Textbox(label="詳細辨識資訊", lines=15, placeholder="辨識完成後,所有結果將顯示於此。")
 
 
 
 
 
 
 
 
 
276
  output_status = gr.Textbox(label="執行狀態", interactive=False)
277
+ download_button = gr.File(label="下載所有結果 (.zip)", file_count="single", visible=False)
 
 
278
 
279
  # --- 事件綁定 ---
280
+
281
  # 點擊 "開始辨識" 按鈕
282
  run_button.click(
283
  fn=gradio_multi_model_detection,
284
+ inputs=[image_input, model_input, conf_slider, mllm_enabled_checkbox, mllm_prompt_input],
285
+ outputs=[output_gallery, output_status, download_button, output_text]
 
 
 
 
 
 
286
  )
287
 
288
  # 勾選/取消 "開啟MLLM辨識"
289
  mllm_enabled_checkbox.change(
290
+ fn=toggle_mllm_prompt,
291
+ inputs=mllm_enabled_checkbox,
292
+ outputs=mllm_prompt_input
293
  )
294
 
295
  # 啟動 Gradio 應用
296
  if __name__ == "__main__":
297
  demo.launch(debug=True)
298
+ #demo.launch(share=True)
main_ver2.py CHANGED
@@ -15,10 +15,12 @@ from ultralytics import YOLO
15
  import shutil
16
  import zipfile
17
  import uuid # 匯入 uuid 以生成唯一的執行 ID
18
- from pathlib import Path # 匯入 Path 以更方便地操作路徑
19
-
20
- # 假設 gemini_ai.py 在同一個目錄或 Python 路徑中
21
  import gemini_ai as genai
 
 
 
 
22
 
23
 
24
  def create_zip_archive(files, zip_filename):
@@ -32,7 +34,7 @@ def create_zip_archive(files, zip_filename):
32
  Returns:
33
  str: 產生的 zip 檔案路徑。
34
  """
35
- with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
36
  for file in files:
37
  if os.path.exists(file):
38
  # 使用 os.path.basename 確保只寫入檔案名稱,而非完整路徑
@@ -41,14 +43,13 @@ def create_zip_archive(files, zip_filename):
41
  print(f"警告: 檔案 '{file}' 不存在,無法加入壓縮檔。")
42
  return zip_filename
43
 
44
-
45
  def gradio_multi_model_detection(
46
  image_files,
47
  model_files,
48
  conf_threshold,
49
  enable_mllm,
50
  mllm_prompt,
51
- progress=gr.Progress(track_tqdm=True),
52
  ):
53
  """
54
  Gradio 的主要處理函式,使用生成器 (yield) 實現流式輸出。
@@ -64,19 +65,33 @@ def gradio_multi_model_detection(
64
  Yields:
65
  dict: 用於更新 Gradio 介面元件的字典。
66
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  if not image_files:
68
  yield {
69
  output_status: gr.update(value="錯誤:請至少上傳一張圖片。"),
70
  output_gallery: None,
71
  output_text: None,
72
- download_button: None,
73
  }
74
  return
75
 
76
  # --- 1. 初始化設定 ---
77
  # 為本次執行創建一個唯一的子目錄
78
  run_id = str(uuid.uuid4())
79
- base_output_dir = Path("gradio_detection_results")
80
  run_output_dir = base_output_dir / f"run_{run_id[:8]}"
81
  run_output_dir.mkdir(parents=True, exist_ok=True)
82
 
@@ -88,16 +103,12 @@ def gradio_multi_model_detection(
88
  loaded_models = []
89
  if not model_paths:
90
  # 如果沒有上傳模型,使用預設模型
91
- default_model_path = "yolov8n.pt"
92
  try:
93
  model = YOLO(default_model_path)
94
  loaded_models.append((default_model_path, model))
95
  except Exception as e:
96
- yield {
97
- output_status: gr.update(
98
- value=f"錯誤: 無法載入預設模型 '{default_model_path}' - {e}"
99
- )
100
- }
101
  return
102
  else:
103
  for model_path in model_paths:
@@ -125,24 +136,22 @@ def gradio_multi_model_detection(
125
  image_path = Path(image_path_str)
126
  progress(i / total_images, desc=f"處理中: {image_path.name}")
127
  yield {
128
- output_status: gr.update(
129
- value=f"處理中... ({i+1}/{total_images}) - {image_path.name}"
130
- ),
131
- output_gallery: gr.update(value=annotated_image_paths),
132
  }
133
 
134
  original_image = cv2.imread(str(image_path))
135
  if original_image is None:
136
  print(f"警告: 無法讀取圖片 '{image_path}',跳過。")
137
  continue
138
-
139
  annotated_image = original_image.copy()
140
  image_base_name = image_path.stem
141
 
142
  # --- 3a. YOLO 物件偵測 ---
143
  yolo_output_content = [f"--- 檔案: {image_path.name} ---"]
144
  all_detections_for_image = []
145
-
146
  for model_path_str, model_obj in loaded_models:
147
  model_name = Path(model_path_str).name
148
  yolo_output_content.append(f"--- 模型: {model_name} ---")
@@ -155,47 +164,22 @@ def gradio_multi_model_detection(
155
  x1, y1, x2, y2 = map(int, box.xyxy[0])
156
  cls_id = int(box.cls[0])
157
  cls_name = model_obj.names[cls_id]
158
-
159
- detection_info = {
160
- "model_name": model_name,
161
- "class_name": cls_name,
162
- "confidence": conf,
163
- "bbox": (x1, y1, x2, y2),
164
- }
165
  all_detections_for_image.append(detection_info)
166
- yolo_output_content.append(
167
- f" - {cls_name} (信賴度: {conf:.2f}) [座標: {x1},{y1},{x2},{y2}]"
168
- )
169
  else:
170
  yolo_output_content.append(" 未偵測到任何物件。")
171
 
172
  # 繪製偵測框
173
- colors = [
174
- (255, 0, 0),
175
- (0, 255, 0),
176
- (0, 0, 255),
177
- (255, 255, 0),
178
- (255, 0, 255),
179
- (0, 255, 255),
180
- ]
181
- color_map = {
182
- Path(p).name: colors[idx % len(colors)]
183
- for idx, (p, _) in enumerate(loaded_models)
184
- }
185
  for det in all_detections_for_image:
186
- x1, y1, x2, y2 = det["bbox"]
187
- color = color_map.get(det["model_name"], (200, 200, 200))
188
  label = f"{det['class_name']} {det['confidence']:.2f}"
189
  cv2.rectangle(annotated_image, (x1, y1), (x2, y2), color, 2)
190
- cv2.putText(
191
- annotated_image,
192
- label,
193
- (x1, y1 - 10),
194
- cv2.FONT_HERSHEY_SIMPLEX,
195
- 0.5,
196
- color,
197
- 2,
198
- )
199
 
200
  # 儲存 YOLO 標註圖
201
  output_image_path = run_output_dir / f"{image_base_name}_yolo_detected.jpg"
@@ -205,134 +189,110 @@ def gradio_multi_model_detection(
205
 
206
  # 儲存 YOLO 辨識資訊
207
  output_yolo_txt_path = run_output_dir / f"{image_base_name}_yolo_objects.txt"
208
- output_yolo_txt_path.write_text(
209
- "\n".join(yolo_output_content), encoding="utf-8"
210
- )
211
  all_result_files.append(str(output_yolo_txt_path))
212
 
213
  # --- 3b. MLLM 分析 (如果啟用) ---
214
  output_mllm_txt_path = None
 
215
  if enable_mllm:
216
  try:
217
- prompt_to_use = (
218
- mllm_prompt if mllm_prompt and mllm_prompt.strip() else None
219
- )
220
- mllm_str = genai.analyze_content_with_gemini(
221
- str(image_path), prompt_to_use
222
- )
223
  mllm_result_content = f"--- MLLM 分析結果 ---\n{mllm_str}"
224
  except Exception as e:
225
  mllm_result_content = f"--- MLLM 分析失敗 ---\n原因: {e}"
226
-
227
  output_mllm_txt_path = run_output_dir / f"{image_base_name}_mllm_result.txt"
228
- output_mllm_txt_path.write_text(mllm_result_content, encoding="utf-8")
229
  all_result_files.append(str(output_mllm_txt_path))
 
 
 
 
 
 
 
 
 
 
 
230
 
231
  # 將本次圖片的結果加入到總列表中
232
  all_texts.append("\n".join(yolo_output_content))
233
  if output_mllm_txt_path:
234
- all_texts.append(output_mllm_txt_path.read_text(encoding="utf-8"))
 
235
 
236
  # --- 4. 完成處理,打包並更新最終結果 ---
237
  progress(1, desc="打包結果中...")
238
  zip_filename = run_output_dir / f"run_{run_id[:8]}_results.zip"
239
  created_zip_path = create_zip_archive(all_result_files, str(zip_filename))
240
 
241
- final_status = (
242
- f"處理完成!共 {total_images} 張圖片。結果儲存於: {run_output_dir.absolute()}"
243
- )
244
  combined_text_output = "\n\n".join(all_texts)
 
 
 
245
 
246
  yield {
247
  output_status: gr.update(value=final_status),
248
  download_button: gr.update(value=created_zip_path, visible=True),
249
  output_text: gr.update(value=combined_text_output),
250
- output_gallery: gr.update(
251
- value=annotated_image_paths
252
- ), # 確保最終 gallery 也被更新
253
  }
254
 
255
-
256
  def toggle_mllm_prompt(is_enabled):
257
  """
258
  根據 Checkbox 狀態,顯示或隱藏 MLLM prompt 輸入框。
259
  """
260
  return gr.update(visible=is_enabled)
261
 
262
-
263
  # --- Gradio Interface ---
264
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
265
- str1 = os.getenv("mongo_1")
266
- str2 = os.getenv("mongo_2")
267
- str3 = os.getenv("mongo_3")
268
  gr.Markdown("# 智慧影像分析工具 (YOLO + MLLM)")
269
- gr.Markdown("上傳圖片與YOLO模型進行物件偵測,並可選用MLLM進行進階圖像理解。")
270
- gr.Markdown("Str1=" + str1)
271
- gr.Markdown("Str2=" + str2)
272
- gr.Markdown("Str3=" + str3)
273
 
274
  with gr.Row():
275
  with gr.Column(scale=1):
276
  # 輸入元件
277
- image_input = gr.File(
278
- label="上傳圖片", file_count="multiple", file_types=["image"]
279
- )
280
- # model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"], info="若不提供,將使用預設的 yolov8n.pt 模型。")
281
- model_input = gr.File(
282
- label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"]
283
- )
284
-
285
  with gr.Accordion("進階設定", open=False):
286
- conf_slider = gr.Slider(
287
- minimum=0.1, maximum=1, value=0.40, step=0.05, label="信賴度閾值"
288
- )
289
  mllm_enabled_checkbox = gr.Checkbox(label="開啟MLLM辨識", value=False)
290
- mllm_prompt_input = gr.Textbox(
291
- label="自訂 MLLM Prompt (選填)",
292
- placeholder="例如:請描述圖中人物的穿著與場景。",
293
- visible=False,
294
- )
295
 
296
  run_button = gr.Button("開始辨識", variant="primary")
297
 
298
  with gr.Column(scale=2):
299
  # 輸出元件
300
- output_gallery = gr.Gallery(
301
- label="辨識結果預覽",
302
- height=500,
303
- object_fit="contain",
304
- allow_preview=True,
305
- )
306
- output_text = gr.Textbox(
307
- label="詳細辨識資訊",
308
- lines=15,
309
- placeholder="辨識完成後,所有結果將顯示於此。",
310
- )
311
  output_status = gr.Textbox(label="執行狀態", interactive=False)
312
- download_button = gr.File(
313
- label="下載所有結果 (.zip)", file_count="single", visible=False
314
- )
315
 
316
  # --- 事件綁定 ---
317
-
318
  # 點擊 "開始辨識" 按鈕
319
  run_button.click(
320
  fn=gradio_multi_model_detection,
321
- inputs=[
322
- image_input,
323
- model_input,
324
- conf_slider,
325
- mllm_enabled_checkbox,
326
- mllm_prompt_input,
327
- ],
328
- outputs=[output_gallery, output_status, download_button, output_text],
329
  )
330
 
331
  # 勾選/取消 "開啟MLLM辨識"
332
  mllm_enabled_checkbox.change(
333
- fn=toggle_mllm_prompt, inputs=mllm_enabled_checkbox, outputs=mllm_prompt_input
 
 
334
  )
335
 
336
  # 啟動 Gradio 應用
337
  if __name__ == "__main__":
338
  demo.launch(debug=True)
 
 
15
  import shutil
16
  import zipfile
17
  import uuid # 匯入 uuid 以生成唯一的執行 ID
18
+ from pathlib import Path # 匯入 Path 以更方便地操作路徑
 
 
19
  import gemini_ai as genai
20
+ from datetime import datetime
21
+ import mongo_lib as mongo
22
+
23
+
24
 
25
 
26
  def create_zip_archive(files, zip_filename):
 
34
  Returns:
35
  str: 產生的 zip 檔案路徑。
36
  """
37
+ with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
38
  for file in files:
39
  if os.path.exists(file):
40
  # 使用 os.path.basename 確保只寫入檔案名稱,而非完整路徑
 
43
  print(f"警告: 檔案 '{file}' 不存在,無法加入壓縮檔。")
44
  return zip_filename
45
 
 
46
  def gradio_multi_model_detection(
47
  image_files,
48
  model_files,
49
  conf_threshold,
50
  enable_mllm,
51
  mllm_prompt,
52
+ progress=gr.Progress(track_tqdm=True)
53
  ):
54
  """
55
  Gradio 的主要處理函式,使用生成器 (yield) 實現流式輸出。
 
65
  Yields:
66
  dict: 用於更新 Gradio 介面元件的字典。
67
  """
68
+ global_datetime = datetime.now()
69
+
70
+ #寫主表log
71
+ document = {"log_style":"master",
72
+ "create_datetime": str(global_datetime),
73
+ "image_files": image_files,
74
+ "model_files": model_files,
75
+ "conf_threshold":conf_threshold,
76
+ "enable_mllm":enable_mllm,
77
+ "mllm_prompt":mllm_prompt
78
+ }
79
+
80
+ mongo.insert_mongodb_log("multi_model_detection",document) #寫入log方便日後查驗
81
+
82
  if not image_files:
83
  yield {
84
  output_status: gr.update(value="錯誤:請至少上傳一張圖片。"),
85
  output_gallery: None,
86
  output_text: None,
87
+ download_button: None
88
  }
89
  return
90
 
91
  # --- 1. 初始化設定 ---
92
  # 為本次執行創建一個唯一的子目錄
93
  run_id = str(uuid.uuid4())
94
+ base_output_dir = Path('gradio_detection_results')
95
  run_output_dir = base_output_dir / f"run_{run_id[:8]}"
96
  run_output_dir.mkdir(parents=True, exist_ok=True)
97
 
 
103
  loaded_models = []
104
  if not model_paths:
105
  # 如果沒有上傳模型,使用預設模型
106
+ default_model_path = 'yolov8n.pt'
107
  try:
108
  model = YOLO(default_model_path)
109
  loaded_models.append((default_model_path, model))
110
  except Exception as e:
111
+ yield {output_status: gr.update(value=f"錯誤: 無法載入預設模型 '{default_model_path}' - {e}")}
 
 
 
 
112
  return
113
  else:
114
  for model_path in model_paths:
 
136
  image_path = Path(image_path_str)
137
  progress(i / total_images, desc=f"處理中: {image_path.name}")
138
  yield {
139
+ output_status: gr.update(value=f"處理中... ({i+1}/{total_images}) - {image_path.name}"),
140
+ output_gallery: gr.update(value=annotated_image_paths)
 
 
141
  }
142
 
143
  original_image = cv2.imread(str(image_path))
144
  if original_image is None:
145
  print(f"警告: 無法讀取圖片 '{image_path}',跳過。")
146
  continue
147
+
148
  annotated_image = original_image.copy()
149
  image_base_name = image_path.stem
150
 
151
  # --- 3a. YOLO 物件偵測 ---
152
  yolo_output_content = [f"--- 檔案: {image_path.name} ---"]
153
  all_detections_for_image = []
154
+
155
  for model_path_str, model_obj in loaded_models:
156
  model_name = Path(model_path_str).name
157
  yolo_output_content.append(f"--- 模型: {model_name} ---")
 
164
  x1, y1, x2, y2 = map(int, box.xyxy[0])
165
  cls_id = int(box.cls[0])
166
  cls_name = model_obj.names[cls_id]
167
+
168
+ detection_info = {'model_name': model_name, 'class_name': cls_name, 'confidence': conf, 'bbox': (x1, y1, x2, y2)}
 
 
 
 
 
169
  all_detections_for_image.append(detection_info)
170
+ yolo_output_content.append(f" - {cls_name} (信賴度: {conf:.2f}) [座標: {x1},{y1},{x2},{y2}]")
 
 
171
  else:
172
  yolo_output_content.append(" 未偵測到任何物件。")
173
 
174
  # 繪製偵測框
175
+ colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (0, 255, 255)]
176
+ color_map = {Path(p).name: colors[idx % len(colors)] for idx, (p, _) in enumerate(loaded_models)}
 
 
 
 
 
 
 
 
 
 
177
  for det in all_detections_for_image:
178
+ x1, y1, x2, y2 = det['bbox']
179
+ color = color_map.get(det['model_name'], (200, 200, 200))
180
  label = f"{det['class_name']} {det['confidence']:.2f}"
181
  cv2.rectangle(annotated_image, (x1, y1), (x2, y2), color, 2)
182
+ cv2.putText(annotated_image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
 
 
 
 
 
 
 
 
183
 
184
  # 儲存 YOLO 標註圖
185
  output_image_path = run_output_dir / f"{image_base_name}_yolo_detected.jpg"
 
189
 
190
  # 儲存 YOLO 辨識資訊
191
  output_yolo_txt_path = run_output_dir / f"{image_base_name}_yolo_objects.txt"
192
+ output_yolo_txt_path.write_text("\n".join(yolo_output_content), encoding='utf-8')
 
 
193
  all_result_files.append(str(output_yolo_txt_path))
194
 
195
  # --- 3b. MLLM 分析 (如果啟用) ---
196
  output_mllm_txt_path = None
197
+ mllm_result_content = ""
198
  if enable_mllm:
199
  try:
200
+ prompt_to_use = mllm_prompt if mllm_prompt and mllm_prompt.strip() else None
201
+ mllm_str = genai.analyze_content_with_gemini(str(image_path), prompt_to_use)
 
 
 
 
202
  mllm_result_content = f"--- MLLM 分析結果 ---\n{mllm_str}"
203
  except Exception as e:
204
  mllm_result_content = f"--- MLLM 分析失敗 ---\n原因: {e}"
205
+
206
  output_mllm_txt_path = run_output_dir / f"{image_base_name}_mllm_result.txt"
207
+ output_mllm_txt_path.write_text(mllm_result_content, encoding='utf-8')
208
  all_result_files.append(str(output_mllm_txt_path))
209
+
210
+ #寫明細表log
211
+ document = {"log_style":"detail",
212
+ "create_datetime": str(global_datetime),
213
+ "image_path": str(image_path),
214
+ "yolo_result": yolo_output_content,
215
+ "enable_mllm": enable_mllm,
216
+ "mllm_prompt": mllm_prompt,
217
+ "mllm_result": mllm_result_content}
218
+
219
+ mongo.insert_mongodb_log("multi_model_detection",document) #寫入log方便日後查驗
220
 
221
  # 將本次圖片的結果加入到總列表中
222
  all_texts.append("\n".join(yolo_output_content))
223
  if output_mllm_txt_path:
224
+ all_texts.append(output_mllm_txt_path.read_text(encoding='utf-8'))
225
+
226
 
227
  # --- 4. 完成處理,打包並更新最終結果 ---
228
  progress(1, desc="打包結果中...")
229
  zip_filename = run_output_dir / f"run_{run_id[:8]}_results.zip"
230
  created_zip_path = create_zip_archive(all_result_files, str(zip_filename))
231
 
232
+ final_status = f"處理完成!共 {total_images} 張圖片。結果儲存於: {run_output_dir.absolute()}"
 
 
233
  combined_text_output = "\n\n".join(all_texts)
234
+
235
+
236
+
237
 
238
  yield {
239
  output_status: gr.update(value=final_status),
240
  download_button: gr.update(value=created_zip_path, visible=True),
241
  output_text: gr.update(value=combined_text_output),
242
+ output_gallery: gr.update(value=annotated_image_paths) # 確保最終 gallery 也被更新
 
 
243
  }
244
 
 
245
  def toggle_mllm_prompt(is_enabled):
246
  """
247
  根據 Checkbox 狀態,顯示或隱藏 MLLM prompt 輸入框。
248
  """
249
  return gr.update(visible=is_enabled)
250
 
 
251
  # --- Gradio Interface ---
252
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
 
 
253
  gr.Markdown("# 智慧影像分析工具 (YOLO + MLLM)")
254
+ gr.Markdown("上傳圖片與YOLO模型進行物件偵測,並可選用MLLM進行進階圖像理解。 ver.250814.1")
255
+ # mongo_uri = os.getenv('mongo_uri')
256
+ # gr.Markdown(mongo_uri)
 
257
 
258
  with gr.Row():
259
  with gr.Column(scale=1):
260
  # 輸入元件
261
+ image_input = gr.File(label="上傳圖片", file_count="multiple", file_types=["image"])
262
+ #model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"], info="若不提供,將使用預設的 yolov8n.pt 模型。")
263
+ model_input = gr.File(label="上傳YOLO模型 (.pt)", file_count="multiple", file_types=[".pt"])
264
+
 
 
 
 
265
  with gr.Accordion("進階設定", open=False):
266
+ conf_slider = gr.Slider(minimum=0.1, maximum=1, value=0.40, step=0.05, label="信賴度閾值")
 
 
267
  mllm_enabled_checkbox = gr.Checkbox(label="開啟MLLM辨識", value=False)
268
+ mllm_prompt_input = gr.Textbox(label="自訂 MLLM Prompt (選填)", placeholder="例如:請描述圖中人物的穿著與場景。", visible=False)
 
 
 
 
269
 
270
  run_button = gr.Button("開始辨識", variant="primary")
271
 
272
  with gr.Column(scale=2):
273
  # 輸出元件
274
+ output_gallery = gr.Gallery(label="辨識結果預覽", height=500, object_fit="contain", allow_preview=True)
275
+ output_text = gr.Textbox(label="詳細辨識資訊", lines=15, placeholder="辨識完成後,所有結果將顯示於此。")
 
 
 
 
 
 
 
 
 
276
  output_status = gr.Textbox(label="執行狀態", interactive=False)
277
+ download_button = gr.File(label="下載所有結果 (.zip)", file_count="single", visible=False)
 
 
278
 
279
  # --- 事件綁定 ---
280
+
281
  # 點擊 "開始辨識" 按鈕
282
  run_button.click(
283
  fn=gradio_multi_model_detection,
284
+ inputs=[image_input, model_input, conf_slider, mllm_enabled_checkbox, mllm_prompt_input],
285
+ outputs=[output_gallery, output_status, download_button, output_text]
 
 
 
 
 
 
286
  )
287
 
288
  # 勾選/取消 "開啟MLLM辨識"
289
  mllm_enabled_checkbox.change(
290
+ fn=toggle_mllm_prompt,
291
+ inputs=mllm_enabled_checkbox,
292
+ outputs=mllm_prompt_input
293
  )
294
 
295
  # 啟動 Gradio 應用
296
  if __name__ == "__main__":
297
  demo.launch(debug=True)
298
+ #demo.launch(share=True)
mongo_lib.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #python -m pip install "pymongo[srv]==3.11"
2
+ from pymongo.mongo_client import MongoClient
3
+ from datetime import datetime
4
+ import os
5
+
6
+ def insert_mongodb_log(model_name,document:dict):
7
+
8
+ mongo_uri = os.getenv('mongo_uri')
9
+ client = MongoClient(mongo_uri)
10
+
11
+ try:
12
+ #client.admin.command('ping')
13
+ # 選擇數據庫,如果不存在會自動創建
14
+ db = client["huggingface-space"] # 替換成你想要的數據庫名稱
15
+ # 選擇集合,如果不存在會自動創建
16
+ collection = db["space-log"] # 替換成你想要的集合名稱
17
+ # 將文檔插入到集合中
18
+ document.update({"model_name":model_name,"process_time": str(datetime.now())})
19
+
20
+ result = collection.insert_one(document)
21
+ # 打印插入結果
22
+ #print(f"Document inserted with ID: {result.inserted_id}")
23
+
24
+ except Exception as e:
25
+ print(e)
26
+
27
+
28
+
29
+ if __name__ == "__main__":
30
+ # 創建JSON文檔
31
+ document = {
32
+ "msg": "hello world",
33
+ "status": "success",
34
+ }
35
+
36
+ insert_mongodb_log("test_client",document)
37
+
38
+
output_images/1411032040-楊宗祥.jpg ADDED

Git LFS Details

  • SHA256: 363eb4f6e11ac17f543a3c701b21e78dce280c436d88cf9b57cfd42a169b50aa
  • Pointer size: 131 Bytes
  • Size of remote file: 358 kB
requirements.txt CHANGED
@@ -1,7 +1,11 @@
 
 
 
 
 
 
 
 
 
1
  google-generativeai
2
- Pillow
3
- gradio
4
- ultralytics
5
- numpy
6
- PyYAML
7
- transformers
 
1
+ gradio>=4.0.0
2
+ ultralytics>=8.0.0
3
+ opencv-python>=4.8.0
4
+ pillow>=10.0.0
5
+ torch>=2.0.0
6
+ torchvision>=0.15.0
7
+ numpy>=1.24.0
8
+ pathlib
9
+ transformers
10
  google-generativeai
11
+ pymongo[srv]==3.11
 
 
 
 
 
target_object.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # target_JSON = {
2
+ # "物理環境": ["辦公室", "臥室", "工作室", "工廠"],
3
+ # "技術應用": ["人工智慧", "虛擬實境", "大數據分析", "其他"],
4
+ # "社交關係": ["獨立工作(1人)", "團隊合作(2人以上)", "遠程協作(遠端控制)"],
5
+ # "職業情感": ["快樂", "睡覺", "壓力/焦慮", "成就感"],
6
+ # "資訊設備": ["AI助手", "投影儀", "手機", "眼鏡投影",
7
+ # "智慧手錶", "機械手臂", "平板",
8
+ # "電腦", "鍵盤", "滑鼠", "其他"],
9
+ # "物體": ["床", "椅子", "桌子",
10
+ # "書架", "PC",
11
+ # "肖像", "監視器",
12
+ # "窗戶", "冷氣機","其他"],
13
+ # "角色": ["機器人","教師","學生","動物","工作人員"]
14
+ # }
15
+ target_JSON ={
16
+ "目標物": [
17
+ "物理環境_辦公室",
18
+ "物理環境_臥室",
19
+ "物理環境_工作室",
20
+ "物理環境_工廠",
21
+ "技術應用_人工智慧",
22
+ "技術應用_虛擬實境",
23
+ "技術應用_大數據分析",
24
+ "技術應用_其他",
25
+ "社交關係_獨立工作(1人)",
26
+ "社交關係_團隊合作(2人以上)",
27
+ "社交關係_遠程協作(遠端控制)",
28
+ "職業情感_快樂",
29
+ "職業情感_睡覺",
30
+ "職業情感_壓力/焦慮",
31
+ "職業情感_成就感",
32
+ "資訊設備_AI助手",
33
+ "資訊設備_投影儀",
34
+ "資訊設備_手機",
35
+ "資訊設備_眼鏡投影",
36
+ "資訊設備_智慧手錶",
37
+ "資訊設備_機械手臂",
38
+ "資訊設備_平板",
39
+ "資訊設備_電腦",
40
+ "資訊設備_鍵盤",
41
+ "資訊設備_滑鼠",
42
+ "資訊設備_其他",
43
+ "物體_床",
44
+ "物體_椅子",
45
+ "物體_桌子",
46
+ "物體_書架",
47
+ "物體_PC",
48
+ "物體_肖像",
49
+ "物體_監視器",
50
+ "物體_窗戶",
51
+ "物體_冷氣機",
52
+ "物體_其他",
53
+ "角色_機器人",
54
+ "角色_教師",
55
+ "角色_學生",
56
+ "角色_動物",
57
+ "角色_工作人員"
58
+ ]
59
+ }