DawnC commited on
Commit
ccb675d
1 Parent(s): 14d0efa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -114
app.py CHANGED
@@ -249,19 +249,19 @@ def get_akc_breeds_link():
249
  # if __name__ == "__main__":
250
  # iface.launch()
251
 
252
- def format_description(description, breed, is_multi_dog=False, dog_number=None):
 
 
253
  if isinstance(description, dict):
254
- formatted_description = "\n\n".join([f"**{key}**: {value}" for key, value in description.items() if key != "Breed"])
255
  else:
256
  formatted_description = description
257
 
258
- header = f"**Dog {dog_number}: {breed}**\n\n" if is_multi_dog else f"**Breed: {breed}**\n\n"
259
-
260
  formatted_description = f"""
261
- {header}
262
  {formatted_description}
263
 
264
- **Want to learn more about dog breeds?**
265
  [Visit the AKC dog breeds page]({get_akc_breeds_link()}) and search for {breed} to find detailed information.
266
 
267
  *Disclaimer: The external link provided leads to the American Kennel Club (AKC) dog breeds page.
@@ -272,9 +272,14 @@ Please refer to the AKC's terms of use and privacy policy.*
272
  return formatted_description
273
 
274
  async def predict_single_dog(image):
 
 
 
 
 
275
  image_tensor = preprocess_image(image)
276
  with torch.no_grad():
277
- output = model(image_tensor.to(device))
278
  logits = output[0] if isinstance(output, tuple) else output
279
  probabilities = F.softmax(logits, dim=1)
280
  topk_probs, topk_indices = torch.topk(probabilities, k=3)
@@ -283,34 +288,22 @@ async def predict_single_dog(image):
283
  topk_probs_percent = [f"{prob.item() * 100:.2f}%" for prob in topk_probs[0]]
284
  return top1_prob, topk_breeds, topk_probs_percent
285
 
286
-
287
  async def detect_multiple_dogs(image):
288
- try:
289
- img = image.copy()
290
- img.thumbnail((640, 640))
291
-
292
- results = model_yolo(img, conf=0.1) # 降低閾值以檢測更多狗
293
- dogs = []
294
- for result in results:
295
- for box in result.boxes:
296
- if box.cls == 16: # COCO dataset class for dog is 16
297
- xyxy = box.xyxy[0].tolist()
298
- confidence = box.conf.item()
299
- cropped_image = image.crop((xyxy[0], xyxy[1], xyxy[2], xyxy[3]))
300
- dogs.append((cropped_image, confidence, xyxy))
301
-
302
- # 如果只檢測到一隻狗,嘗試檢測其他可能的狗
303
- if len(dogs) == 1:
304
- # 使用整張圖像進行品種預測
305
- full_image_prob, full_image_breeds, _ = await predict_single_dog(image)
306
- if full_image_prob >= 0.3 and full_image_breeds[0] != dogs[0][0]:
307
- # 如果整張圖像的預測結果不同且置信度較高,添加為第二隻狗
308
- dogs.append((image, full_image_prob, [0, 0, image.width, image.height]))
309
-
310
- return dogs
311
- except Exception as e:
312
- print(f"Error in detect_multiple_dogs: {e}")
313
- return []
314
 
315
  async def predict(image):
316
  if image is None:
@@ -320,96 +313,60 @@ async def predict(image):
320
  if isinstance(image, np.ndarray):
321
  image = Image.fromarray(image)
322
 
 
323
  dogs = await detect_multiple_dogs(image)
324
 
325
  if len(dogs) == 0:
326
- # 如果沒有檢測到狗,嘗試對整張圖像進行預測
327
  top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(image)
328
- if top1_prob >= 0.3:
329
- return await process_single_dog_result(top1_prob, topk_breeds, topk_probs_percent, image, [0, 0, image.width, image.height])
330
- else:
331
- return "No dogs detected in the image. Please upload a clear image of a dog.", None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
332
- elif len(dogs) == 1:
333
- cropped_image, _, box = dogs[0]
334
- top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(cropped_image)
335
- return await process_single_dog_result(top1_prob, topk_breeds, topk_probs_percent, image, box)
336
- else:
337
- return await process_multiple_dogs_result(dogs, image)
338
-
339
- except Exception as e:
340
- return f"An error occurred: {str(e)}", None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
341
 
342
- async def process_multiple_dogs_result(dogs, image):
343
- annotated_image = image.copy()
344
- draw = ImageDraw.Draw(annotated_image)
345
- font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
346
-
347
- explanations = []
348
- buttons = []
349
-
350
- for i, (cropped_image, _, box) in enumerate(dogs, 1):
351
- top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(cropped_image)
352
-
353
- optimized_box = optimize_box(box, image.size)
354
- draw.rectangle(optimized_box, outline="red", width=3)
355
- draw.text((optimized_box[0], optimized_box[1]), f"Dog {i}", fill="yellow", font=font, stroke_width=2, stroke_fill="black")
356
-
357
- if top1_prob >= 0.2:
358
  breed = topk_breeds[0]
359
  description = get_dog_description(breed)
360
- explanation = f"Dog {i}: **{breed}**\n\n"
361
- explanation += "\n".join([f"**{key}**: {value}" for key, value in description.items() if key != "Breed"])
362
- explanation += f"\n\n**Want to learn more about dog breeds?** [Visit the AKC dog breeds page]({get_akc_breeds_link()}) and search for {breed} to find detailed information."
363
- explanations.append(explanation)
364
-
365
- if top1_prob < 0.5:
366
- buttons.append(f"More about Dog {i}: {breed}")
367
- buttons.append(f"More about Dog {i}: {topk_breeds[1]}")
368
- buttons.append(f"More about Dog {i}: {topk_breeds[2]}")
369
- else:
370
- explanations.append(f"Dog {i}: The image is unclear or the breed is not in the dataset. Please upload a clearer image of this dog.")
371
 
372
- final_explanation = "\n\n---\n\n".join(explanations)
373
-
374
- if not explanations:
375
- return "No dogs were confidently detected in the image. Please upload a clearer image of a dog.", annotated_image, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
376
-
377
- return final_explanation, annotated_image, gr.update(visible=bool(buttons), choices=buttons), gr.update(visible=False), gr.update(visible=False)
378
 
379
- async def process_single_dog_result(top1_prob, topk_breeds, topk_probs_percent, image, box):
380
- annotated_image = image.copy()
381
- draw = ImageDraw.Draw(annotated_image)
382
- optimized_box = optimize_box(box, image.size)
383
- draw.rectangle(optimized_box, outline="red", width=3)
384
- draw.text((optimized_box[0], optimized_box[1]), "Dog", fill="yellow", font=ImageFont.load_default())
385
 
386
- if top1_prob >= 0.5:
387
- breed = topk_breeds[0]
388
- description = get_dog_description(breed)
389
- formatted_description = format_description(description, breed)
390
- return formatted_description, annotated_image, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
391
- elif top1_prob >= 0.2:
392
- explanation = (
393
- f"The model couldn't confidently identify the breed. Here are the top 3 possible breeds:\n\n"
394
- f"1. **{topk_breeds[0]}** ({topk_probs_percent[0]} confidence)\n"
395
- f"2. **{topk_breeds[1]}** ({topk_probs_percent[1]} confidence)\n"
396
- f"3. **{topk_breeds[2]}** ({topk_probs_percent[2]} confidence)\n\n"
397
- "Click on a button to view more information about the breed."
398
- )
399
- breed_buttons = [f"More about {breed}" for breed in topk_breeds[:3]]
400
- return explanation, annotated_image, gr.update(visible=True, choices=breed_buttons), gr.update(visible=False), gr.update(visible=False)
401
- else:
402
- return "The image is unclear or the breed is not in the dataset. Please upload a clearer image of a dog.", annotated_image, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
403
-
404
- def optimize_box(box, image_size):
405
- x1, y1, x2, y2 = box
406
- w, h = image_size
407
- # 擴大邊界框以確保完整包含狗
408
- x1 = max(0, x1 - 10)
409
- y1 = max(0, y1 - 10)
410
- x2 = min(w, x2 + 10)
411
- y2 = min(h, y2 + 10)
412
- return [x1, y1, x2, y2]
413
 
414
  async def show_details(choice):
415
  if not choice:
 
249
  # if __name__ == "__main__":
250
  # iface.launch()
251
 
252
+
253
+ def format_description(description, breed):
254
+ # 分別將不同的屬性分開來顯示,保持結果的可讀性
255
  if isinstance(description, dict):
256
+ formatted_description = "\n".join([f"**{key}**: {value}" for key, value in description.items()])
257
  else:
258
  formatted_description = description
259
 
 
 
260
  formatted_description = f"""
