Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -27,6 +27,8 @@ from html_templates import (
|
|
27 |
format_single_dog_result,
|
28 |
format_multiple_breeds_result,
|
29 |
format_error_message,
|
|
|
|
|
30 |
format_warning_html,
|
31 |
format_multi_dog_container,
|
32 |
format_breed_details_html,
|
@@ -238,36 +240,85 @@ def predict_single_dog(image):
|
|
238 |
|
239 |
return probabilities[0], breeds[:3], relative_probs
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
@spaces.GPU
|
242 |
def detect_multiple_dogs(image, conf_threshold=0.3, iou_threshold=0.55):
|
|
|
|
|
|
|
243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
results = model_manager.yolo_model(image, conf=conf_threshold,
|
245 |
iou=iou_threshold)[0]
|
246 |
|
247 |
dogs = []
|
248 |
boxes = []
|
|
|
|
|
249 |
for box in results.boxes:
|
250 |
-
|
|
|
251 |
xyxy = box.xyxy[0].tolist()
|
252 |
confidence = box.conf.item()
|
253 |
-
boxes.append((xyxy, confidence))
|
254 |
|
255 |
if not boxes:
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
|
|
|
|
|
|
|
|
271 |
|
272 |
def non_max_suppression(boxes, iou_threshold):
|
273 |
keep = []
|
@@ -324,17 +375,137 @@ def create_breed_comparison(breed1: str, breed2: str) -> dict:
|
|
324 |
return comparison_data
|
325 |
|
326 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
def predict(image):
|
328 |
"""
|
329 |
-
|
330 |
-
|
|
|
331 |
Args:
|
332 |
-
image: PIL Image
|
333 |
-
|
334 |
Returns:
|
335 |
tuple: (html_output, annotated_image, initial_state)
|
336 |
"""
|
337 |
-
|
338 |
if image is None:
|
339 |
return format_warning_html("Please upload an image to start."), None, None
|
340 |
|
@@ -342,11 +513,11 @@ def predict(image):
|
|
342 |
if isinstance(image, np.ndarray):
|
343 |
image = Image.fromarray(image)
|
344 |
|
345 |
-
#
|
346 |
dogs = detect_multiple_dogs(image)
|
347 |
color_scheme = get_color_scheme(len(dogs) == 1)
|
348 |
|
349 |
-
#
|
350 |
annotated_image = image.copy()
|
351 |
draw = ImageDraw.Draw(annotated_image)
|
352 |
|
@@ -357,18 +528,18 @@ def predict(image):
|
|
357 |
|
358 |
dogs_info = ""
|
359 |
|
360 |
-
#
|
361 |
-
for i, (cropped_image, detection_confidence, box) in enumerate(dogs):
|
362 |
color = color_scheme if len(dogs) == 1 else color_scheme[i % len(color_scheme)]
|
363 |
|
364 |
-
#
|
365 |
draw.rectangle(box, outline=color, width=4)
|
366 |
-
label = f"Dog {i+1}"
|
367 |
label_bbox = draw.textbbox((0, 0), label, font=font)
|
368 |
label_width = label_bbox[2] - label_bbox[0]
|
369 |
label_height = label_bbox[3] - label_bbox[1]
|
370 |
|
371 |
-
#
|
372 |
label_x = box[0] + 5
|
373 |
label_y = box[1] + 5
|
374 |
draw.rectangle(
|
@@ -379,20 +550,23 @@ def predict(image):
|
|
379 |
)
|
380 |
draw.text((label_x, label_y), label, fill=color, font=font)
|
381 |
|
382 |
-
# Predict breed
|
383 |
-
top1_prob, topk_breeds, relative_probs = predict_single_dog(cropped_image)
|
384 |
-
combined_confidence = detection_confidence * top1_prob
|
385 |
-
|
386 |
-
# Format results based on confidence with error handling
|
387 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
if combined_confidence < 0.2:
|
389 |
-
dogs_info +=
|
390 |
elif top1_prob >= 0.45:
|
391 |
breed = topk_breeds[0]
|
392 |
description = get_dog_description(breed)
|
393 |
-
# Handle missing breed description
|
394 |
if description is None:
|
395 |
-
# 如果沒有描述,創建一個基本描述
|
396 |
description = {
|
397 |
"Name": breed,
|
398 |
"Size": "Unknown",
|
@@ -404,7 +578,6 @@ def predict(image):
|
|
404 |
}
|
405 |
dogs_info += format_single_dog_result(breed, description, color)
|
406 |
else:
|
407 |
-
# 修改format_multiple_breeds_result的調用,包含錯誤處理
|
408 |
dogs_info += format_multiple_breeds_result(
|
409 |
topk_breeds,
|
410 |
relative_probs,
|
@@ -422,12 +595,12 @@ def predict(image):
|
|
422 |
)
|
423 |
except Exception as e:
|
424 |
print(f"Error formatting results for dog {i+1}: {str(e)}")
|
425 |
-
dogs_info +=
|
426 |
|
427 |
-
#
|
428 |
html_output = format_multi_dog_container(dogs_info)
|
429 |
|
430 |
-
#
|
431 |
initial_state = {
|
432 |
"dogs_info": dogs_info,
|
433 |
"image": annotated_image,
|
|
|
27 |
format_single_dog_result,
|
28 |
format_multiple_breeds_result,
|
29 |
format_error_message,
|
30 |
+
format_unknown_breed_message,
|
31 |
+
format_not_dog_message,
|
32 |
format_warning_html,
|
33 |
format_multi_dog_container,
|
34 |
format_breed_details_html,
|
|
|
240 |
|
241 |
return probabilities[0], breeds[:3], relative_probs
|
242 |
|
243 |
+
# @spaces.GPU
|
244 |
+
# def detect_multiple_dogs(image, conf_threshold=0.3, iou_threshold=0.55):
|
245 |
+
|
246 |
+
# results = model_manager.yolo_model(image, conf=conf_threshold,
|
247 |
+
# iou=iou_threshold)[0]
|
248 |
+
|
249 |
+
# dogs = []
|
250 |
+
# boxes = []
|
251 |
+
# for box in results.boxes:
|
252 |
+
# if box.cls == 16: # COCO dataset class for dog is 16
|
253 |
+
# xyxy = box.xyxy[0].tolist()
|
254 |
+
# confidence = box.conf.item()
|
255 |
+
# boxes.append((xyxy, confidence))
|
256 |
+
|
257 |
+
# if not boxes:
|
258 |
+
# dogs.append((image, 1.0, [0, 0, image.width, image.height]))
|
259 |
+
# else:
|
260 |
+
# nms_boxes = non_max_suppression(boxes, iou_threshold)
|
261 |
+
|
262 |
+
# for box, confidence in nms_boxes:
|
263 |
+
# x1, y1, x2, y2 = box
|
264 |
+
# w, h = x2 - x1, y2 - y1
|
265 |
+
# x1 = max(0, x1 - w * 0.05)
|
266 |
+
# y1 = max(0, y1 - h * 0.05)
|
267 |
+
# x2 = min(image.width, x2 + w * 0.05)
|
268 |
+
# y2 = min(image.height, y2 + h * 0.05)
|
269 |
+
# cropped_image = image.crop((x1, y1, x2, y2))
|
270 |
+
# dogs.append((cropped_image, confidence, [x1, y1, x2, y2]))
|
271 |
+
|
272 |
+
# return dogs
|
273 |
+
|
274 |
@spaces.GPU
|
275 |
def detect_multiple_dogs(image, conf_threshold=0.3, iou_threshold=0.55):
|
276 |
+
"""
|
277 |
+
使用YOLO模型檢測圖片中的狗。
|
278 |
+
只保留被識別為狗(class 16)的物體,並標記它們的狀態。
|
279 |
|
280 |
+
Args:
|
281 |
+
image: PIL Image
|
282 |
+
conf_threshold: YOLO檢測的信心度閾值
|
283 |
+
iou_threshold: 非極大值抑制的IoU閾值
|
284 |
+
|
285 |
+
Returns:
|
286 |
+
list: 包含檢測到的狗的列表,每個元素是(cropped_image, confidence, box, is_dog)的元組
|
287 |
+
"""
|
288 |
results = model_manager.yolo_model(image, conf=conf_threshold,
|
289 |
iou=iou_threshold)[0]
|
290 |
|
291 |
dogs = []
|
292 |
boxes = []
|
293 |
+
|
294 |
+
# 只處理被識別為狗的物體
|
295 |
for box in results.boxes:
|
296 |
+
class_id = box.cls.item()
|
297 |
+
if class_id == 16: # COCO dataset中狗的類別是16
|
298 |
xyxy = box.xyxy[0].tolist()
|
299 |
confidence = box.conf.item()
|
300 |
+
boxes.append((xyxy, confidence, True)) # 加入is_dog標記
|
301 |
|
302 |
if not boxes:
|
303 |
+
# 如果沒有檢測到狗,返回整張圖片並標記為非狗
|
304 |
+
return [(image, 1.0, [0, 0, image.width, image.height], False)]
|
305 |
+
|
306 |
+
nms_boxes = non_max_suppression(boxes, iou_threshold)
|
307 |
+
detected_objects = []
|
308 |
+
|
309 |
+
# 處理每個檢測到的狗
|
310 |
+
for box, confidence, is_dog in nms_boxes:
|
311 |
+
x1, y1, x2, y2 = box
|
312 |
+
w, h = x2 - x1, y2 - y1
|
313 |
+
# 擴大檢測框範圍以包含完整的狗
|
314 |
+
x1 = max(0, x1 - w * 0.05)
|
315 |
+
y1 = max(0, y1 - h * 0.05)
|
316 |
+
x2 = min(image.width, x2 + w * 0.05)
|
317 |
+
y2 = min(image.height, y2 + h * 0.05)
|
318 |
+
cropped_image = image.crop((x1, y1, x2, y2))
|
319 |
+
detected_objects.append((cropped_image, confidence, [x1, y1, x2, y2], is_dog))
|
320 |
+
|
321 |
+
return detected_objects
|
322 |
|
323 |
def non_max_suppression(boxes, iou_threshold):
|
324 |
keep = []
|
|
|
375 |
return comparison_data
|
376 |
|
377 |
|
378 |
+
# def predict(image):
|
379 |
+
# """
|
380 |
+
# Main prediction function that handles both single and multiple dog detection.
|
381 |
+
|
382 |
+
# Args:
|
383 |
+
# image: PIL Image or numpy array
|
384 |
+
|
385 |
+
# Returns:
|
386 |
+
# tuple: (html_output, annotated_image, initial_state)
|
387 |
+
# """
|
388 |
+
|
389 |
+
# if image is None:
|
390 |
+
# return format_warning_html("Please upload an image to start."), None, None
|
391 |
+
|
392 |
+
# try:
|
393 |
+
# if isinstance(image, np.ndarray):
|
394 |
+
# image = Image.fromarray(image)
|
395 |
+
|
396 |
+
# # Detect dogs in the image
|
397 |
+
# dogs = detect_multiple_dogs(image)
|
398 |
+
# color_scheme = get_color_scheme(len(dogs) == 1)
|
399 |
+
|
400 |
+
# # Prepare for annotation
|
401 |
+
# annotated_image = image.copy()
|
402 |
+
# draw = ImageDraw.Draw(annotated_image)
|
403 |
+
|
404 |
+
# try:
|
405 |
+
# font = ImageFont.truetype("arial.ttf", 24)
|
406 |
+
# except:
|
407 |
+
# font = ImageFont.load_default()
|
408 |
+
|
409 |
+
# dogs_info = ""
|
410 |
+
|
411 |
+
# # Process each detected dog
|
412 |
+
# for i, (cropped_image, detection_confidence, box) in enumerate(dogs):
|
413 |
+
# color = color_scheme if len(dogs) == 1 else color_scheme[i % len(color_scheme)]
|
414 |
+
|
415 |
+
# # Draw box and label on image
|
416 |
+
# draw.rectangle(box, outline=color, width=4)
|
417 |
+
# label = f"Dog {i+1}"
|
418 |
+
# label_bbox = draw.textbbox((0, 0), label, font=font)
|
419 |
+
# label_width = label_bbox[2] - label_bbox[0]
|
420 |
+
# label_height = label_bbox[3] - label_bbox[1]
|
421 |
+
|
422 |
+
# # Draw label background and text
|
423 |
+
# label_x = box[0] + 5
|
424 |
+
# label_y = box[1] + 5
|
425 |
+
# draw.rectangle(
|
426 |
+
# [label_x - 2, label_y - 2, label_x + label_width + 4, label_y + label_height + 4],
|
427 |
+
# fill='white',
|
428 |
+
# outline=color,
|
429 |
+
# width=2
|
430 |
+
# )
|
431 |
+
# draw.text((label_x, label_y), label, fill=color, font=font)
|
432 |
+
|
433 |
+
# # Predict breed
|
434 |
+
# top1_prob, topk_breeds, relative_probs = predict_single_dog(cropped_image)
|
435 |
+
# combined_confidence = detection_confidence * top1_prob
|
436 |
+
|
437 |
+
# # Format results based on confidence with error handling
|
438 |
+
# try:
|
439 |
+
# if combined_confidence < 0.2:
|
440 |
+
# dogs_info += format_error_message(color, i+1)
|
441 |
+
# elif top1_prob >= 0.45:
|
442 |
+
# breed = topk_breeds[0]
|
443 |
+
# description = get_dog_description(breed)
|
444 |
+
# # Handle missing breed description
|
445 |
+
# if description is None:
|
446 |
+
# # 如果沒有描述,創建一個基本描述
|
447 |
+
# description = {
|
448 |
+
# "Name": breed,
|
449 |
+
# "Size": "Unknown",
|
450 |
+
# "Exercise Needs": "Unknown",
|
451 |
+
# "Grooming Needs": "Unknown",
|
452 |
+
# "Care Level": "Unknown",
|
453 |
+
# "Good with Children": "Unknown",
|
454 |
+
# "Description": f"Identified as {breed.replace('_', ' ')}"
|
455 |
+
# }
|
456 |
+
# dogs_info += format_single_dog_result(breed, description, color)
|
457 |
+
# else:
|
458 |
+
# # 修改format_multiple_breeds_result的調用,包含錯誤處理
|
459 |
+
# dogs_info += format_multiple_breeds_result(
|
460 |
+
# topk_breeds,
|
461 |
+
# relative_probs,
|
462 |
+
# color,
|
463 |
+
# i+1,
|
464 |
+
# lambda breed: get_dog_description(breed) or {
|
465 |
+
# "Name": breed,
|
466 |
+
# "Size": "Unknown",
|
467 |
+
# "Exercise Needs": "Unknown",
|
468 |
+
# "Grooming Needs": "Unknown",
|
469 |
+
# "Care Level": "Unknown",
|
470 |
+
# "Good with Children": "Unknown",
|
471 |
+
# "Description": f"Identified as {breed.replace('_', ' ')}"
|
472 |
+
# }
|
473 |
+
# )
|
474 |
+
# except Exception as e:
|
475 |
+
# print(f"Error formatting results for dog {i+1}: {str(e)}")
|
476 |
+
# dogs_info += format_error_message(color, i+1)
|
477 |
+
|
478 |
+
# # Wrap final HTML output
|
479 |
+
# html_output = format_multi_dog_container(dogs_info)
|
480 |
+
|
481 |
+
# # Prepare initial state
|
482 |
+
# initial_state = {
|
483 |
+
# "dogs_info": dogs_info,
|
484 |
+
# "image": annotated_image,
|
485 |
+
# "is_multi_dog": len(dogs) > 1,
|
486 |
+
# "html_output": html_output
|
487 |
+
# }
|
488 |
+
|
489 |
+
# return html_output, annotated_image, initial_state
|
490 |
+
|
491 |
+
# except Exception as e:
|
492 |
+
# error_msg = f"An error occurred: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
493 |
+
# print(error_msg)
|
494 |
+
# return format_warning_html(error_msg), None, None
|
495 |
+
|
496 |
+
|
497 |
+
@spaces.GPU
|
498 |
def predict(image):
|
499 |
"""
|
500 |
+
主要的預測函數,負責處理狗的檢測和品種辨識。
|
501 |
+
它整合了YOLO的物體檢測和專門的品種分類模型。
|
502 |
+
|
503 |
Args:
|
504 |
+
image: PIL Image 或 numpy array
|
505 |
+
|
506 |
Returns:
|
507 |
tuple: (html_output, annotated_image, initial_state)
|
508 |
"""
|
|
|
509 |
if image is None:
|
510 |
return format_warning_html("Please upload an image to start."), None, None
|
511 |
|
|
|
513 |
if isinstance(image, np.ndarray):
|
514 |
image = Image.fromarray(image)
|
515 |
|
516 |
+
# 檢測圖片中的狗
|
517 |
dogs = detect_multiple_dogs(image)
|
518 |
color_scheme = get_color_scheme(len(dogs) == 1)
|
519 |
|
520 |
+
# 準備標註
|
521 |
annotated_image = image.copy()
|
522 |
draw = ImageDraw.Draw(annotated_image)
|
523 |
|
|
|
528 |
|
529 |
dogs_info = ""
|
530 |
|
531 |
+
# 處理每個檢測到的物體
|
532 |
+
for i, (cropped_image, detection_confidence, box, is_dog) in enumerate(dogs):
|
533 |
color = color_scheme if len(dogs) == 1 else color_scheme[i % len(color_scheme)]
|
534 |
|
535 |
+
# 繪製框和標籤
|
536 |
draw.rectangle(box, outline=color, width=4)
|
537 |
+
label = f"Dog {i+1}" if is_dog else f"Object {i+1}"
|
538 |
label_bbox = draw.textbbox((0, 0), label, font=font)
|
539 |
label_width = label_bbox[2] - label_bbox[0]
|
540 |
label_height = label_bbox[3] - label_bbox[1]
|
541 |
|
542 |
+
# 繪製標籤背景和文字
|
543 |
label_x = box[0] + 5
|
544 |
label_y = box[1] + 5
|
545 |
draw.rectangle(
|
|
|
550 |
)
|
551 |
draw.text((label_x, label_y), label, fill=color, font=font)
|
552 |
|
|
|
|
|
|
|
|
|
|
|
553 |
try:
|
554 |
+
# 首先檢查是否為狗
|
555 |
+
if not is_dog:
|
556 |
+
dogs_info += format_not_dog_message(color, i+1)
|
557 |
+
continue
|
558 |
+
|
559 |
+
# 如果是狗,進行品種預測
|
560 |
+
top1_prob, topk_breeds, relative_probs = predict_single_dog(cropped_image)
|
561 |
+
combined_confidence = detection_confidence * top1_prob
|
562 |
+
|
563 |
+
# 根據信心度決定輸出格式
|
564 |
if combined_confidence < 0.2:
|
565 |
+
dogs_info += format_unknown_breed_message(color, i+1)
|
566 |
elif top1_prob >= 0.45:
|
567 |
breed = topk_breeds[0]
|
568 |
description = get_dog_description(breed)
|
|
|
569 |
if description is None:
|
|
|
570 |
description = {
|
571 |
"Name": breed,
|
572 |
"Size": "Unknown",
|
|
|
578 |
}
|
579 |
dogs_info += format_single_dog_result(breed, description, color)
|
580 |
else:
|
|
|
581 |
dogs_info += format_multiple_breeds_result(
|
582 |
topk_breeds,
|
583 |
relative_probs,
|
|
|
595 |
)
|
596 |
except Exception as e:
|
597 |
print(f"Error formatting results for dog {i+1}: {str(e)}")
|
598 |
+
dogs_info += format_unknown_breed_message(color, i+1)
|
599 |
|
600 |
+
# 包裝最終的HTML輸出
|
601 |
html_output = format_multi_dog_container(dogs_info)
|
602 |
|
603 |
+
# 準備初始狀態
|
604 |
initial_state = {
|
605 |
"dogs_info": dogs_info,
|
606 |
"image": annotated_image,
|