DawnC commited on
Commit
1f6954f
1 Parent(s): 2589d86

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -829
app.py DELETED
@@ -1,829 +0,0 @@
1
- import os
2
- import numpy as np
3
- import torch
4
- import torch.nn as nn
5
- import gradio as gr
6
- from torchvision.models import efficientnet_v2_m, EfficientNet_V2_M_Weights
7
- from torchvision.ops import nms, box_iou
8
- import torch.nn.functional as F
9
- from torchvision import transforms
10
- from PIL import Image, ImageDraw, ImageFont, ImageFilter
11
- from data_manager import get_dog_description, UserPreferences, get_breed_recommendations, format_recommendation_html
12
- from history_manager import UserHistoryManager
13
- from search_history import create_history_tab, create_history_component
14
- from styles import get_css_styles
15
- from breed_detection import create_detection_tab
16
- from breed_comparison import create_comparison_tab
17
- from breed_recommendation import create_recommendation_tab
18
- from urllib.parse import quote
19
- from ultralytics import YOLO
20
- import asyncio
21
- import traceback
22
-
23
-
24
- model_yolo = YOLO('yolov8l.pt')
25
-
26
- history_manager = UserHistoryManager()
27
-
28
- dog_breeds = ["Afghan_Hound", "African_Hunting_Dog", "Airedale", "American_Staffordshire_Terrier",
29
- "Appenzeller", "Australian_Terrier", "Bedlington_Terrier", "Bernese_Mountain_Dog",
30
- "Blenheim_Spaniel", "Border_Collie", "Border_Terrier", "Boston_Bull", "Bouvier_Des_Flandres",
31
- "Brabancon_Griffon", "Brittany_Spaniel", "Cardigan", "Chesapeake_Bay_Retriever",
32
- "Chihuahua", "Dandie_Dinmont", "Doberman", "English_Foxhound", "English_Setter",
33
- "English_Springer", "EntleBucher", "Eskimo_Dog", "French_Bulldog", "German_Shepherd",
34
- "German_Short-Haired_Pointer", "Gordon_Setter", "Great_Dane", "Great_Pyrenees",
35
- "Greater_Swiss_Mountain_Dog", "Ibizan_Hound", "Irish_Setter", "Irish_Terrier",
36
- "Irish_Water_Spaniel", "Irish_Wolfhound", "Italian_Greyhound", "Japanese_Spaniel",
37
- "Kerry_Blue_Terrier", "Labrador_Retriever", "Lakeland_Terrier", "Leonberg", "Lhasa",
38
- "Maltese_Dog", "Mexican_Hairless", "Newfoundland", "Norfolk_Terrier", "Norwegian_Elkhound",
39
- "Norwich_Terrier", "Old_English_Sheepdog", "Pekinese", "Pembroke", "Pomeranian",
40
- "Rhodesian_Ridgeback", "Rottweiler", "Saint_Bernard", "Saluki", "Samoyed",
41
- "Scotch_Terrier", "Scottish_Deerhound", "Sealyham_Terrier", "Shetland_Sheepdog",
42
- "Shih-Tzu", "Siberian_Husky", "Staffordshire_Bullterrier", "Sussex_Spaniel",
43
- "Tibetan_Mastiff", "Tibetan_Terrier", "Walker_Hound", "Weimaraner",
44
- "Welsh_Springer_Spaniel", "West_Highland_White_Terrier", "Yorkshire_Terrier",
45
- "Affenpinscher", "Basenji", "Basset", "Beagle", "Black-and-Tan_Coonhound", "Bloodhound",
46
- "Bluetick", "Borzoi", "Boxer", "Briard", "Bull_Mastiff", "Cairn", "Chow", "Clumber",
47
- "Cocker_Spaniel", "Collie", "Curly-Coated_Retriever", "Dhole", "Dingo",
48
- "Flat-Coated_Retriever", "Giant_Schnauzer", "Golden_Retriever", "Groenendael", "Keeshond",
49
- "Kelpie", "Komondor", "Kuvasz", "Malamute", "Malinois", "Miniature_Pinscher",
50
- "Miniature_Poodle", "Miniature_Schnauzer", "Otterhound", "Papillon", "Pug", "Redbone",
51
- "Schipperke", "Silky_Terrier", "Soft-Coated_Wheaten_Terrier", "Standard_Poodle",
52
- "Standard_Schnauzer", "Toy_Poodle", "Toy_Terrier", "Vizsla", "Whippet",
53
- "Wire-Haired_Fox_Terrier"]
54
-
55
- class MultiHeadAttention(nn.Module):
56
-
57
- def __init__(self, in_dim, num_heads=8):
58
- super().__init__()
59
- self.num_heads = num_heads
60
- self.head_dim = max(1, in_dim // num_heads)
61
- self.scaled_dim = self.head_dim * num_heads
62
- self.fc_in = nn.Linear(in_dim, self.scaled_dim)
63
- self.query = nn.Linear(self.scaled_dim, self.scaled_dim)
64
- self.key = nn.Linear(self.scaled_dim, self.scaled_dim)
65
- self.value = nn.Linear(self.scaled_dim, self.scaled_dim)
66
- self.fc_out = nn.Linear(self.scaled_dim, in_dim)
67
-
68
- def forward(self, x):
69
- N = x.shape[0]
70
- x = self.fc_in(x)
71
- q = self.query(x).view(N, self.num_heads, self.head_dim)
72
- k = self.key(x).view(N, self.num_heads, self.head_dim)
73
- v = self.value(x).view(N, self.num_heads, self.head_dim)
74
-
75
- energy = torch.einsum("nqd,nkd->nqk", [q, k])
76
- attention = F.softmax(energy / (self.head_dim ** 0.5), dim=2)
77
-
78
- out = torch.einsum("nqk,nvd->nqd", [attention, v])
79
- out = out.reshape(N, self.scaled_dim)
80
- out = self.fc_out(out)
81
- return out
82
-
83
- class BaseModel(nn.Module):
84
- def __init__(self, num_classes, device='cuda' if torch.cuda.is_available() else 'cpu'):
85
- super().__init__()
86
- self.device = device
87
- self.backbone = efficientnet_v2_m(weights=EfficientNet_V2_M_Weights.IMAGENET1K_V1)
88
- self.feature_dim = self.backbone.classifier[1].in_features
89
- self.backbone.classifier = nn.Identity()
90
-
91
- self.num_heads = max(1, min(8, self.feature_dim // 64))
92
- self.attention = MultiHeadAttention(self.feature_dim, num_heads=self.num_heads)
93
-
94
- self.classifier = nn.Sequential(
95
- nn.LayerNorm(self.feature_dim),
96
- nn.Dropout(0.3),
97
- nn.Linear(self.feature_dim, num_classes)
98
- )
99
-
100
- self.to(device)
101
-
102
- def forward(self, x):
103
- x = x.to(self.device)
104
- features = self.backbone(x)
105
- attended_features = self.attention(features)
106
- logits = self.classifier(attended_features)
107
- return logits, attended_features
108
-
109
-
110
- num_classes = 120
111
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
112
- model = BaseModel(num_classes=num_classes, device=device)
113
-
114
- checkpoint = torch.load('best_model_81_dog.pth', map_location=torch.device('cpu'))
115
- model.load_state_dict(checkpoint['model_state_dict'])
116
-
117
- # evaluation mode
118
- model.eval()
119
-
120
- # Image preprocessing function
121
- def preprocess_image(image):
122
- # If the image is numpy.ndarray turn into PIL.Image
123
- if isinstance(image, np.ndarray):
124
- image = Image.fromarray(image)
125
-
126
- # Use torchvision.transforms to process images
127
- transform = transforms.Compose([
128
- transforms.Resize((224, 224)),
129
- transforms.ToTensor(),
130
- transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
131
- ])
132
-
133
- return transform(image).unsqueeze(0)
134
-
135
-
136
- def get_akc_breeds_link(breed: str) -> str:
137
- """Generate AKC breed page URL with intelligent name handling"""
138
- # 基本的字符串處理
139
- breed_name = breed.lower()
140
-
141
- # 處理常見的名稱格式
142
- breed_name = breed_name.replace('_', '-')
143
- breed_name = breed_name.replace("'", '')
144
- breed_name = breed_name.replace(" ", '-')
145
-
146
- # 處理特殊情況
147
- special_cases = {
148
- 'mexican-hairless': 'xoloitzcuintli',
149
- 'brabancon-griffon': 'brussels-griffon',
150
- 'bull-mastiff': 'bullmastiff',
151
- 'walker-hound': 'treeing-walker-coonhound'
152
- }
153
-
154
- breed_name = special_cases.get(breed_name, breed_name)
155
- return f"https://www.akc.org/dog-breeds/{breed_name}/"
156
-
157
-
158
- async def predict_single_dog(image):
159
- image_tensor = preprocess_image(image)
160
- with torch.no_grad():
161
- output = model(image_tensor)
162
- logits = output[0] if isinstance(output, tuple) else output
163
- probabilities = F.softmax(logits, dim=1)
164
- topk_probs, topk_indices = torch.topk(probabilities, k=3)
165
- top1_prob = topk_probs[0][0].item()
166
- topk_breeds = [dog_breeds[idx.item()] for idx in topk_indices[0]]
167
-
168
- # Calculate relative probabilities for display
169
- raw_probs = [prob.item() for prob in topk_probs[0]]
170
- sum_probs = sum(raw_probs)
171
- relative_probs = [f"{(prob/sum_probs * 100):.2f}%" for prob in raw_probs]
172
-
173
- return top1_prob, topk_breeds, relative_probs
174
-
175
-
176
- async def detect_multiple_dogs(image, conf_threshold=0.3, iou_threshold=0.45):
177
- results = model_yolo(image, conf=conf_threshold, iou=iou_threshold)[0]
178
- dogs = []
179
- boxes = []
180
- for box in results.boxes:
181
- if box.cls == 16: # COCO dataset class for dog is 16
182
- xyxy = box.xyxy[0].tolist()
183
- confidence = box.conf.item()
184
- boxes.append((xyxy, confidence))
185
-
186
- if not boxes:
187
- dogs.append((image, 1.0, [0, 0, image.width, image.height]))
188
- else:
189
- nms_boxes = non_max_suppression(boxes, iou_threshold)
190
-
191
- for box, confidence in nms_boxes:
192
- x1, y1, x2, y2 = box
193
- w, h = x2 - x1, y2 - y1
194
- x1 = max(0, x1 - w * 0.05)
195
- y1 = max(0, y1 - h * 0.05)
196
- x2 = min(image.width, x2 + w * 0.05)
197
- y2 = min(image.height, y2 + h * 0.05)
198
- cropped_image = image.crop((x1, y1, x2, y2))
199
- dogs.append((cropped_image, confidence, [x1, y1, x2, y2]))
200
-
201
- return dogs
202
-
203
-
204
- def non_max_suppression(boxes, iou_threshold):
205
- keep = []
206
- boxes = sorted(boxes, key=lambda x: x[1], reverse=True)
207
- while boxes:
208
- current = boxes.pop(0)
209
- keep.append(current)
210
- boxes = [box for box in boxes if calculate_iou(current[0], box[0]) < iou_threshold]
211
- return keep
212
-
213
-
214
- def calculate_iou(box1, box2):
215
- x1 = max(box1[0], box2[0])
216
- y1 = max(box1[1], box2[1])
217
- x2 = min(box1[2], box2[2])
218
- y2 = min(box1[3], box2[3])
219
-
220
- intersection = max(0, x2 - x1) * max(0, y2 - y1)
221
- area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
222
- area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
223
-
224
- iou = intersection / float(area1 + area2 - intersection)
225
- return iou
226
-
227
-
228
- async def process_single_dog(image):
229
- top1_prob, topk_breeds, relative_probs = await predict_single_dog(image)
230
-
231
- # Case 1: Low confidence - unclear image or breed not in dataset
232
- if top1_prob < 0.2:
233
- error_message = '''
234
- <div class="dog-info-card">
235
- <div class="breed-info">
236
- <p class="warning-message">
237
- <span class="icon">⚠️</span>
238
- The image is unclear or the breed is not in the dataset. Please upload a clearer image of a dog.
239
- </p>
240
- </div>
241
- </div>
242
- '''
243
- initial_state = {
244
- "explanation": error_message,
245
- "image": None,
246
- "is_multi_dog": False
247
- }
248
- return error_message, None, initial_state
249
-
250
- breed = topk_breeds[0]
251
-
252
- # Case 2: High confidence - single breed result
253
- if top1_prob >= 0.45:
254
- description = get_dog_description(breed)
255
- formatted_description = format_description_html(description, breed) # 使用 format_description_html
256
- html_content = f'''
257
- <div class="dog-info-card">
258
- <div class="breed-info">
259
- {formatted_description}
260
- </div>
261
- </div>
262
- '''
263
- initial_state = {
264
- "explanation": html_content,
265
- "image": image,
266
- "is_multi_dog": False
267
- }
268
- return html_content, image, initial_state
269
-
270
- # Case 3: Medium confidence - show top 3 breeds with relative probabilities
271
- else:
272
- breeds_html = ""
273
- for i, (breed, prob) in enumerate(zip(topk_breeds, relative_probs)):
274
- description = get_dog_description(breed)
275
- formatted_description = format_description_html(description, breed) # 使用 format_description_html
276
- breeds_html += f'''
277
- <div class="dog-info-card">
278
- <div class="breed-info">
279
- <div class="breed-header">
280
- <span class="breed-name">Breed {i+1}: {breed}</span>
281
- <span class="confidence-badge">Confidence: {prob}</span>
282
- </div>
283
- {formatted_description}
284
- </div>
285
- </div>
286
- '''
287
-
288
- initial_state = {
289
- "explanation": breeds_html,
290
- "image": image,
291
- "is_multi_dog": False
292
- }
293
- return breeds_html, image, initial_state
294
-
295
-
296
- def create_breed_comparison(breed1: str, breed2: str) -> dict:
297
- breed1_info = get_dog_description(breed1)
298
- breed2_info = get_dog_description(breed2)
299
-
300
- # 標準化數值轉換
301
- value_mapping = {
302
- 'Size': {'Small': 1, 'Medium': 2, 'Large': 3, 'Giant': 4},
303
- 'Exercise_Needs': {'Low': 1, 'Moderate': 2, 'High': 3, 'Very High': 4},
304
- 'Care_Level': {'Low': 1, 'Moderate': 2, 'High': 3},
305
- 'Grooming_Needs': {'Low': 1, 'Moderate': 2, 'High': 3}
306
- }
307
-
308
- comparison_data = {
309
- breed1: {},
310
- breed2: {}
311
- }
312
-
313
- for breed, info in [(breed1, breed1_info), (breed2, breed2_info)]:
314
- comparison_data[breed] = {
315
- 'Size': value_mapping['Size'].get(info['Size'], 2), # 預設 Medium
316
- 'Exercise_Needs': value_mapping['Exercise_Needs'].get(info['Exercise Needs'], 2), # 預設 Moderate
317
- 'Care_Level': value_mapping['Care_Level'].get(info['Care Level'], 2),
318
- 'Grooming_Needs': value_mapping['Grooming_Needs'].get(info['Grooming Needs'], 2),
319
- 'Good_with_Children': info['Good with Children'] == 'Yes',
320
- 'Original_Data': info
321
- }
322
-
323
- return comparison_data
324
-
325
-
326
- async def predict(image):
327
- if image is None:
328
- return "Please upload an image to start.", None, None
329
-
330
- try:
331
- if isinstance(image, np.ndarray):
332
- image = Image.fromarray(image)
333
-
334
- dogs = await detect_multiple_dogs(image)
335
- # 更新顏色組合
336
- single_dog_color = '#34C759' # 清爽的綠色作為單狗顏色
337
- color_list = [
338
- '#FF5733', # 珊瑚紅
339
- '#28A745', # 深綠色
340
- '#3357FF', # 寶藍色
341
- '#FF33F5', # 粉紫色
342
- '#FFB733', # 橙黃色
343
- '#33FFF5', # 青藍色
344
- '#A233FF', # 紫色
345
- '#FF3333', # 紅色
346
- '#33FFB7', # 青綠色
347
- '#FFE033' # 金黃色
348
- ]
349
- annotated_image = image.copy()
350
- draw = ImageDraw.Draw(annotated_image)
351
-
352
- try:
353
- font = ImageFont.truetype("arial.ttf", 24)
354
- except:
355
- font = ImageFont.load_default()
356
-
357
- dogs_info = ""
358
-
359
- for i, (cropped_image, detection_confidence, box) in enumerate(dogs):
360
- color = single_dog_color if len(dogs) == 1 else color_list[i % len(color_list)]
361
-
362
- # 優化圖片上的標記
363
- draw.rectangle(box, outline=color, width=4)
364
- label = f"Dog {i+1}"
365
- label_bbox = draw.textbbox((0, 0), label, font=font)
366
- label_width = label_bbox[2] - label_bbox[0]
367
- label_height = label_bbox[3] - label_bbox[1]
368
-
369
- label_x = box[0] + 5
370
- label_y = box[1] + 5
371
- draw.rectangle(
372
- [label_x - 2, label_y - 2, label_x + label_width + 4, label_y + label_height + 4],
373
- fill='white',
374
- outline=color,
375
- width=2
376
- )
377
- draw.text((label_x, label_y), label, fill=color, font=font)
378
-
379
- top1_prob, topk_breeds, relative_probs = await predict_single_dog(cropped_image)
380
- combined_confidence = detection_confidence * top1_prob
381
-
382
- # 開始資訊卡片
383
- dogs_info += f'<div class="dog-info-card" style="border-left: 6px solid {color};">'
384
-
385
- if combined_confidence < 0.2:
386
- dogs_info += f'''
387
- <div class="dog-info-header" style="background-color: {color}10;">
388
- <span class="dog-label" style="color: {color};">Dog {i+1}</span>
389
- </div>
390
- <div class="breed-info">
391
- <p class="warning-message">
392
- <span class="icon">⚠️</span>
393
- The image is unclear or the breed is not in the dataset. Please upload a clearer image.
394
- </p>
395
- </div>
396
- '''
397
- elif top1_prob >= 0.45:
398
- breed = topk_breeds[0]
399
- description = get_dog_description(breed)
400
- dogs_info += f'''
401
- <div class="dog-info-header" style="background-color: {color}10;">
402
- <span class="dog-label" style="color: {color};">
403
- <span class="icon">🐾</span> {breed}
404
- </span>
405
- </div>
406
- <div class="breed-info">
407
- <h2 class="section-title">
408
- <span class="icon">📋</span> BASIC INFORMATION
409
- </h2>
410
- <div class="info-section">
411
- <div class="info-item">
412
- <span class="tooltip tooltip-left">
413
- <span class="icon">📏</span>
414
- <span class="label">Size:</span>
415
- <span class="tooltip-icon">ⓘ</span>
416
- <span class="tooltip-text">
417
- <strong>Size Categories:</strong><br>
418
- • Small: Under 20 pounds<br>
419
- • Medium: 20-60 pounds<br>
420
- • Large: Over 60 pounds<br>
421
- • Giant: Over 100 pounds<br>
422
- • Varies: Depends on variety
423
- </span>
424
- </span>
425
- <span class="value">{description['Size']}</span>
426
- </div>
427
- <div class="info-item">
428
- <span class="tooltip">
429
- <span class="icon">⏳</span>
430
- <span class="label">Lifespan:</span>
431
- <span class="tooltip-icon">ⓘ</span>
432
- <span class="tooltip-text">
433
- <strong>Average Lifespan:</strong><br>
434
- • Short: 6-8 years<br>
435
- • Average: 10-15 years<br>
436
- • Long: 12-20 years<br>
437
- • Varies by size: Larger breeds typically have shorter lifespans
438
- </span>
439
- </span>
440
- <span class="value">{description['Lifespan']}</span>
441
- </div>
442
- </div>
443
- <h2 class="section-title">
444
- <span class="icon">🐕</span> TEMPERAMENT & PERSONALITY
445
- </h2>
446
- <div class="temperament-section">
447
- <span class="tooltip">
448
- <span class="value">{description['Temperament']}</span>
449
- <span class="tooltip-icon">ⓘ</span>
450
- <span class="tooltip-text">
451
- <strong>Temperament Guide:</strong><br>
452
- • Describes the dog's natural behavior and personality<br>
453
- • Important for matching with owner's lifestyle<br>
454
- • Can be influenced by training and socialization
455
- </span>
456
- </span>
457
- </div>
458
- <h2 class="section-title">
459
- <span class="icon">💪</span> CARE REQUIREMENTS
460
- </h2>
461
- <div class="care-section">
462
- <div class="info-item">
463
- <span class="tooltip tooltip-left">
464
- <span class="icon">🏃</span>
465
- <span class="label">Exercise:</span>
466
- <span class="tooltip-icon">ⓘ</span>
467
- <span class="tooltip-text">
468
- <strong>Exercise Needs:</strong><br>
469
- • Low: Short walks and play sessions<br>
470
- • Moderate: 1-2 hours of daily activity<br>
471
- • High: Extensive exercise (2+ hours/day)<br>
472
- • Very High: Constant activity and mental stimulation needed
473
- </span>
474
- </span>
475
- <span class="value">{description['Exercise Needs']}</span>
476
- </div>
477
- <div class="info-item">
478
- <span class="tooltip">
479
- <span class="icon">✂️</span>
480
- <span class="label">Grooming:</span>
481
- <span class="tooltip-icon">ⓘ</span>
482
- <span class="tooltip-text">
483
- <strong>Grooming Requirements:</strong><br>
484
- • Low: Basic brushing, occasional baths<br>
485
- • Moderate: Weekly brushing, occasional grooming<br>
486
- • High: Daily brushing, frequent professional grooming needed<br>
487
- • Professional care recommended for all levels
488
- </span>
489
- </span>
490
- <span class="value">{description['Grooming Needs']}</span>
491
- </div>
492
- <div class="info-item">
493
- <span class="tooltip">
494
- <span class="icon">⭐</span>
495
- <span class="label">Care Level:</span>
496
- <span class="tooltip-icon">ⓘ</span>
497
- <span class="tooltip-text">
498
- <strong>Care Level Explained:</strong><br>
499
- • Low: Basic care and attention needed<br>
500
- • Moderate: Regular care and routine needed<br>
501
- • High: Significant time and attention needed<br>
502
- • Very High: Extensive care, training and attention required
503
- </span>
504
- </span>
505
- <span class="value">{description['Care Level']}</span>
506
- </div>
507
- </div>
508
- <h2 class="section-title">
509
- <span class="icon">👨‍👩‍👧‍👦</span> FAMILY COMPATIBILITY
510
- </h2>
511
- <div class="family-section">
512
- <div class="info-item">
513
- <span class="tooltip">
514
- <span class="icon"></span>
515
- <span class="label">Good with Children:</span>
516
- <span class="tooltip-icon">ⓘ</span>
517
- <span class="tooltip-text">
518
- <strong>Child Compatibility:</strong><br>
519
- • Yes: Excellent with kids, patient and gentle<br>
520
- • Moderate: Good with older children<br>
521
- • No: Better suited for adult households
522
- </span>
523
- </span>
524
- <span class="value">{description['Good with Children']}</span>
525
- </div>
526
- </div>
527
- <h2 class="section-title">
528
- <span class="icon">📝</span> DESCRIPTION
529
- </h2>
530
- <div class="description-section">
531
- <p>{description.get('Description', '')}</p>
532
- </div>
533
- <div class="action-section">
534
- <a href="{get_akc_breeds_link(breed)}" target="_blank" class="akc-button">
535
- <span class="icon">🌐</span>
536
- Learn more about {breed} on AKC website
537
- </a>
538
- </div>
539
- </div>
540
- '''
541
- else:
542
- dogs_info += f'''
543
- <div class="dog-info-header" style="background-color: {color}10;">
544
- <span class="dog-label" style="color: {color};">Dog {i+1}</span>
545
- </div>
546
- <div class="breed-info">
547
- <div class="model-uncertainty-note">
548
- <span class="icon">ℹ️</span>
549
- Note: The model is showing some uncertainty in its predictions.
550
- Here are the most likely breeds based on the available visual features.
551
- </div>
552
- <div class="breeds-list">
553
- '''
554
-
555
- for j, (breed, prob) in enumerate(zip(topk_breeds, relative_probs)):
556
- description = get_dog_description(breed)
557
- dogs_info += f'''
558
- <div class="breed-option uncertainty-mode">
559
- <div class="breed-header">
560
- <span class="option-number">Option {j+1}</span>
561
- <span class="breed-name">{breed}</span>
562
- <span class="confidence-badge" style="background-color: {color}20; color: {color};">
563
- Confidence: {prob}
564
- </span>
565
- </div>
566
- <div class="breed-content">
567
- {format_description_html(description, breed)}
568
- </div>
569
- </div>
570
- '''
571
- dogs_info += '</div></div>'
572
-
573
- dogs_info += '</div>'
574
-
575
-
576
- html_output = f"""
577
- <div class="dog-info-card">
578
- {dogs_info}
579
- </div>
580
- """
581
-
582
- initial_state = {
583
- "dogs_info": dogs_info,
584
- "image": annotated_image,
585
- "is_multi_dog": len(dogs) > 1,
586
- "html_output": html_output
587
- }
588
-
589
- return html_output, annotated_image, initial_state
590
-
591
- except Exception as e:
592
- error_msg = f"An error occurred: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
593
- print(error_msg)
594
- return error_msg, None, None
595
-
596
-
597
-
598
- def show_details_html(choice, previous_output, initial_state):
599
- if not choice:
600
- return previous_output, gr.update(visible=True), initial_state
601
-
602
- try:
603
- breed = choice.split("More about ")[-1]
604
- description = get_dog_description(breed)
605
- formatted_description = format_description_html(description, breed)
606
-
607
- html_output = f"""
608
- <div class="dog-info">
609
- <h2>{breed}</h2>
610
- {formatted_description}
611
- </div>
612
- """
613
-
614
- initial_state["current_description"] = html_output
615
- initial_state["original_buttons"] = initial_state.get("buttons", [])
616
-
617
- return html_output, gr.update(visible=True), initial_state
618
- except Exception as e:
619
- error_msg = f"An error occurred while showing details: {e}"
620
- print(error_msg)
621
- return f"<p style='color: red;'>{error_msg}</p>", gr.update(visible=True), initial_state
622
-
623
-
624
- def format_description_html(description, breed):
625
- html = "<ul style='list-style-type: none; padding-left: 0;'>"
626
- if isinstance(description, dict):
627
- for key, value in description.items():
628
- if key != "Breed": # 跳過重複的品種顯示
629
- if key == "Size":
630
- html += f'''
631
- <li style='margin-bottom: 10px;'>
632
- <span class="tooltip">
633
- <strong>{key}:</strong>
634
- <span class="tooltip-icon">ⓘ</span>
635
- <span class="tooltip-text">
636
- <strong>Size Categories:</strong><br>
637
- • Small: Under 20 pounds<br>
638
- • Medium: 20-60 pounds<br>
639
- • Large: Over 60 pounds
640
- </span>
641
- </span> {value}
642
- </li>
643
- '''
644
- elif key == "Exercise Needs":
645
- html += f'''
646
- <li style='margin-bottom: 10px;'>
647
- <span class="tooltip">
648
- <strong>{key}:</strong>
649
- <span class="tooltip-icon">ⓘ</span>
650
- <span class="tooltip-text">
651
- <strong>Exercise Needs:</strong><br>
652
- • High: 2+ hours of daily exercise<br>
653
- • Moderate: 1-2 hours of daily activity<br>
654
- • Low: Short walks and play sessions
655
- </span>
656
- </span> {value}
657
- </li>
658
- '''
659
- elif key == "Grooming Needs":
660
- html += f'''
661
- <li style='margin-bottom: 10px;'>
662
- <span class="tooltip">
663
- <strong>{key}:</strong>
664
- <span class="tooltip-icon">ⓘ</span>
665
- <span class="tooltip-text">
666
- <strong>Grooming Requirements:</strong><br>
667
- • High: Daily brushing, regular professional care<br>
668
- • Moderate: Weekly brushing, occasional grooming<br>
669
- • Low: Minimal brushing, basic maintenance
670
- </span>
671
- </span> {value}
672
- </li>
673
- '''
674
- elif key == "Care Level":
675
- html += f'''
676
- <li style='margin-bottom: 10px;'>
677
- <span class="tooltip">
678
- <strong>{key}:</strong>
679
- <span class="tooltip-icon">ⓘ</span>
680
- <span class="tooltip-text">
681
- <strong>Care Level Explained:</strong><br>
682
- • High: Needs significant training and attention<br>
683
- • Moderate: Regular care and routine needed<br>
684
- • Low: More independent, basic care sufficient
685
- </span>
686
- </span> {value}
687
- </li>
688
- '''
689
- elif key == "Good with Children":
690
- html += f'''
691
- <li style='margin-bottom: 10px;'>
692
- <span class="tooltip">
693
- <strong>{key}:</strong>
694
- <span class="tooltip-icon">ⓘ</span>
695
- <span class="tooltip-text">
696
- <strong>Child Compatibility:</strong><br>
697
- • Yes: Excellent with kids, patient and gentle<br>
698
- • Moderate: Good with older children<br>
699
- • No: Better suited for adult households
700
- </span>
701
- </span> {value}
702
- </li>
703
- '''
704
- elif key == "Lifespan":
705
- html += f'''
706
- <li style='margin-bottom: 10px;'>
707
- <span class="tooltip">
708
- <strong>{key}:</strong>
709
- <span class="tooltip-icon">ⓘ</span>
710
- <span class="tooltip-text">
711
- <strong>Average Lifespan:</strong><br>
712
- • Short: 6-8 years<br>
713
- • Average: 10-15 years<br>
714
- • Long: 12-20 years
715
- </span>
716
- </span> {value}
717
- </li>
718
- '''
719
- elif key == "Temperament":
720
- html += f'''
721
- <li style='margin-bottom: 10px;'>
722
- <span class="tooltip">
723
- <strong>{key}:</strong>
724
- <span class="tooltip-icon">ⓘ</span>
725
- <span class="tooltip-text">
726
- <strong>Temperament Guide:</strong><br>
727
- • Describes the dog's natural behavior<br>
728
- • Important for matching with owner
729
- </span>
730
- </span> {value}
731
- </li>
732
- '''
733
- else:
734
- # 其他欄位保持原樣顯示
735
- html += f"<li style='margin-bottom: 10px;'><strong>{key}:</strong> {value}</li>"
736
- else:
737
- html += f"<li>{description}</li>"
738
- html += "</ul>"
739
-
740
- # 添加AKC連結
741
- html += f'''
742
- <div class="action-section">
743
- <a href="{get_akc_breeds_link(breed)}" target="_blank" class="akc-button">
744
- <span class="icon">🌐</span>
745
- Learn more about {breed} on AKC website
746
- </a>
747
- </div>
748
- '''
749
- return html
750
-
751
- with gr.Blocks(css=get_css_styles()) as iface:
752
-
753
- gr.HTML("""
754
- <header style='text-align: center; padding: 20px; margin-bottom: 20px;'>
755
- <h1 style='font-size: 2.5em; margin-bottom: 10px; color: #2D3748;'>
756
- 🐾 PawMatch AI
757
- </h1>
758
- <h2 style='font-size: 1.2em; font-weight: normal; color: #4A5568; margin-top: 5px;'>
759
- Your Smart Dog Breed Guide
760
- </h2>
761
- <div style='width: 50px; height: 3px; background: linear-gradient(90deg, #4299e1, #48bb78); margin: 15px auto;'></div>
762
- <p style='color: #718096; font-size: 0.9em;'>
763
- Powered by AI • Breed Recognition • Smart Matching • Companion Guide
764
- </p>
765
- </header>
766
- """)
767
-
768
- def main():
769
- with gr.Blocks(css=get_css_styles()) as iface:
770
- # Header HTML
771
- gr.HTML("""
772
- <header style='text-align: center; padding: 20px; margin-bottom: 20px;'>
773
- <h1 style='font-size: 2.5em; margin-bottom: 10px; color: #2D3748;'>
774
- 🐾 PawMatch AI
775
- </h1>
776
- <h2 style='font-size: 1.2em; font-weight: normal; color: #4A5568; margin-top: 5px;'>
777
- Your Smart Dog Breed Guide
778
- </h2>
779
- <div style='width: 50px; height: 3px; background: linear-gradient(90deg, #4299e1, #48bb78); margin: 15px auto;'></div>
780
- <p style='color: #718096; font-size: 0.9em;'>
781
- Powered by AI • Breed Recognition • Smart Matching • Companion Guide
782
- </p>
783
- </header>
784
- """)
785
-
786
- # 先創建歷史組件實例(但不創建標籤頁)
787
- history_component = create_history_component()
788
-
789
- with gr.Tabs():
790
- # 1. 品種檢測標籤頁
791
- example_images = [
792
- 'Border_Collie.jpg',
793
- 'Golden_Retriever.jpeg',
794
- 'Saint_Bernard.jpeg',
795
- 'Samoyed.jpg',
796
- 'French_Bulldog.jpeg'
797
- ]
798
- detection_components = create_detection_tab(predict, example_images)
799
-
800
- # 2. 品種比較標籤頁
801
- comparison_components = create_comparison_tab(
802
- dog_breeds=dog_breeds,
803
- get_dog_description=get_dog_description
804
- )
805
-
806
- # 3. 品種推薦標籤頁
807
- recommendation_components = create_recommendation_tab(
808
- UserPreferences=UserPreferences,
809
- get_breed_recommendations=get_breed_recommendations,
810
- format_recommendation_html=format_recommendation_html,
811
- history_component=history_component
812
- )
813
-
814
- # 4. 最後創建歷史記錄標籤頁
815
- create_history_tab(history_component)
816
-
817
- # Footer
818
- gr.HTML('''
819
- For more details on this project and other work, feel free to visit my GitHub
820
- <a href="https://github.com/Eric-Chung-0511/Learning-Record/tree/main/Data%20Science%20Projects/Dog_Breed_Classifier">
821
- Dog Breed Classifier
822
- </a>
823
- ''')
824
-
825
- return iface
826
-
827
- if __name__ == "__main__":
828
- iface = main()
829
- iface.launch(share=True, debug=True)