261
+ **Breed**: {breed}
262
  {formatted_description}
263
 
264
+ **Want to learn more about dog breeds?**
265
  [Visit the AKC dog breeds page]({get_akc_breeds_link()}) and search for {breed} to find detailed information.
266
 
267
  *Disclaimer: The external link provided leads to the American Kennel Club (AKC) dog breeds page.
 
272
  return formatted_description
273
 
274
  async def predict_single_dog(image):
275
+ # 使用 asyncio.to_thread 將同步操作轉換為異步
276
+ return await asyncio.to_thread(_predict_single_dog, image)
277
+
278
+ def _predict_single_dog(image):
279
+ # 直接使用模型進行預測,無需通過 YOLO
280
  image_tensor = preprocess_image(image)
281
  with torch.no_grad():
282
+ output = model(image_tensor)
283
  logits = output[0] if isinstance(output, tuple) else output
284
  probabilities = F.softmax(logits, dim=1)
285
  topk_probs, topk_indices = torch.topk(probabilities, k=3)
 
288
  topk_probs_percent = [f"{prob.item() * 100:.2f}%" for prob in topk_probs[0]]
289
  return top1_prob, topk_breeds, topk_probs_percent
290
 
 
291
  async def detect_multiple_dogs(image):
292
+ # 使用 asyncio.to_thread 將同步操作轉換為異步
293
+ return await asyncio.to_thread(_detect_multiple_dogs, image)
294
+
295
+ def _detect_multiple_dogs(image):
296
+ # 使用 YOLO 檢測多隻狗
297
+ results = model_yolo(image)
298
+ dogs = []
299
+ for result in results:
300
+ for box in result.boxes:
301
+ if box.cls == 16: # COCO 資料集中狗的類別是 16
302
+ xyxy = box.xyxy[0].tolist()
303
+ confidence = box.conf.item()
304
+ cropped_image = image.crop((xyxy[0], xyxy[1], xyxy[2], xyxy[3]))
305
+ dogs.append((cropped_image, confidence, xyxy))
306
+ return dogs
 
 
 
 
 
 
 
 
 
 
 
