DawnC commited on
Commit
c82fe58
1 Parent(s): a87f25a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +266 -850
app.py CHANGED
@@ -9,6 +9,9 @@ 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 urllib.parse import quote
13
  from ultralytics import YOLO
14
  import asyncio
@@ -17,6 +20,7 @@ import traceback
17
 
18
  model_yolo = YOLO('yolov8l.pt')
19
 
 
20
 
21
  dog_breeds = ["Afghan_Hound", "African_Hunting_Dog", "Airedale", "American_Staffordshire_Terrier",
22
  "Appenzeller", "Australian_Terrier", "Bedlington_Terrier", "Bernese_Mountain_Dog",
@@ -104,7 +108,7 @@ num_classes = 120
104
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
105
  model = BaseModel(num_classes=num_classes, device=device)
106
 
107
- checkpoint = torch.load('best_model_81_dog.pth', map_location=torch.device('cpu'))
108
  model.load_state_dict(checkpoint['model_state_dict'])
109
 
110
  # evaluation mode
@@ -271,7 +275,6 @@ async def process_single_dog(image):
271
 
272
 
273
  def create_breed_comparison(breed1: str, breed2: str) -> dict:
274
- """比較兩個狗品種的特性"""
275
  breed1_info = get_dog_description(breed1)
276
  breed2_info = get_dog_description(breed2)
277
 
@@ -730,877 +733,290 @@ def format_description_html(description, breed):
730
  '''
731
  return html
732
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
 
734
- with gr.Blocks(css="""
735
- .dog-info-card {
736
- border: 1px solid #e1e4e8;
737
- margin: 40px 0; /* 增加卡片間距 */
738
- padding: 0;
739
- border-radius: 12px;
740
- box-shadow: 0 2px 12px rgba(0,0,0,0.08);
741
- overflow: hidden;
742
- transition: all 0.3s ease;
743
- background: white;
744
- }
745
-
746
- .dog-info-card:hover {
747
- box-shadow: 0 4px 16px rgba(0,0,0,0.12);
748
- }
749
-
750
- .dog-info-header {
751
- padding: 24px 28px; /* 增加內距 */
752
- margin: 0;
753
- font-size: 22px;
754
- font-weight: bold;
755
- border-bottom: 1px solid #e1e4e8;
756
- }
757
-
758
- .breed-info {
759
- padding: 28px; /* 增加整體內距 */
760
- line-height: 1.6;
761
- }
762
-
763
- .section-title {
764
- font-size: 1.3em;
765
- font-weight: 700;
766
- color: #2c3e50;
767
- margin: 32px 0 20px 0;
768
- padding: 12px 0;
769
- border-bottom: 2px solid #e1e4e8;
770
- text-transform: uppercase;
771
- letter-spacing: 0.5px;
772
- display: flex;
773
- align-items: center;
774
- gap: 8px;
775
- position: relative;
776
- }
777
-
778
- .icon {
779
- font-size: 1.2em;
780
- display: inline-flex;
781
- align-items: center;
782
- justify-content: center;
783
- }
784
-
785
- .info-section, .care-section, .family-section {
786
- display: flex;
787
- flex-wrap: wrap;
788
- gap: 16px;
789
- margin-bottom: 28px; /* 增加底部間距 */
790
- padding: 20px; /* 增加內距 */
791
- background: #f8f9fa;
792
- border-radius: 12px;
793
- border: 1px solid #e1e4e8; /* 添加邊框 */
794
- }
795
-
796
- .info-item {
797
- background: white; /* 改為白色背景 */
798
- padding: 14px 18px; /* 增加內距 */
799
- border-radius: 8px;
800
- display: flex;
801
- align-items: center;
802
- gap: 10px;
803
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
804
- border: 1px solid #e1e4e8;
805
- flex: 1 1 auto;
806
- min-width: 200px;
807
- }
808
-
809
- .label {
810
- color: #666;
811
- font-weight: 600;
812
- font-size: 1.1rem;
813
- }
814
-
815
- .value {
816
- color: #2c3e50;
817
- font-weight: 500;
818
- font-size: 1.1rem;
819
- }
820
-
821
- .temperament-section {
822
- background: #f8f9fa;
823
- padding: 20px; /* 增加內距 */
824
- border-radius: 12px;
825
- margin-bottom: 28px; /* 增加間距 */
826
- color: #444;
827
- border: 1px solid #e1e4e8; /* 添加邊框 */
828
- }
829
-
830
- .description-section {
831
- background: #f8f9fa;
832
- padding: 24px; /* 增加內距 */
833
- border-radius: 12px;
834
- margin: 28px 0; /* 增加上下間距 */
835
- line-height: 1.8;
836
- color: #444;
837
- border: 1px solid #e1e4e8; /* 添加邊框 */
838
- fontsize: 1.1rem;
839
- }
840
- .description-section p {
841
- margin: 0;
842
- padding: 0;
843
- text-align: justify; /* 文字兩端對齊 */
844
- word-wrap: break-word; /* 確保長單字會換行 */
845
- white-space: pre-line; /* 保留換行但合併空白 */
846
- max-width: 100%; /* 確保不會超出容器 */
847
- }
848
-
849
- .action-section {
850
- margin-top: 24px;
851
- text-align: center;
852
- }
853
-
854
- .akc-button,
855
- .breed-section .akc-link,
856
- .breed-option .akc-link {
857
- display: inline-flex;
858
- align-items: center;
859
- padding: 14px 28px;
860
- background: linear-gradient(145deg, #00509E, #003F7F);
861
- color: white;
862
- border-radius: 12px; /* 增加圓角 */
863
- text-decoration: none;
864
- gap: 12px; /* 增加圖標和文字間距 */
865
- transition: all 0.3s ease;
866
- font-weight: 600;
867
- font-size: 1.1em;
868
- box-shadow:
869
- 0 2px 4px rgba(0,0,0,0.1),
870
- inset 0 1px 1px rgba(255,255,255,0.1);
871
- border: 1px solid rgba(255,255,255,0.1);
872
- }
873
-
874
- .akc-button:hover,
875
- .breed-section .akc-link:hover,
876
- .breed-option .akc-link:hover {
877
- background: linear-gradient(145deg, #003F7F, #00509E);
878
- transform: translateY(-2px);
879
- color: white;
880
- box-shadow:
881
- 0 6px 12px rgba(0,0,0,0.2),
882
- inset 0 1px 1px rgba(255,255,255,0.2);
883
- border: 1px solid rgba(255,255,255,0.2);
884
- }
885
- .icon {
886
- font-size: 1.3em;
887
- filter: drop-shadow(0 1px 1px rgba(0,0,0,0.2));
888
- }
889
-
890
- .warning-message {
891
- display: flex;
892
- align-items: center;
893
- gap: 8px;
894
- color: #ff3b30;
895
- font-weight: 500;
896
- margin: 0;
897
- padding: 16px;
898
- background: #fff5f5;
899
- border-radius: 8px;
900
- }
901
-
902
- .model-uncertainty-note {
903
- display: flex;
904
- align-items: center;
905
- gap: 12px;
906
- padding: 16px;
907
- background-color: #f8f9fa;
908
- border-left: 4px solid #6c757d;
909
- margin-bottom: 20px;
910
- color: #495057;
911
- border-radius: 4px;
912
- }
913
-
914
- .breeds-list {
915
- display: flex;
916
- flex-direction: column;
917
- gap: 20px;
918
- }
919
-
920
- .breed-option {
921
- background: white;
922
- border: 1px solid #e1e4e8;
923
- border-radius: 8px;
924
- overflow: hidden;
925
- }
926
-
927
- .breed-header {
928
- display: flex;
929
- align-items: center;
930
- padding: 16px;
931
- background: #f8f9fa;
932
- gap: 12px;
933
- border-bottom: 1px solid #e1e4e8;
934
- }
935
-
936
- .option-number {
937
- font-weight: 600;
938
- color: #666;
939
- padding: 4px 8px;
940
- background: #e1e4e8;
941
- border-radius: 4px;
942
- }
943
-
944
- .breed-name {
945
- font-size: 1.5em;
946
- font-weight: bold;
947
- color: #2c3e50;
948
- flex-grow: 1;
949
- }
950
-
951
- .confidence-badge {
952
- padding: 4px 12px;
953
- border-radius: 20px;
954
- font-size: 0.9em;
955
- font-weight: 500;
956
- }
957
-
958
- .breed-content {
959
- padding: 20px;
960
- }
961
- .breed-content li {
962
- margin-bottom: 8px;
963
- display: flex;
964
- align-items: flex-start; /* 改為頂部對齊 */
965
- gap: 8px;
966
- flex-wrap: wrap; /* 允許內容換�� */
967
- }
968
- .breed-content li strong {
969
- flex: 0 0 auto; /* 不讓標題縮放 */
970
- min-width: 100px; /* 給標題一個固定最小寬度 */
971
- }
972
-
973
- ul {
974
- padding-left: 0;
975
- margin: 0;
976
- list-style-type: none;
977
- }
978
-
979
- li {
980
- margin-bottom: 8px;
981
- display: flex;
982
- align-items: center;
983
- gap: 8px;
984
- }
985
- .akc-link {
986
- color: white;
987
- text-decoration: none;
988
- font-weight: 600;
989
- font-size: 1.1em;
990
- transition: all 0.3s ease;
991
- }
992
-
993
- .akc-link:hover {
994
- text-decoration: underline;
995
- color: #D3E3F0;
996
- }
997
- .tooltip {
998
- position: relative;
999
- display: inline-flex;
1000
- align-items: center;
1001
- gap: 4px;
1002
- cursor: help;
1003
- }
1004
- .tooltip .tooltip-icon {
1005
- font-size: 14px;
1006
- color: #666;
1007
- }
1008
- .tooltip .tooltip-text {
1009
- visibility: hidden;
1010
- width: 250px;
1011
- background-color: rgba(44, 62, 80, 0.95);
1012
- color: white;
1013
- text-align: left;
1014
- border-radius: 8px;
1015
- padding: 8px 10px;
1016
- position: absolute;
1017
- z-index: 100;
1018
- bottom: 150%;
1019
- left: 50%;
1020
- transform: translateX(-50%);
1021
- opacity: 0;
1022
- transition: all 0.3s ease;
1023
- font-size: 14px;
1024
- line-height: 1.3;
1025
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
1026
- border: 1px solid rgba(255, 255, 255, 0.1)
1027
- margin-bottom: 10px;
1028
- }
1029
- .tooltip.tooltip-left .tooltip-text {
1030
- left: 0;
1031
- transform: translateX(0);
1032
- }
1033
- .tooltip.tooltip-right .tooltip-text {
1034
- left: auto;
1035
- right: 0;
1036
- transform: translateX(0);
1037
- }
1038
- .tooltip-text strong {
1039
- color: white !important;
1040
- background-color: transparent !important;
1041
- display: block; /* 讓標題獨立一行 */
1042
- margin-bottom: 2px; /* 增加標題下方間距 */
1043
- padding-bottom: 2px; /* 加入小間距 */
1044
- border-bottom: 1px solid rgba(255,255,255,0.2);
1045
- }
1046
- .tooltip-text {
1047
- font-size: 13px; /* 稍微縮小字體 */
1048
- }
1049
-
1050
- /* 調整列表符號和文字的間距 */
1051
- .tooltip-text ul {
1052
- margin: 0;
1053
- padding-left: 15px; /* 減少列表符號的縮進 */
1054
- }
1055
-
1056
- .tooltip-text li {
1057
- margin-bottom: 1px; /* 減少列表項目間的間距 */
1058
- }
1059
- .tooltip-text br {
1060
- line-height: 1.2; /* 減少行距 */
1061
- }
1062
-
1063
- .tooltip .tooltip-text::after {
1064
- content: "";
1065
- position: absolute;
1066
- top: 100%;
1067
- left: 20%; /* 調整箭頭位置 */
1068
- margin-left: -5px;
1069
- border-width: 5px;
1070
- border-style: solid;
1071
- border-color: rgba(44, 62, 80, 0.95) transparent transparent transparent;
1072
- }
1073
- .tooltip-left .tooltip-text::after {
1074
- left: 20%;
1075
- }
1076
-
1077
- /* 右側箭頭 */
1078
- .tooltip-right .tooltip-text::after {
1079
- left: 80%;
1080
- }
1081
- .tooltip:hover .tooltip-text {
1082
- visibility: visible;
1083
- opacity: 1;
1084
- }
1085
- .tooltip .tooltip-text::after {
1086
- content: "";
1087
- position: absolute;
1088
- top: 100%;
1089
- left: 50%;
1090
- transform: translateX(-50%);
1091
- border-width: 8px;
1092
- border-style: solid;
1093
- border-color: rgba(44, 62, 80, 0.95) transparent transparent transparent;
1094
- }
1095
- .uncertainty-mode .tooltip .tooltip-text {
1096
- position: absolute;
1097
- left: 100%;
1098
- bottom: auto;
1099
- top: 50%;
1100
- transform: translateY(-50%);
1101
- margin-left: 10px;
1102
- z-index: 1000; /* 確保提示框在最上層 */
1103
- }
1104
-
1105
- .uncertainty-mode .tooltip .tooltip-text::after {
1106
- content: "";
1107
- position: absolute;
1108
- top: 50%;
1109
- right: 100%;
1110
- transform: translateY(-50%);
1111
- border-width: 5px;
1112
- border-style: solid;
1113
- border-color: transparent rgba(44, 62, 80, 0.95) transparent transparent;
1114
- }
1115
- .uncertainty-mode .breed-content {
1116
- font-size: 1.1rem; /* 增加字體大小 */
1117
- }
1118
- .description-section,
1119
- .description-section p,
1120
- .temperament-section,
1121
- .temperament-section .value,
1122
- .info-item,
1123
- .info-item .value,
1124
- .breed-content {
1125
- font-size: 1.1rem !important; /* 使用 !important 確保覆蓋其他樣式 */
1126
- }
1127
-
1128
- .recommendation-card {
1129
- margin-bottom: 40px;
1130
- }
1131
-
1132
- .compatibility-scores {
1133
- background: #f8f9fa;
1134
- padding: 24px;
1135
- border-radius: 12px;
1136
- margin: 20px 0;
1137
- }
1138
-
1139
- .score-item {
1140
- margin: 15px 0;
1141
- }
1142
-
1143
- .progress-bar {
1144
- height: 12px;
1145
- background-color: #e9ecef;
1146
- border-radius: 6px;
1147
- overflow: hidden;
1148
- margin: 8px 0;
1149
- }
1150
-
1151
- .progress {
1152
- height: 100%;
1153
- background: linear-gradient(90deg, #34C759, #30B350);
1154
- border-radius: 6px;
1155
- transition: width 0.6s ease;
1156
- }
1157
-
1158
- .percentage {
1159
- float: right;
1160
- color: #34C759;
1161
- font-weight: 600;
1162
- }
1163
-
1164
- .breed-details-section {
1165
- margin: 30px 0;
1166
- }
1167
-
1168
- .subsection-title {
1169
- font-size: 1.2em;
1170
- color: #2c3e50;
1171
- margin-bottom: 20px;
1172
- display: flex;
1173
- align-items: center;
1174
- gap: 8px;
1175
- }
1176
-
1177
- .details-grid {
1178
- display: grid;
1179
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
1180
- gap: 20px;
1181
- background: #f8f9fa;
1182
- padding: 20px;
1183
- border-radius: 12px;
1184
- border: 1px solid #e1e4e8;
1185
- }
1186
-
1187
- .detail-item {
1188
- background: white;
1189
- padding: 15px;
1190
- border-radius: 8px;
1191
- border: 1px solid #e1e4e8;
1192
- }
1193
-
1194
- .description-text {
1195
- line-height: 1.8;
1196
- color: #444;
1197
- margin: 0;
1198
- padding: 24px 30px; /* 調整內部間距,從 20px 改為 24px 30px */
1199
- background: #f8f9fa;
1200
- border-radius: 12px;
1201
- border: 1px solid #e1e4e8;
1202
- text-align: justify; /* 添加文字對齊 */
1203
- word-wrap: break-word; /* 確保長文字會換行 */
1204
- word-spacing: 1px; /* 加入字間距 */
1205
- }
1206
-
1207
- /* 工具提示改進 */
1208
- .tooltip {
1209
- position: relative;
1210
- display: inline-flex;
1211
- align-items: center;
1212
- gap: 4px;
1213
- cursor: help;
1214
- padding: 5px 0;
1215
- }
1216
-
1217
- .tooltip .tooltip-text {
1218
- visibility: hidden;
1219
- width: 280px;
1220
- background-color: rgba(44, 62, 80, 0.95);
1221
- color: white;
1222
- text-align: left;
1223
- border-radius: 8px;
1224
- padding: 12px 15px;
1225
- position: absolute;
1226
- z-index: 1000;
1227
- bottom: calc(100% + 15px);
1228
- left: 50%;
1229
- transform: translateX(-50%);
1230
- opacity: 0;
1231
- transition: all 0.3s ease;
1232
- font-size: 14px;
1233
- line-height: 1.4;
1234
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
1235
- white-space: normal;
1236
- }
1237
-
1238
- .tooltip:hover .tooltip-text {
1239
- visibility: visible;
1240
- opacity: 1;
1241
- }
1242
-
1243
- .score-badge {
1244
- background-color: #34C759;
1245
- color: white;
1246
- padding: 6px 12px;
1247
- border-radius: 20px;
1248
- font-size: 0.9em;
1249
- margin-left: 10px;
1250
- font-weight: 500;
1251
- box-shadow: 0 2px 4px rgba(52, 199, 89, 0.2);
1252
- }
1253
 
1254
- .bonus-score .tooltip-text {
1255
- width: 250px;
1256
- line-height: 1.4;
1257
- padding: 10px;
1258
- }
1259
 
1260
- .bonus-score .progress {
1261
- background: linear-gradient(90deg, #48bb78, #68d391);
1262
- }
 
 
 
 
 
 
 
 
1263
 
1264
- .health-section {
1265
- margin: 25px 0;
1266
- padding: 24px;
1267
- background-color: #f8f9fb;
1268
- border-radius: 12px;
1269
- border: 1px solid #e1e4e8;
1270
- }
1271
 
1272
- .health-section .subsection-title {
1273
- font-size: 1.3em;
1274
- font-weight: 600;
1275
- margin-bottom: 20px;
1276
- display: flex;
1277
- align-items: center;
1278
- gap: 8px;
1279
- color: #2c3e50;
1280
- }
1281
 
1282
- .health-info {
1283
- background-color: white;
1284
- padding: 24px;
1285
- border-radius: 8px;
1286
- margin: 15px 0;
1287
- border: 1px solid #e1e4e8;
1288
- }
1289
 
1290
- .health-details {
1291
- font-size: 1.1rem;
1292
- line-height: 1.6;
1293
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1294
 
1295
- .health-details h4 {
1296
- color: #2c3e50;
1297
- font-size: 1.15rem;
1298
- font-weight: 600;
1299
- margin: 20px 0 15px 0;
1300
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1301
 
1302
- .health-details h4:first-child {
1303
- margin-top: 0;
1304
- }
 
 
1305
 
1306
- .health-details ul {
1307
- list-style-type: none;
1308
- padding-left: 0;
1309
- margin: 0 0 25px 0;
1310
- }
 
 
 
 
 
 
 
 
1311
 
1312
- .health-details ul li {
1313
- margin-bottom: 12px;
1314
- padding-left: 20px;
1315
- position: relative;
1316
- }
 
 
1317
 
1318
- .health-details ul li:before {
1319
- content: "";
1320
- position: absolute;
1321
- left: 0;
1322
- color: #2c3e50;
1323
- }
1324
 
1325
- .health-disclaimer {
1326
- margin-top: 20px;
1327
- padding-top: 20px;
1328
- border-top: 1px solid #e1e4e8;
1329
- }
1330
-
1331
- .health-disclaimer p {
1332
- margin: 6px 0;
1333
- padding-left: 20px;
1334
- position: relative;
1335
- color: #888; /* 統一設定灰色 */
1336
- font-size: 0.95rem;
1337
- line-height: 1.5;
1338
- font-style: italic;
1339
- }
1340
-
1341
- .health-disclaimer p:before {
1342
- content: "›";
1343
- position: absolute;
1344
- left: 0;
1345
- color: #999;
1346
- font-style: normal;
1347
- font-weight: 500;
1348
- }
1349
-
1350
- .health-disclaimer p:first-child {
1351
- font-style: normal; /* 取消斜體 */
1352
- font-weight: 500; /* 加粗 */
1353
- color: #666; /* 稍深的灰色 */
1354
- }
1355
-
1356
- .health-disclaimer p span,
1357
- .health-disclaimer p strong,
1358
- .health-disclaimer p em {
1359
- color: inherit;
1360
- }
1361
 
1362
- """) as iface:
1363
-
1364
- gr.HTML("""
1365
- <header style='text-align: center; padding: 20px; margin-bottom: 20px;'>
1366
- <h1 style='font-size: 2.5em; margin-bottom: 10px; color: #2D3748;'>
1367
- 🐾 PawMatch AI
1368
- </h1>
1369
- <h2 style='font-size: 1.2em; font-weight: normal; color: #4A5568; margin-top: 5px;'>
1370
- Your Smart Dog Breed Guide
1371
- </h2>
1372
- <div style='width: 50px; height: 3px; background: linear-gradient(90deg, #4299e1, #48bb78); margin: 15px auto;'></div>
1373
- <p style='color: #718096; font-size: 0.9em;'>
1374
- Powered by AI • Breed Recognition • Smart Matching • Companion Guide
1375
- </p>
1376
- </header>
1377
- """)
1378
-
1379
- # 使用 Tabs 來分隔兩個功能
1380
- with gr.Tabs():
1381
- # 第一個 Tab:原有的辨識功能
1382
- with gr.TabItem("Breed Detection"):
1383
- gr.HTML("<p style='text-align: center;'>Upload a picture of a dog, and the model will predict its breed and provide detailed information!</p>")
1384
- gr.HTML("<p style='text-align: center; color: #666; font-size: 0.9em;'>Note: The model's predictions may not always be 100% accurate, and it is recommended to use the results as a reference.</p>")
1385
-
1386
- with gr.Row():
1387
- input_image = gr.Image(label="Upload a dog image", type="pil")
1388
- output_image = gr.Image(label="Annotated Image")
1389
-
1390
- output = gr.HTML(label="Prediction Results")
1391
- initial_state = gr.State()
1392
-
1393
- input_image.change(
1394
- predict,
1395
- inputs=input_image,
1396
- outputs=[output, output_image, initial_state]
1397
- )
1398
 
1399
- gr.Examples(
1400
- examples=['Border_Collie.jpg', 'Golden_Retriever.jpeg', 'Saint_Bernard.jpeg', 'French_Bulldog.jpeg', 'Samoyed.jpg'],
1401
- inputs=input_image
1402
- )
 
 
1403
 
1404
- # 第二個 Tab:品種比較功能
1405
- with gr.TabItem("Breed Comparison"):
1406
- gr.HTML("<p style='text-align: center;'>Select two dog breeds to compare their characteristics and care requirements.</p>")
1407
 
1408
- with gr.Row():
1409
- breed1_dropdown = gr.Dropdown(
1410
- choices=dog_breeds,
1411
- label="Select First Breed",
1412
- value="Golden_Retriever"
1413
- )
1414
- breed2_dropdown = gr.Dropdown(
1415
- choices=dog_breeds,
1416
- label="Select Second Breed",
1417
- value="Border_Collie"
 
 
 
 
 
 
1418
  )
1419
 
1420
- compare_btn = gr.Button("Compare Breeds")
1421
- comparison_output = gr.HTML(label="Comparison Results")
1422
-
1423
- def show_comparison(breed1, breed2):
1424
- if not breed1 or not breed2:
1425
- return "Please select two breeds to compare"
1426
-
1427
- breed1_info = get_dog_description(breed1)
1428
- breed2_info = get_dog_description(breed2)
1429
-
1430
- html_output = f"""
1431
- <div class="dog-info-card">
1432
- <div class="comparison-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
1433
- <div class="breed-info">
1434
- <h2 class="section-title">
1435
- <span class="icon">🐕</span> {breed1.replace('_', ' ')}
1436
- </h2>
1437
- <div class="info-section">
1438
- <div class="info-item">
1439
- <span class="tooltip">
1440
- <span class="icon">📏</span>
1441
- <span class="label">Size:</span>
1442
- <span class="value">{breed1_info['Size']}</span>
1443
- </span>
1444
- </div>
1445
- <div class="info-item">
1446
- <span class="tooltip">
1447
- <span class="icon">🏃</span>
1448
- <span class="label">Exercise Needs:</span>
1449
- <span class="value">{breed1_info['Exercise Needs']}</span>
1450
- </span>
1451
- </div>
1452
- <div class="info-item">
1453
- <span class="tooltip">
1454
- <span class="icon">✂️</span>
1455
- <span class="label">Grooming:</span>
1456
- <span class="value">{breed1_info['Grooming Needs']}</span>
1457
- </span>
1458
- </div>
1459
- <div class="info-item">
1460
- <span class="tooltip">
1461
- <span class="icon">👨‍👩‍👧‍👦</span>
1462
- <span class="label">Good with Children:</span>
1463
- <span class="value">{breed1_info['Good with Children']}</span>
1464
- </span>
1465
- </div>
1466
- </div>
1467
- </div>
1468
 
1469
- <div class="breed-info">
1470
- <h2 class="section-title">
1471
- <span class="icon">🐕</span> {breed2.replace('_', ' ')}
1472
- </h2>
1473
- <div class="info-section">
1474
- <div class="info-item">
1475
- <span class="tooltip">
1476
- <span class="icon">📏</span>
1477
- <span class="label">Size:</span>
1478
- <span class="value">{breed2_info['Size']}</span>
1479
- </span>
1480
- </div>
1481
- <div class="info-item">
1482
- <span class="tooltip">
1483
- <span class="icon">🏃</span>
1484
- <span class="label">Exercise Needs:</span>
1485
- <span class="value">{breed2_info['Exercise Needs']}</span>
1486
- </span>
1487
- </div>
1488
- <div class="info-item">
1489
- <span class="tooltip">
1490
- <span class="icon">✂️</span>
1491
- <span class="label">Grooming:</span>
1492
- <span class="value">{breed2_info['Grooming Needs']}</span>
1493
- </span>
1494
- </div>
1495
- <div class="info-item">
1496
- <span class="tooltip">
1497
- <span class="icon">👨‍👩‍👧‍👦</span>
1498
- <span class="label">Good with Children:</span>
1499
- <span class="value">{breed2_info['Good with Children']}</span>
1500
- </span>
1501
- </div>
1502
- </div>
1503
- </div>
1504
- </div>
1505
- </div>
1506
- """
1507
- return html_output
1508
 
1509
- compare_btn.click(
1510
- show_comparison,
1511
- inputs=[breed1_dropdown, breed2_dropdown],
1512
- outputs=comparison_output
1513
- )
1514
 
1515
- # 第三個 Tab:品種推薦功能
1516
- with gr.TabItem("Breed Recommendation"):
1517
- gr.HTML("<p style='text-align: center;'>Tell us about your lifestyle, and we'll recommend the perfect dog breeds for you!</p>")
1518
-
1519
- with gr.Row():
1520
- with gr.Column():
1521
- living_space = gr.Radio(
1522
- choices=["apartment", "house_small", "house_large"],
1523
- label="What type of living space do you have?",
1524
- info="Choose your current living situation",
1525
- value="apartment"
1526
- )
1527
-
1528
- exercise_time = gr.Slider(
1529
- minimum=0,
1530
- maximum=180,
1531
- value=60,
1532
- label="Daily exercise time (minutes)",
1533
- info="Consider walks, play time, and training"
1534
- )
1535
-
1536
- grooming_commitment = gr.Radio(
1537
- choices=["low", "medium", "high"],
1538
- label="Grooming commitment level",
1539
- info="Low: monthly, Medium: weekly, High: daily",
1540
- value="medium"
1541
- )
1542
-
1543
- with gr.Column():
1544
- experience_level = gr.Radio(
1545
- choices=["beginner", "intermediate", "advanced"],
1546
- label="Dog ownership experience",
1547
- info="Be honest - this helps find the right match",
1548
- value="beginner"
1549
- )
1550
-
1551
- has_children = gr.Checkbox(
1552
- label="Have children at home",
1553
- info="Helps recommend child-friendly breeds"
1554
- )
1555
-
1556
- noise_tolerance = gr.Radio(
1557
- choices=["low", "medium", "high"],
1558
- label="Noise tolerance level",
1559
- info="Some breeds are more vocal than others",
1560
- value="medium"
1561
- )
1562
-
1563
-
1564
- # 設置按鈕的點擊事件
1565
- get_recommendations_btn = gr.Button("Find My Perfect Match! 🔍", variant="primary")
1566
- recommendation_output = gr.HTML(label="Breed Recommendations")
1567
-
1568
- def process_recommendations(living_space, exercise_time, grooming_commitment,
1569
- experience_level, has_children, noise_tolerance):
1570
- try:
1571
- user_prefs = UserPreferences(
1572
- living_space=living_space,
1573
- exercise_time=exercise_time,
1574
- grooming_commitment=grooming_commitment,
1575
- experience_level=experience_level,
1576
- has_children=has_children,
1577
- noise_tolerance=noise_tolerance,
1578
- space_for_play=True if living_space != "apartment" else False,
1579
- other_pets=False,
1580
- climate="moderate"
1581
- )
1582
-
1583
- recommendations = get_breed_recommendations(user_prefs)
1584
- return format_recommendation_html(recommendations)
1585
- except Exception as e:
1586
- print(f"Error in process_recommendations: {str(e)}")
1587
- return f"An error occurred: {str(e)}"
1588
-
1589
- # 這行是關鍵 - 確保按鈕點擊事件有正確連接到處理函數
1590
- get_recommendations_btn.click(
1591
- fn=process_recommendations, # 處理函數
1592
- inputs=[
1593
- living_space,
1594
- exercise_time,
1595
- grooming_commitment,
1596
- experience_level,
1597
- has_children,
1598
- noise_tolerance
1599
- ],
1600
- outputs=recommendation_output # 輸出結果的位置
1601
- )
1602
 
1603
- gr.HTML('For more details on this project and other work, feel free to visit my GitHub <a href="https://github.com/Eric-Chung-0511/Learning-Record/tree/main/Data%20Science%20Projects/Dog_Breed_Classifier">Dog Breed Classifier</a>')
1604
 
1605
  if __name__ == "__main__":
1606
 
 
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
14
+ from styles import get_css_styles
15
  from urllib.parse import quote
16
  from ultralytics import YOLO
17
  import asyncio
 
20
 
21
  model_yolo = YOLO('yolov8l.pt')
22
 
23
+ history_manager = UserHistoryManager()
24
 
25
  dog_breeds = ["Afghan_Hound", "African_Hunting_Dog", "Airedale", "American_Staffordshire_Terrier",
26
  "Appenzeller", "Australian_Terrier", "Bedlington_Terrier", "Bernese_Mountain_Dog",
 
108
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
109
  model = BaseModel(num_classes=num_classes, device=device)
110
 
111
+ checkpoint = torch.load('/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/(120)best_model/best_model_81_dog.pth', map_location=torch.device('cpu'))
112
  model.load_state_dict(checkpoint['model_state_dict'])
113
 
114
  # evaluation mode
 
275
 
276
 
277
  def create_breed_comparison(breed1: str, breed2: str) -> dict:
 
278
  breed1_info = get_dog_description(breed1)
279
  breed2_info = get_dog_description(breed2)
280
 
 
733
  '''
734
  return html
735
 
736
+ with gr.Blocks(css=get_css_styles()) as iface:
737
+
738
+ gr.HTML("""
739
+ <header style='text-align: center; padding: 20px; margin-bottom: 20px;'>
740
+ <h1 style='font-size: 2.5em; margin-bottom: 10px; color: #2D3748;'>
741
+ 🐾 PawMatch AI
742
+ </h1>
743
+ <h2 style='font-size: 1.2em; font-weight: normal; color: #4A5568; margin-top: 5px;'>
744
+ Your Smart Dog Breed Guide
745
+ </h2>
746
+ <div style='width: 50px; height: 3px; background: linear-gradient(90deg, #4299e1, #48bb78); margin: 15px auto;'></div>
747
+ <p style='color: #718096; font-size: 0.9em;'>
748
+ Powered by AI • Breed Recognition • Smart Matching • Companion Guide
749
+ </p>
750
+ </header>
751
+ """)
752
+
753
+ # 使用 Tabs 來分隔兩個功能
754
+ with gr.Tabs():
755
+ # 第一個 Tab:原有的辨識功能
756
+ with gr.TabItem("Breed Detection"):
757
+ gr.HTML("<p style='text-align: center;'>Upload a picture of a dog, and the model will predict its breed and provide detailed information!</p>")
758
+ gr.HTML("<p style='text-align: center; color: #666; font-size: 0.9em;'>Note: The model's predictions may not always be 100% accurate, and it is recommended to use the results as a reference.</p>")
759
+
760
+ with gr.Row():
761
+ input_image = gr.Image(label="Upload a dog image", type="pil")
762
+ output_image = gr.Image(label="Annotated Image")
763
+
764
+ output = gr.HTML(label="Prediction Results")
765
+ initial_state = gr.State()
766
+
767
+ input_image.change(
768
+ predict,
769
+ inputs=input_image,
770
+ outputs=[output, output_image, initial_state]
771
+ )
772
 
773
+ gr.Examples(
774
+ examples=['/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/test_images/Border_Collie.jpg',
775
+ '/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/test_images/Golden_Retriever.jpeg',
776
+ '/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/test_images/Saint_Bernard.jpeg',
777
+ '/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/test_images/Samoyed.jpg',
778
+ '/content/drive/Othercomputers/我的 MacBook Pro/Learning/Cats_Dogs_Detector/test_images/French_Bulldog.jpeg'],
779
+ inputs=input_image
780
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
 
782
+ # 第二個 Tab:品種比較功能
783
+ with gr.TabItem("Breed Comparison"):
784
+ gr.HTML("<p style='text-align: center;'>Select two dog breeds to compare their characteristics and care requirements.</p>")
 
 
785
 
786
+ with gr.Row():
787
+ breed1_dropdown = gr.Dropdown(
788
+ choices=dog_breeds,
789
+ label="Select First Breed",
790
+ value="Golden_Retriever"
791
+ )
792
+ breed2_dropdown = gr.Dropdown(
793
+ choices=dog_breeds,
794
+ label="Select Second Breed",
795
+ value="Border_Collie"
796
+ )
797
 
798
+ compare_btn = gr.Button("Compare Breeds")
799
+ comparison_output = gr.HTML(label="Comparison Results")
 
 
 
 
 
800
 
801
+ def show_comparison(breed1, breed2):
802
+ if not breed1 or not breed2:
803
+ return "Please select two breeds to compare"
 
 
 
 
 
 
804
 
805
+ breed1_info = get_dog_description(breed1)
806
+ breed2_info = get_dog_description(breed2)
 
 
 
 
 
807
 
808
+ html_output = f"""
809
+ <div class="dog-info-card">
810
+ <div class="comparison-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
811
+ <div class="breed-info">
812
+ <h2 class="section-title">
813
+ <span class="icon">🐕</span> {breed1.replace('_', ' ')}
814
+ </h2>
815
+ <div class="info-section">
816
+ <div class="info-item">
817
+ <span class="tooltip">
818
+ <span class="icon">📏</span>
819
+ <span class="label">Size:</span>
820
+ <span class="value">{breed1_info['Size']}</span>
821
+ </span>
822
+ </div>
823
+ <div class="info-item">
824
+ <span class="tooltip">
825
+ <span class="icon">🏃</span>
826
+ <span class="label">Exercise Needs:</span>
827
+ <span class="value">{breed1_info['Exercise Needs']}</span>
828
+ </span>
829
+ </div>
830
+ <div class="info-item">
831
+ <span class="tooltip">
832
+ <span class="icon">✂️</span>
833
+ <span class="label">Grooming:</span>
834
+ <span class="value">{breed1_info['Grooming Needs']}</span>
835
+ </span>
836
+ </div>
837
+ <div class="info-item">
838
+ <span class="tooltip">
839
+ <span class="icon">👨‍👩‍👧‍👦</span>
840
+ <span class="label">Good with Children:</span>
841
+ <span class="value">{breed1_info['Good with Children']}</span>
842
+ </span>
843
+ </div>
844
+ </div>
845
+ </div>
846
 
847
+ <div class="breed-info">
848
+ <h2 class="section-title">
849
+ <span class="icon">🐕</span> {breed2.replace('_', ' ')}
850
+ </h2>
851
+ <div class="info-section">
852
+ <div class="info-item">
853
+ <span class="tooltip">
854
+ <span class="icon">📏</span>
855
+ <span class="label">Size:</span>
856
+ <span class="value">{breed2_info['Size']}</span>
857
+ </span>
858
+ </div>
859
+ <div class="info-item">
860
+ <span class="tooltip">
861
+ <span class="icon">🏃</span>
862
+ <span class="label">Exercise Needs:</span>
863
+ <span class="value">{breed2_info['Exercise Needs']}</span>
864
+ </span>
865
+ </div>
866
+ <div class="info-item">
867
+ <span class="tooltip">
868
+ <span class="icon">✂️</span>
869
+ <span class="label">Grooming:</span>
870
+ <span class="value">{breed2_info['Grooming Needs']}</span>
871
+ </span>
872
+ </div>
873
+ <div class="info-item">
874
+ <span class="tooltip">
875
+ <span class="icon">👨‍👩‍👧‍👦</span>
876
+ <span class="label">Good with Children:</span>
877
+ <span class="value">{breed2_info['Good with Children']}</span>
878
+ </span>
879
+ </div>
880
+ </div>
881
+ </div>
882
+ </div>
883
+ </div>
884
+ """
885
+ return html_output
886
 
887
+ compare_btn.click(
888
+ show_comparison,
889
+ inputs=[breed1_dropdown, breed2_dropdown],
890
+ outputs=comparison_output
891
+ )
892
 
893
+ # 第三個 Tab:品種推薦功能
894
+ # history_component = create_history_tab()
895
+ with gr.TabItem("Breed Recommendation"):
896
+ gr.HTML("<p style='text-align: center;'>Tell us about your lifestyle, and we'll recommend the perfect dog breeds for you!</p>")
897
+
898
+ with gr.Row():
899
+ with gr.Column():
900
+ living_space = gr.Radio(
901
+ choices=["apartment", "house_small", "house_large"],
902
+ label="What type of living space do you have?",
903
+ info="Choose your current living situation",
904
+ value="apartment"
905
+ )
906
 
907
+ exercise_time = gr.Slider(
908
+ minimum=0,
909
+ maximum=180,
910
+ value=60,
911
+ label="Daily exercise time (minutes)",
912
+ info="Consider walks, play time, and training"
913
+ )
914
 
915
+ grooming_commitment = gr.Radio(
916
+ choices=["low", "medium", "high"],
917
+ label="Grooming commitment level",
918
+ info="Low: monthly, Medium: weekly, High: daily",
919
+ value="medium"
920
+ )
921
 
922
+ with gr.Column():
923
+ experience_level = gr.Radio(
924
+ choices=["beginner", "intermediate", "advanced"],
925
+ label="Dog ownership experience",
926
+ info="Be honest - this helps find the right match",
927
+ value="beginner"
928
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
929
 
930
+ has_children = gr.Checkbox(
931
+ label="Have children at home",
932
+ info="Helps recommend child-friendly breeds"
933
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
 
935
+ noise_tolerance = gr.Radio(
936
+ choices=["low", "medium", "high"],
937
+ label="Noise tolerance level",
938
+ info="Some breeds are more vocal than others",
939
+ value="medium"
940
+ )
941
 
 
 
 
942
 
943
+ # 設置按鈕的點擊事件
944
+ get_recommendations_btn = gr.Button("Find My Perfect Match! 🔍", variant="primary")
945
+ recommendation_output = gr.HTML(label="Breed Recommendations")
946
+
947
+ def on_find_match_click(*args):
948
+ try:
949
+ user_prefs = UserPreferences(
950
+ living_space=args[0],
951
+ exercise_time=args[1],
952
+ grooming_commitment=args[2],
953
+ experience_level=args[3],
954
+ has_children=args[4],
955
+ noise_tolerance=args[5],
956
+ space_for_play=True if args[0] != "apartment" else False,
957
+ other_pets=False,
958
+ climate="moderate"
959
  )
960
 
961
+ # 取得推薦結果
962
+ recommendations = get_breed_recommendations(user_prefs)
963
+
964
+ print("Debug - Recommendations received:")
965
+ for rec in recommendations:
966
+ print(f"#{rec['rank']} {rec['breed']}: {rec['final_score']:.4f}")
967
+
968
+ # 修改這裡:確保保存完整的推薦資訊
969
+ history_results = [{
970
+ 'breed': rec['breed'],
971
+ 'rank': rec['rank'],
972
+ 'overall_score': rec['final_score'], # 使用 final_score
973
+ 'base_score': rec['base_score'],
974
+ 'bonus_score': rec['bonus_score'],
975
+ 'scores': rec['scores']
976
+ } for rec in recommendations]
977
+
978
+ print("Debug - Preparing to save history:")
979
+ for res in history_results:
980
+ print(f"Saving #{res['rank']} {res['breed']}: {res['overall_score']:.4f}")
981
+
982
+
983
+ history_component.save_search(
984
+ user_preferences={
985
+ 'living_space': args[0],
986
+ 'exercise_time': args[1],
987
+ 'grooming_commitment': args[2],
988
+ 'experience_level': args[3],
989
+ 'has_children': args[4],
990
+ 'noise_tolerance': args[5]
991
+ },
992
+ results=history_results
993
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994
 
995
+ return format_recommendation_html(recommendations)
996
+
997
+ except Exception as e:
998
+ print(f"Error in find match: {str(e)}")
999
+ import traceback
1000
+ print(traceback.format_exc())
1001
+ return "Error getting recommendations"
1002
+
1003
+ get_recommendations_btn.click(
1004
+ fn=on_find_match_click,
1005
+ inputs=[
1006
+ living_space,
1007
+ exercise_time,
1008
+ grooming_commitment,
1009
+ experience_level,
1010
+ has_children,
1011
+ noise_tolerance
1012
+ ],
1013
+ outputs=recommendation_output
1014
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
 
1016
+ history_component = create_history_tab()
 
 
 
 
1017
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1018
 
1019
+ gr.HTML('For more details on this project and other work, feel free to visit my GitHub <a href="https://github.com/Eric-Chung-0511/Learning-Record/tree/main/Data%20Science%20Projects/Dog_Breed_Classifier">Dog Breed Classifier</a>')
1020
 
1021
  if __name__ == "__main__":
1022