Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1077,336 +1077,83 @@ def update_media_input(input_type):
|
|
1077 |
else: # Video Analysis
|
1078 |
return gr.update(visible=False), gr.update(visible=True)
|
1079 |
|
|
|
1080 |
custom_css = """
|
1081 |
-
/*
|
1082 |
-
:root {
|
1083 |
-
/* Color system */
|
1084 |
-
--color-primary: #2563eb;
|
1085 |
-
--color-primary-hover: #1d4ed8;
|
1086 |
-
--color-primary-active: #1e40af;
|
1087 |
-
--color-secondary: #f1f5f9;
|
1088 |
-
--color-background: #fefefe;
|
1089 |
-
--color-surface: #ffffff;
|
1090 |
-
--color-text: #0f172a;
|
1091 |
-
--color-text-secondary: #64748b;
|
1092 |
-
--color-border: #e2e8f0;
|
1093 |
-
--color-card-border: #e2e8f0;
|
1094 |
-
|
1095 |
-
/* Spacing */
|
1096 |
-
--space-8: 8px;
|
1097 |
-
--space-12: 12px;
|
1098 |
-
--space-16: 16px;
|
1099 |
-
--space-20: 20px;
|
1100 |
-
--space-24: 24px;
|
1101 |
-
--space-32: 32px;
|
1102 |
-
|
1103 |
-
/* Typography */
|
1104 |
-
--font-size-base: 14px;
|
1105 |
-
--font-size-lg: 16px;
|
1106 |
-
--font-size-xl: 18px;
|
1107 |
-
--font-size-2xl: 20px;
|
1108 |
-
--font-size-3xl: 24px;
|
1109 |
-
--font-size-4xl: 30px;
|
1110 |
-
|
1111 |
-
/* Border radius */
|
1112 |
-
--radius-base: 8px;
|
1113 |
-
--radius-lg: 12px;
|
1114 |
-
--radius-xl: 16px;
|
1115 |
-
|
1116 |
-
/* Shadows */
|
1117 |
-
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
|
1118 |
-
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04), 0 2px 4px -1px rgba(0, 0, 0, 0.02);
|
1119 |
-
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.04), 0 4px 6px -2px rgba(0, 0, 0, 0.02);
|
1120 |
-
}
|
1121 |
-
|
1122 |
-
/* Base container styling */
|
1123 |
.gradio-container {
|
1124 |
-
background:
|
1125 |
min-height: 100vh;
|
1126 |
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
1127 |
-
}
|
1128 |
-
|
1129 |
-
/* Header styling matching your design */
|
1130 |
-
.main-header {
|
1131 |
-
background: linear-gradient(135deg, var(--color-primary) 0%, #7c3aed 100%);
|
1132 |
-
color: white;
|
1133 |
-
text-align: center;
|
1134 |
-
padding: var(--space-32);
|
1135 |
-
border-radius: var(--radius-xl);
|
1136 |
-
margin-bottom: var(--space-32);
|
1137 |
-
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.2);
|
1138 |
-
}
|
1139 |
-
|
1140 |
-
.main-header h1 {
|
1141 |
-
margin: 0;
|
1142 |
-
font-size: var(--font-size-4xl);
|
1143 |
-
font-weight: 700;
|
1144 |
-
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
.main-header p {
|
1148 |
-
margin: var(--space-16) 0 0 0;
|
1149 |
-
font-size: var(--font-size-xl);
|
1150 |
-
opacity: 0.9;
|
1151 |
-
font-weight: 400;
|
1152 |
}
|
1153 |
|
1154 |
-
/* Card styling
|
1155 |
.input-card, .questionnaire-card {
|
1156 |
-
background:
|
1157 |
-
border-radius:
|
1158 |
-
padding:
|
1159 |
-
box-shadow:
|
1160 |
-
margin:
|
1161 |
-
border: 1px solid
|
1162 |
-
transition: all 0.3s ease;
|
1163 |
}
|
1164 |
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
.section-title {
|
1171 |
-
color: var(--color-primary);
|
1172 |
-
margin: 0 0 var(--space-20) 0;
|
1173 |
text-align: center;
|
1174 |
-
|
1175 |
-
|
|
|
|
|
1176 |
}
|
1177 |
|
1178 |
-
/*
|
1179 |
-
.
|
1180 |
-
|
1181 |
-
border
|
1182 |
-
|
1183 |
-
|
|
|
|
|
|
|
|
|
1184 |
transition: all 0.3s ease;
|
1185 |
-
|
1186 |
-
}
|
1187 |
-
|
1188 |
-
.upload-area:hover {
|
1189 |
-
border-color: var(--color-primary);
|
1190 |
-
background-color: #f1f5f9;
|
1191 |
}
|
1192 |
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
border: 2px solid var(--color-border) !important;
|
1197 |
-
transition: border-color 0.3s ease !important;
|
1198 |
-
font-size: var(--font-size-base) !important;
|
1199 |
-
}
|
1200 |
-
|
1201 |
-
.gr-dropdown:focus, .gr-textbox:focus, .gr-number:focus {
|
1202 |
-
border-color: var(--color-primary) !important;
|
1203 |
-
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1) !important;
|
1204 |
-
outline: none !important;
|
1205 |
}
|
1206 |
|
1207 |
-
/* Accordion styling
|
1208 |
.accordion-header {
|
1209 |
-
background: linear-gradient(135deg, #
|
1210 |
-
border: 1px solid
|
1211 |
-
border-radius:
|
1212 |
-
padding:
|
1213 |
-
margin:
|
1214 |
cursor: pointer;
|
1215 |
transition: all 0.3s ease;
|
1216 |
}
|
1217 |
|
1218 |
.accordion-header:hover {
|
1219 |
-
background: linear-gradient(135deg, #
|
1220 |
transform: translateY(-1px);
|
1221 |
}
|
1222 |
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
}
|
1229 |
-
|
1230 |
-
.accordion-header p {
|
1231 |
-
margin: 5px 0 0 0;
|
1232 |
-
color: var(--color-text-secondary);
|
1233 |
-
font-size: var(--font-size-base);
|
1234 |
-
}
|
1235 |
-
|
1236 |
-
/* Enhanced button styling */
|
1237 |
-
.analyze-button {
|
1238 |
-
background: linear-gradient(135deg, var(--color-primary) 0%, #7c3aed 100%) !important;
|
1239 |
-
border: none !important;
|
1240 |
-
color: white !important;
|
1241 |
-
padding: var(--space-16) var(--space-32) !important;
|
1242 |
-
font-size: var(--font-size-lg) !important;
|
1243 |
-
font-weight: 600 !important;
|
1244 |
-
border-radius: 25px !important;
|
1245 |
-
cursor: pointer !important;
|
1246 |
-
transition: all 0.3s ease !important;
|
1247 |
-
box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3) !important;
|
1248 |
-
min-width: 300px !important;
|
1249 |
-
}
|
1250 |
-
|
1251 |
-
.analyze-button:hover {
|
1252 |
-
transform: translateY(-2px) !important;
|
1253 |
-
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.4) !important;
|
1254 |
-
}
|
1255 |
-
|
1256 |
-
.analyze-button:disabled {
|
1257 |
-
opacity: 0.6 !important;
|
1258 |
-
cursor: not-allowed !important;
|
1259 |
-
transform: none !important;
|
1260 |
-
}
|
1261 |
-
|
1262 |
-
/* Progress bar styling */
|
1263 |
-
.progress-container {
|
1264 |
-
margin: var(--space-24) 0;
|
1265 |
-
}
|
1266 |
-
|
1267 |
-
.progress-bar {
|
1268 |
-
width: 100%;
|
1269 |
-
height: 8px;
|
1270 |
-
background-color: var(--color-secondary);
|
1271 |
-
border-radius: 25px;
|
1272 |
-
overflow: hidden;
|
1273 |
-
margin-bottom: var(--space-8);
|
1274 |
-
}
|
1275 |
-
|
1276 |
-
.progress-fill {
|
1277 |
-
height: 100%;
|
1278 |
-
background: linear-gradient(90deg, var(--color-primary) 0%, #06b6d4 100%);
|
1279 |
-
border-radius: 25px;
|
1280 |
-
transition: width 0.5s ease;
|
1281 |
-
width: 0%;
|
1282 |
-
}
|
1283 |
-
|
1284 |
-
.progress-text {
|
1285 |
-
font-size: var(--font-size-base);
|
1286 |
-
color: var(--color-text-secondary);
|
1287 |
-
text-align: center;
|
1288 |
-
}
|
1289 |
-
|
1290 |
-
/* Results section styling */
|
1291 |
-
.results-section {
|
1292 |
-
margin-top: var(--space-32);
|
1293 |
-
background: var(--color-surface);
|
1294 |
-
border-radius: var(--radius-xl);
|
1295 |
-
padding: var(--space-32);
|
1296 |
-
box-shadow: var(--shadow-lg);
|
1297 |
-
border: 1px solid var(--color-card-border);
|
1298 |
-
}
|
1299 |
-
|
1300 |
-
.results-grid {
|
1301 |
-
display: grid;
|
1302 |
-
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
1303 |
-
gap: var(--space-20);
|
1304 |
-
margin-bottom: var(--space-24);
|
1305 |
-
}
|
1306 |
-
|
1307 |
-
.result-card {
|
1308 |
-
background: var(--color-surface);
|
1309 |
-
border: 1px solid var(--color-card-border);
|
1310 |
-
border-radius: var(--radius-lg);
|
1311 |
-
padding: var(--space-20);
|
1312 |
-
text-align: center;
|
1313 |
-
box-shadow: var(--shadow-sm);
|
1314 |
-
transition: all 0.3s ease;
|
1315 |
-
}
|
1316 |
-
|
1317 |
-
.result-card:hover {
|
1318 |
-
box-shadow: var(--shadow-md);
|
1319 |
-
transform: translateY(-2px);
|
1320 |
-
}
|
1321 |
-
|
1322 |
-
.metric-title {
|
1323 |
-
font-size: var(--font-size-base);
|
1324 |
-
font-weight: 500;
|
1325 |
-
color: var(--color-text-secondary);
|
1326 |
-
margin: 0 0 var(--space-8) 0;
|
1327 |
-
text-transform: uppercase;
|
1328 |
-
letter-spacing: 0.5px;
|
1329 |
-
}
|
1330 |
-
|
1331 |
-
.metric-value {
|
1332 |
-
font-size: var(--font-size-3xl);
|
1333 |
-
font-weight: 700;
|
1334 |
-
color: var(--color-primary);
|
1335 |
-
margin: 0;
|
1336 |
-
}
|
1337 |
-
|
1338 |
-
.metric-subtitle {
|
1339 |
-
font-size: var(--font-size-base);
|
1340 |
-
color: var(--color-text-secondary);
|
1341 |
-
margin: var(--space-8) 0 0 0;
|
1342 |
-
}
|
1343 |
-
|
1344 |
-
/* Score bars */
|
1345 |
-
.score-bar {
|
1346 |
-
width: 100%;
|
1347 |
-
height: 8px;
|
1348 |
-
background-color: var(--color-secondary);
|
1349 |
-
border-radius: 25px;
|
1350 |
-
overflow: hidden;
|
1351 |
-
margin: var(--space-12) 0;
|
1352 |
-
}
|
1353 |
-
|
1354 |
-
.score-fill {
|
1355 |
-
height: 100%;
|
1356 |
-
border-radius: 25px;
|
1357 |
-
transition: width 0.8s ease;
|
1358 |
-
}
|
1359 |
-
|
1360 |
-
.score-excellent { background: linear-gradient(90deg, #10b981 0%, #34d399 100%); }
|
1361 |
-
.score-good { background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 100%); }
|
1362 |
-
.score-fair { background: linear-gradient(90deg, #f59e0b 0%, #fbbf24 100%); }
|
1363 |
-
.score-poor { background: linear-gradient(90deg, #ef4444 0%, #f87171 100%); }
|
1364 |
-
|
1365 |
-
/* Recommendations styling */
|
1366 |
-
.recommendations-list {
|
1367 |
-
list-style: none;
|
1368 |
-
padding: 0;
|
1369 |
-
margin: 0;
|
1370 |
-
}
|
1371 |
-
|
1372 |
-
.recommendations-list li {
|
1373 |
-
background: #f8fafc;
|
1374 |
-
padding: var(--space-12) var(--space-16);
|
1375 |
-
margin: var(--space-8) 0;
|
1376 |
-
border-radius: var(--radius-base);
|
1377 |
-
border-left: 4px solid var(--color-primary);
|
1378 |
-
font-size: var(--font-size-base);
|
1379 |
-
line-height: 1.5;
|
1380 |
-
}
|
1381 |
-
|
1382 |
-
/* Health aspects grid */
|
1383 |
-
.health-aspects {
|
1384 |
-
display: grid;
|
1385 |
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
1386 |
-
gap: var(--space-16);
|
1387 |
-
margin-top: var(--space-24);
|
1388 |
-
}
|
1389 |
-
|
1390 |
-
.aspect-item {
|
1391 |
-
display: flex;
|
1392 |
-
justify-content: space-between;
|
1393 |
-
align-items: center;
|
1394 |
-
padding: var(--space-12);
|
1395 |
-
background: #f8fafc;
|
1396 |
-
border-radius: var(--radius-base);
|
1397 |
-
font-size: var(--font-size-base);
|
1398 |
-
}
|
1399 |
-
|
1400 |
-
.aspect-name {
|
1401 |
-
font-weight: 500;
|
1402 |
-
color: var(--color-text);
|
1403 |
}
|
1404 |
|
1405 |
-
.
|
1406 |
-
color:
|
|
|
1407 |
}
|
1408 |
|
1409 |
-
/*
|
1410 |
@keyframes pulse {
|
1411 |
0% { opacity: 1; }
|
1412 |
50% { opacity: 0.5; }
|
@@ -1416,196 +1163,152 @@ custom_css = """
|
|
1416 |
.loading-pulse {
|
1417 |
animation: pulse 2s infinite;
|
1418 |
}
|
1419 |
-
|
1420 |
-
/* Responsive design */
|
1421 |
-
@media (max-width: 768px) {
|
1422 |
-
.main-header {
|
1423 |
-
padding: var(--space-24);
|
1424 |
-
}
|
1425 |
-
|
1426 |
-
.main-header h1 {
|
1427 |
-
font-size: var(--font-size-3xl);
|
1428 |
-
}
|
1429 |
-
|
1430 |
-
.main-header p {
|
1431 |
-
font-size: var(--font-size-lg);
|
1432 |
-
}
|
1433 |
-
|
1434 |
-
.input-card, .questionnaire-card {
|
1435 |
-
padding: var(--space-20);
|
1436 |
-
margin: var(--space-12) 0;
|
1437 |
-
}
|
1438 |
-
|
1439 |
-
.analyze-button {
|
1440 |
-
min-width: auto !important;
|
1441 |
-
width: 100% !important;
|
1442 |
-
}
|
1443 |
-
|
1444 |
-
.results-grid {
|
1445 |
-
grid-template-columns: 1fr;
|
1446 |
-
}
|
1447 |
-
}
|
1448 |
-
|
1449 |
-
/* Hide default gradio styling that conflicts */
|
1450 |
-
.gradio-container .prose {
|
1451 |
-
max-width: none !important;
|
1452 |
-
}
|
1453 |
-
|
1454 |
-
.gradio-container .container {
|
1455 |
-
max-width: none !important;
|
1456 |
-
}
|
1457 |
-
|
1458 |
-
/* Enhanced spacing for main content */
|
1459 |
-
.gradio-container > .contain {
|
1460 |
-
padding: var(--space-24) !important;
|
1461 |
-
max-width: 1200px !important;
|
1462 |
-
margin: 0 auto !important;
|
1463 |
-
}
|
1464 |
"""
|
1465 |
|
1466 |
-
#
|
1467 |
-
# (Keep all your existing functions exactly as they are)
|
1468 |
-
|
1469 |
-
def update_media_input(analysis_type):
|
1470 |
-
if analysis_type == "Image Analysis":
|
1471 |
-
return gr.update(visible=True), gr.update(visible=False)
|
1472 |
-
else:
|
1473 |
-
return gr.update(visible=False), gr.update(visible=True)
|
1474 |
-
|
1475 |
-
# Gradio Interface with Clean Professional UI
|
1476 |
with gr.Blocks(
|
1477 |
title="πΆ Enhanced AI Dog Health Analyzer",
|
1478 |
theme=gr.themes.Soft(),
|
1479 |
css=custom_css
|
1480 |
) as demo:
|
1481 |
|
1482 |
-
# Main Header
|
1483 |
gr.HTML("""
|
1484 |
<div class="main-header">
|
1485 |
-
<h1
|
1486 |
-
|
|
|
|
|
|
|
|
|
1487 |
</div>
|
1488 |
""")
|
1489 |
|
1490 |
with gr.Row():
|
1491 |
-
# Left Column - Media Input
|
1492 |
with gr.Column(scale=1):
|
1493 |
-
|
1494 |
-
|
1495 |
-
<
|
1496 |
-
|
1497 |
-
</
|
1498 |
-
|
1499 |
-
|
1500 |
-
# Analysis type dropdown
|
1501 |
-
input_type_dropdown = gr.Dropdown(
|
1502 |
-
choices=["Image Analysis", "Video Analysis"],
|
1503 |
-
label="π Select Analysis Type",
|
1504 |
-
value="Image Analysis",
|
1505 |
-
interactive=True,
|
1506 |
-
elem_classes=["gr-dropdown"]
|
1507 |
-
)
|
1508 |
-
|
1509 |
-
# Media input components
|
1510 |
-
image_input = gr.Image(
|
1511 |
-
type="pil",
|
1512 |
-
label="π· Upload Dog Photo or Use Webcam",
|
1513 |
-
visible=True,
|
1514 |
-
sources=["upload", "webcam"],
|
1515 |
-
height=300
|
1516 |
-
)
|
1517 |
-
|
1518 |
-
video_input = gr.Video(
|
1519 |
-
label="π₯ Upload Video (10-30 seconds) or Record with Webcam",
|
1520 |
-
visible=False,
|
1521 |
-
sources=["upload", "webcam"],
|
1522 |
-
height=300
|
1523 |
-
)
|
1524 |
-
|
1525 |
-
# Update visibility based on dropdown selection
|
1526 |
-
input_type_dropdown.change(
|
1527 |
-
fn=update_media_input,
|
1528 |
-
inputs=[input_type_dropdown],
|
1529 |
-
outputs=[image_input, video_input]
|
1530 |
-
)
|
1531 |
|
1532 |
-
#
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
""
|
1539 |
-
|
1540 |
-
|
1541 |
-
|
1542 |
-
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1555 |
|
1556 |
-
# Right Column - HRQOL Questionnaire
|
1557 |
with gr.Column(scale=1):
|
1558 |
-
|
1559 |
-
|
1560 |
-
<
|
1561 |
-
|
1562 |
-
|
1563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1564 |
</p>
|
1565 |
</div>
|
1566 |
""")
|
1567 |
|
1568 |
-
|
1569 |
-
|
1570 |
-
|
1571 |
-
|
1572 |
-
|
1573 |
-
|
1574 |
-
|
1575 |
-
|
1576 |
-
|
1577 |
-
|
1578 |
-
|
1579 |
-
with gr.Accordion(domain_data["title"], open=True):
|
1580 |
-
for question in domain_data["questions"]:
|
1581 |
-
# Enhanced dropdown for each question
|
1582 |
-
dropdown = gr.Dropdown(
|
1583 |
-
choices=question["options"],
|
1584 |
-
label=question["text"],
|
1585 |
-
value=None,
|
1586 |
-
interactive=True,
|
1587 |
-
elem_classes=["gr-dropdown"]
|
1588 |
-
)
|
1589 |
-
hrqol_inputs.append(dropdown)
|
1590 |
|
1591 |
-
# Enhanced Analysis Button
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
|
1605 |
# Enhanced Results Section
|
1606 |
output_report = gr.HTML()
|
1607 |
|
1608 |
-
# Connect analysis function
|
1609 |
analyze_button.click(
|
1610 |
fn=comprehensive_healthspan_analysis,
|
1611 |
inputs=[input_type_dropdown, image_input, video_input, breed_input, age_input] + hrqol_inputs,
|
@@ -1613,4 +1316,4 @@ with gr.Blocks(
|
|
1613 |
)
|
1614 |
|
1615 |
if __name__ == "__main__":
|
1616 |
-
demo.launch()
|
|
|
1077 |
else: # Video Analysis
|
1078 |
return gr.update(visible=False), gr.update(visible=True)
|
1079 |
|
1080 |
+
# Custom CSS for enhanced styling
|
1081 |
custom_css = """
|
1082 |
+
/* Enhanced gradient background */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1083 |
.gradio-container {
|
1084 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
1085 |
min-height: 100vh;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1086 |
}
|
1087 |
|
1088 |
+
/* Card styling */
|
1089 |
.input-card, .questionnaire-card {
|
1090 |
+
background: white;
|
1091 |
+
border-radius: 15px;
|
1092 |
+
padding: 25px;
|
1093 |
+
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
1094 |
+
margin: 10px;
|
1095 |
+
border: 1px solid #e0e6ed;
|
|
|
1096 |
}
|
1097 |
|
1098 |
+
/* Header styling */
|
1099 |
+
.main-header {
|
1100 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
1101 |
+
color: white;
|
|
|
|
|
|
|
|
|
1102 |
text-align: center;
|
1103 |
+
padding: 30px;
|
1104 |
+
border-radius: 15px;
|
1105 |
+
margin-bottom: 30px;
|
1106 |
+
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
1107 |
}
|
1108 |
|
1109 |
+
/* Button styling */
|
1110 |
+
.analyze-button {
|
1111 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
1112 |
+
border: none;
|
1113 |
+
color: white;
|
1114 |
+
padding: 15px 30px;
|
1115 |
+
font-size: 16px;
|
1116 |
+
font-weight: 600;
|
1117 |
+
border-radius: 25px;
|
1118 |
+
cursor: pointer;
|
1119 |
transition: all 0.3s ease;
|
1120 |
+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
|
|
|
|
|
|
|
|
|
|
1121 |
}
|
1122 |
|
1123 |
+
.analyze-button:hover {
|
1124 |
+
transform: translateY(-2px);
|
1125 |
+
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1126 |
}
|
1127 |
|
1128 |
+
/* Accordion styling */
|
1129 |
.accordion-header {
|
1130 |
+
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
1131 |
+
border: 1px solid #dee2e6;
|
1132 |
+
border-radius: 8px;
|
1133 |
+
padding: 15px;
|
1134 |
+
margin: 10px 0;
|
1135 |
cursor: pointer;
|
1136 |
transition: all 0.3s ease;
|
1137 |
}
|
1138 |
|
1139 |
.accordion-header:hover {
|
1140 |
+
background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
|
1141 |
transform: translateY(-1px);
|
1142 |
}
|
1143 |
|
1144 |
+
/* Dropdown styling */
|
1145 |
+
.gr-dropdown {
|
1146 |
+
border-radius: 8px;
|
1147 |
+
border: 2px solid #e0e6ed;
|
1148 |
+
transition: border-color 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1149 |
}
|
1150 |
|
1151 |
+
.gr-dropdown:focus {
|
1152 |
+
border-color: #667eea;
|
1153 |
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
1154 |
}
|
1155 |
|
1156 |
+
/* Progress animation */
|
1157 |
@keyframes pulse {
|
1158 |
0% { opacity: 1; }
|
1159 |
50% { opacity: 0.5; }
|
|
|
1163 |
.loading-pulse {
|
1164 |
animation: pulse 2s infinite;
|
1165 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1166 |
"""
|
1167 |
|
1168 |
+
# Gradio Interface with Enhanced UI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1169 |
with gr.Blocks(
|
1170 |
title="πΆ Enhanced AI Dog Health Analyzer",
|
1171 |
theme=gr.themes.Soft(),
|
1172 |
css=custom_css
|
1173 |
) as demo:
|
1174 |
|
1175 |
+
# Main Header
|
1176 |
gr.HTML("""
|
1177 |
<div class="main-header">
|
1178 |
+
<h1 style="margin: 0; font-size: 2.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
1179 |
+
π Enhanced AI Dog Health & Aging Analyzer
|
1180 |
+
</h1>
|
1181 |
+
<p style="margin: 15px 0 0 0; font-size: 1.2em; opacity: 0.9;">
|
1182 |
+
Advanced Biological Age Prediction β’ Multi-Factor Analysis β’ Breed-Specific Calibration
|
1183 |
+
</p>
|
1184 |
</div>
|
1185 |
""")
|
1186 |
|
1187 |
with gr.Row():
|
1188 |
+
# Left Column - Enhanced Media Input
|
1189 |
with gr.Column(scale=1):
|
1190 |
+
gr.HTML("""
|
1191 |
+
<div class="input-card">
|
1192 |
+
<h2 style="color: #667eea; margin: 0 0 20px 0; text-align: center;">
|
1193 |
+
πΈ Media Input Selection
|
1194 |
+
</h2>
|
1195 |
+
</div>
|
1196 |
+
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1197 |
|
1198 |
+
# Enhanced dropdown with better styling
|
1199 |
+
input_type_dropdown = gr.Dropdown(
|
1200 |
+
choices=["Image Analysis", "Video Analysis"],
|
1201 |
+
label="π Select Analysis Type",
|
1202 |
+
value="Image Analysis",
|
1203 |
+
interactive=True,
|
1204 |
+
elem_classes=["gr-dropdown"]
|
1205 |
+
)
|
1206 |
+
|
1207 |
+
# Media input components with enhanced labels
|
1208 |
+
image_input = gr.Image(
|
1209 |
+
type="pil",
|
1210 |
+
label="π· Upload Dog Photo or Use Webcam",
|
1211 |
+
visible=True,
|
1212 |
+
sources=["upload", "webcam"],
|
1213 |
+
height=300
|
1214 |
+
)
|
1215 |
+
|
1216 |
+
video_input = gr.Video(
|
1217 |
+
label="π₯ Upload Video (10-30 seconds) or Record with Webcam",
|
1218 |
+
visible=False,
|
1219 |
+
sources=["upload", "webcam"],
|
1220 |
+
height=300
|
1221 |
+
)
|
1222 |
+
|
1223 |
+
# Update visibility based on dropdown selection
|
1224 |
+
input_type_dropdown.change(
|
1225 |
+
fn=update_media_input,
|
1226 |
+
inputs=[input_type_dropdown],
|
1227 |
+
outputs=[image_input, video_input]
|
1228 |
+
)
|
1229 |
+
|
1230 |
+
# Enhanced optional information section
|
1231 |
+
gr.HTML("""
|
1232 |
+
<div style="margin: 20px 0;">
|
1233 |
+
<h3 style="color: #667eea; text-align: center; margin-bottom: 15px;">
|
1234 |
+
β Optional Information
|
1235 |
+
</h3>
|
1236 |
+
</div>
|
1237 |
+
""")
|
1238 |
+
|
1239 |
+
breed_input = gr.Dropdown(
|
1240 |
+
STANFORD_BREEDS,
|
1241 |
+
label="π Dog Breed (Auto-detected if not specified)",
|
1242 |
+
value=None,
|
1243 |
+
allow_custom_value=True,
|
1244 |
+
elem_classes=["gr-dropdown"]
|
1245 |
+
)
|
1246 |
+
age_input = gr.Number(
|
1247 |
+
label="π
Chronological Age (years)",
|
1248 |
+
precision=1,
|
1249 |
+
value=None,
|
1250 |
+
minimum=0,
|
1251 |
+
maximum=25
|
1252 |
+
)
|
1253 |
|
1254 |
+
# Right Column - SHORTENED HRQOL Questionnaire
|
1255 |
with gr.Column(scale=1):
|
1256 |
+
gr.HTML("""
|
1257 |
+
<div class="questionnaire-card">
|
1258 |
+
<h2 style="color: #667eea; margin: 0 0 10px 0; text-align: center;">
|
1259 |
+
π Streamlined HRQOL Assessment
|
1260 |
+
</h2>
|
1261 |
+
<p style="text-align: center; color: #666; font-style: italic; margin-bottom: 20px;">
|
1262 |
+
Complete all 4 comprehensive questions for accurate healthspan analysis
|
1263 |
+
</p>
|
1264 |
+
</div>
|
1265 |
+
""")
|
1266 |
+
|
1267 |
+
hrqol_inputs = []
|
1268 |
+
|
1269 |
+
for domain_key, domain_data in HRQOL_QUESTIONNAIRE.items():
|
1270 |
+
# Enhanced accordion header
|
1271 |
+
gr.HTML(f"""
|
1272 |
+
<div class="accordion-header">
|
1273 |
+
<h3 style="margin: 0; color: #333;">
|
1274 |
+
{domain_data['title']}
|
1275 |
+
</h3>
|
1276 |
+
<p style="margin: 5px 0 0 0; color: #666; font-size: 0.9em;">
|
1277 |
+
{domain_data['description']}
|
1278 |
</p>
|
1279 |
</div>
|
1280 |
""")
|
1281 |
|
1282 |
+
with gr.Accordion(domain_data["title"], open=True):
|
1283 |
+
for question in domain_data["questions"]:
|
1284 |
+
# Enhanced dropdown for each question
|
1285 |
+
dropdown = gr.Dropdown(
|
1286 |
+
choices=question["options"],
|
1287 |
+
label=question["text"],
|
1288 |
+
value=None,
|
1289 |
+
interactive=True,
|
1290 |
+
elem_classes=["gr-dropdown"]
|
1291 |
+
)
|
1292 |
+
hrqol_inputs.append(dropdown)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1293 |
|
1294 |
+
# Enhanced Analysis Button
|
1295 |
+
gr.HTML("""
|
1296 |
+
<div style="text-align: center; margin: 30px 0;">
|
1297 |
+
""")
|
1298 |
+
|
1299 |
+
analyze_button = gr.Button(
|
1300 |
+
"π¬ Run Advanced AI Analysis",
|
1301 |
+
variant="primary",
|
1302 |
+
size="lg",
|
1303 |
+
elem_classes=["analyze-button"]
|
1304 |
+
)
|
1305 |
+
|
1306 |
+
gr.HTML("</div>")
|
1307 |
|
1308 |
# Enhanced Results Section
|
1309 |
output_report = gr.HTML()
|
1310 |
|
1311 |
+
# Connect analysis function with loading
|
1312 |
analyze_button.click(
|
1313 |
fn=comprehensive_healthspan_analysis,
|
1314 |
inputs=[input_type_dropdown, image_input, video_input, breed_input, age_input] + hrqol_inputs,
|
|
|
1316 |
)
|
1317 |
|
1318 |
if __name__ == "__main__":
|
1319 |
+
demo.launch()
|