307
 
308
  async def predict(image):
309
  if image is None:
 
313
  if isinstance(image, np.ndarray):
314
  image = Image.fromarray(image)
315
 
316
+ # 首先檢查圖片中是否有多隻狗
317
  dogs = await detect_multiple_dogs(image)
318
 
319
  if len(dogs) == 0:
320
+ # 沒有狗或 YOLO 未檢測到狗,使用單狗直接分類
321
  top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(image)
322
+ if top1_prob < 0.2:
323
+ return "The image is unclear or the breed is not in the dataset. Please upload a clearer image of a dog.", None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
324
+ breed = topk_breeds[0]
325
+ description = get_dog_description(breed)
326
+ formatted_description = format_description(description, breed)
327
+ return formatted_description, image, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
 
 
 
328
 
329
+ if len(dogs) == 1:
330
+ # 檢測到一隻狗時,直接分類不使用 YOLO 來節省時間
331
+ top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  breed = topk_breeds[0]
333
  description = get_dog_description(breed)
334
+ formatted_description = format_description(description, breed)
335
+ return formatted_description, image, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
 
 
 
 
 
336
 
337
+ # 若有多隻狗,則使用 YOLO 的檢測結果來處理
338
+ explanations = []
339
+ visible_buttons = []
340
+ annotated_image = image.copy()
341
+ draw = ImageDraw.Draw(annotated_image)
 
342
 
343
+ for i, (cropped_image, _, box) in enumerate(dogs):
344
+ top1_prob, topk_breeds, topk_probs_percent = await predict_single_dog(cropped_image)
 
 
 
 
345
 
346
+ draw.rectangle(box, outline="red", width=3)
347
+ draw.text((box[0], box[1]), f"Dog {i+1}", fill="red")
348
+
349
+ if top1_prob >= 0.5:
350
+ breed = topk_breeds[0]
351
+ description = get_dog_description(breed)
352
+ explanations.append(f"Dog {i+1}:\n{format_description(description, breed)}")
353
+ elif 0.2 <= top1_prob < 0.5:
354
+ explanation = f"""
355
+ Dog {i+1}: Detected with moderate confidence. Here are the top 3 possible breeds:
356
+ 1. **{topk_breeds[0]}** ({topk_probs_percent[0]})
357
+ 2. **{topk_breeds[1]}** ({topk_probs_percent[1]})
358
+ 3. **{topk_breeds[2]}** ({topk_probs_percent[2]})
359
+ """
360
+ explanations.append(explanation)
361
+ visible_buttons.extend([f"More about {topk_breeds[0]}", f"More about {topk_breeds[1]}", f"More about {topk_breeds[2]}"])
362
+ else:
363
+ explanations.append(f"Dog {i+1}: The image is unclear or the breed is not in the dataset.")
364
+
365
+ final_explanation = "\n\n".join(explanations)
366
+ return final_explanation, annotated_image, gr.update(visible=len(visible_buttons) >= 1, value=visible_buttons[0] if visible_buttons else ""), gr.update(visible=len(visible_buttons) >= 2, value=visible_buttons[1] if len(visible_buttons) >= 2 else ""), gr.update(visible=len(visible_buttons) >= 3, value=visible_buttons[2] if len(visible_buttons) >= 3 else "")
367
+
368
+ except Exception as e:
369
+ return f"An error occurred: {e}", None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
370
 
371
  async def show_details(choice):
372
  if not choice: