faceimotion / index.html
manmohanai's picture
add functionality capture video and detect water container. check how much ml of water it can have do measurement automatically. - Follow Up Deployment
9a35b42 verified
Raw
History Blame Contribute Delete
21.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hydration Tracker</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd@2.2.2/dist/coco-ssd.min.js"></script>
<style>
.wave {
position: relative;
overflow: hidden;
}
.wave::before {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
background-repeat: repeat;
height: 10px;
background-size: 20px 20px;
background-image: radial-gradient(circle at 10px -5px, transparent 12px, #3b82f6 13px);
}
.wave::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
background-repeat: repeat;
height: 15px;
background-size: 40px 20px;
background-image: radial-gradient(circle at 10px 15px, #3b82f6 12px, transparent 13px);
}
.glass {
transition: all 0.5s ease;
}
.glass:hover {
transform: translateY(-5px);
}
.progress-ring__circle {
transition: stroke-dashoffset 0.5s;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
</style>
</head>
<body class="bg-blue-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="max-w-3xl mx-auto">
<!-- Header -->
<header class="text-center mb-8">
<h1 class="text-4xl font-bold text-blue-800 mb-2">Hydration Tracker</h1>
<p class="text-blue-600">Track your daily water intake and stay hydrated</p>
</header>
<!-- Main Content -->
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<!-- Progress Circle -->
<div class="flex flex-col items-center mb-8">
<div class="relative w-48 h-48 mb-4">
<svg class="w-full h-full" viewBox="0 0 100 100">
<!-- Background circle -->
<circle cx="50" cy="50" r="45" fill="none" stroke="#e2e8f0" stroke-width="8"/>
<!-- Progress circle -->
<circle id="progress-circle" cx="50" cy="50" r="45" fill="none" stroke="#3b82f6"
stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283"/>
</svg>
<div class="absolute inset-0 flex flex-col items-center justify-center">
<span id="progress-percent" class="text-3xl font-bold text-blue-800">0%</span>
<span class="text-blue-600">Daily Goal</span>
</div>
</div>
<div id="progress-text" class="text-center">
<p class="text-lg font-semibold text-blue-800">Start drinking water!</p>
<p class="text-blue-600">0 of 8 glasses (2L)</p>
</div>
</div>
<!-- Video Capture -->
<div class="mb-8 bg-blue-50 rounded-lg p-6">
<h2 class="text-xl font-semibold text-blue-800 mb-4">Measure Container Automatically</h2>
<div class="flex flex-col items-center">
<video id="video" width="320" height="240" autoplay class="mb-4 rounded-lg bg-gray-200"></video>
<canvas id="canvas" width="320" height="240" class="hidden"></canvas>
<button id="capture-btn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
<i class="fas fa-camera mr-2"></i>Capture & Measure
</button>
<div id="measure-result" class="mt-4 text-center hidden">
<p class="font-medium text-blue-800">Detected Container:</p>
<p id="detected-container" class="text-lg font-bold text-blue-800">-</p>
<p id="detected-size" class="text-blue-600">- ml</p>
</div>
</div>
</div>
<!-- Water Container Selection -->
<div class="mb-8">
<h2 class="text-xl font-semibold text-blue-800 mb-4">Select Your Container</h2>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div class="glass cursor-pointer bg-blue-50 rounded-lg p-4 flex flex-col items-center"
data-size="250" data-name="Small Glass">
<div class="w-16 h-24 bg-blue-100 rounded-b-lg rounded-t-full relative wave mb-2"
style="height: 40%;"></div>
<span class="text-sm font-medium text-blue-800">Small Glass</span>
<span class="text-xs text-blue-600">250ml</span>
</div>
<div class="glass cursor-pointer bg-blue-50 rounded-lg p-4 flex flex-col items-center"
data-size="350" data-name="Medium Glass">
<div class="w-16 h-24 bg-blue-100 rounded-b-lg rounded-t-full relative wave mb-2"
style="height: 60%;"></div>
<span class="text-sm font-medium text-blue-800">Medium Glass</span>
<span class="text-xs text-blue-600">350ml</span>
</div>
<div class="glass cursor-pointer bg-blue-50 rounded-lg p-4 flex flex-col items-center"
data-size="500" data-name="Large Glass">
<div class="w-16 h-24 bg-blue-100 rounded-b-lg rounded-t-full relative wave mb-2"
style="height: 80%;"></div>
<span class="text-sm font-medium text-blue-800">Large Glass</span>
<span class="text-xs text-blue-600">500ml</span>
</div>
<div class="glass cursor-pointer bg-blue-50 rounded-lg p-4 flex flex-col items-center"
data-size="1000" data-name="Bottle">
<div class="w-16 h-24 bg-blue-100 rounded-b-lg rounded-t-full relative wave mb-2"
style="height: 100%;"></div>
<span class="text-sm font-medium text-blue-800">Bottle</span>
<span class="text-xs text-blue-600">1L</span>
</div>
</div>
</div>
<!-- Add Water Intake -->
<div class="mb-8">
<h2 class="text-xl font-semibold text-blue-800 mb-4">Add Water Intake</h2>
<div class="flex flex-col sm:flex-row gap-4">
<div class="flex-1">
<label for="custom-amount" class="block text-sm font-medium text-blue-700 mb-1">Custom Amount (ml)</label>
<input type="number" id="custom-amount"
class="w-full px-4 py-2 border border-blue-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<div class="flex items-end">
<button id="add-water"
class="w-full sm:w-auto px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition">
Add Water
</button>
</div>
</div>
</div>
<!-- Today's Intake -->
<div>
<h2 class="text-xl font-semibold text-blue-800 mb-4">Today's Intake</h2>
<div id="intake-list" class="space-y-3">
<div class="text-center py-4 text-blue-600" id="empty-state">
No water intake recorded yet. Add your first glass!
</div>
</div>
<div class="mt-4 pt-4 border-t border-blue-100">
<div class="flex justify-between items-center">
<span class="font-medium text-blue-800">Total:</span>
<span id="total-intake" class="font-bold text-blue-800">0 ml</span>
</div>
<div class="flex justify-between items-center mt-1">
<span class="font-medium text-blue-800">Glasses:</span>
<span id="total-glasses" class="font-bold text-blue-800">0</span>
</div>
</div>
</div>
</div>
<!-- Hydration Tips -->
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-semibold text-blue-800 mb-4">Hydration Tips</h2>
<div class="space-y-4">
<div class="flex items-start">
<div class="flex-shrink-0 mt-1 mr-3 text-blue-500">
<i class="fas fa-check-circle"></i>
</div>
<p class="text-blue-700">Aim for 8 glasses (2 liters) of water per day as a general guideline.</p>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 mt-1 mr-3 text-blue-500">
<i class="fas fa-check-circle"></i>
</div>
<p class="text-blue-700">Drink more if you're active, in hot weather, or pregnant/breastfeeding.</p>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 mt-1 mr-3 text-blue-500">
<i class="fas fa-check-circle"></i>
</div>
<p class="text-blue-700">Your urine color is a good indicator - aim for pale yellow.</p>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', async function() {
// Video Capture Elements
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const captureBtn = document.getElementById('capture-btn');
const measureResult = document.getElementById('measure-result');
const detectedContainer = document.getElementById('detected-container');
const detectedSize = document.getElementById('detected-size');
// Load COCO-SSD model
let model;
try {
model = await cocoSsd.load();
console.log('Model loaded successfully');
} catch (err) {
console.error('Failed to load model:', err);
}
// Start video stream
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true })
.then(function(stream) {
video.srcObject = stream;
})
.catch(function(err) {
console.error("Error accessing camera:", err);
});
}
// Capture and detect button
captureBtn.addEventListener('click', async function() {
if (!model) {
alert('Model not loaded yet. Please wait...');
return;
}
// Draw video frame to canvas
const context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Detect objects
const predictions = await model.detect(canvas);
console.log('Predictions:', predictions);
// Find drinking glasses or bottles
const container = predictions.find(p =>
['bottle', 'cup', 'wine glass'].includes(p.class)
);
if (container) {
// Estimate size based on bounding box dimensions
const area = container.bbox[2] * container.bbox[3];
let size;
if (container.class === 'bottle') {
size = Math.round(area * 0.15); // Bottle size estimation
if (size > 1500) size = 1000;
else if (size > 800) size = 750;
else size = 500;
} else {
size = Math.round(area * 0.3); // Glass size estimation
if (size > 400) size = 500;
else if (size > 300) size = 350;
else size = 250;
}
detectedContainer.textContent = container.class.charAt(0).toUpperCase() + container.class.slice(1);
detectedSize.textContent = `${size} ml`;
measureResult.classList.remove('hidden');
// Auto-fill the custom amount field
customAmount.value = size;
} else {
detectedContainer.textContent = 'No container detected';
detectedSize.textContent = '';
measureResult.classList.remove('hidden');
}
});
// Constants
const DAILY_GOAL_ML = 2000; // 2 liters
const GLASS_SIZE_ML = 250; // Standard glass size
// DOM Elements
const progressCircle = document.getElementById('progress-circle');
const progressPercent = document.getElementById('progress-percent');
const progressText = document.getElementById('progress-text');
const totalIntake = document.getElementById('total-intake');
const totalGlasses = document.getElementById('total-glasses');
const intakeList = document.getElementById('intake-list');
const emptyState = document.getElementById('empty-state');
const addWaterBtn = document.getElementById('add-water');
const customAmount = document.getElementById('custom-amount');
const glassContainers = document.querySelectorAll('.glass');
// State
let currentIntake = 0;
let glassesCount = 0;
// Initialize
updateUI();
// Event Listeners
glassContainers.forEach(glass => {
glass.addEventListener('click', function() {
const size = parseInt(this.dataset.size);
const name = this.dataset.name;
addWater(size, name);
});
});
addWaterBtn.addEventListener('click', function() {
const amount = parseInt(customAmount.value);
if (amount && amount > 0) {
addWater(amount, 'Custom Amount');
customAmount.value = '';
}
});
// Functions
function addWater(amount, containerName) {
currentIntake += amount;
glassesCount = Math.round(currentIntake / GLASS_SIZE_ML);
// Add to history
if (emptyState) emptyState.style.display = 'none';
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const intakeItem = document.createElement('div');
intakeItem.className = 'flex justify-between items-center bg-blue-50 p-3 rounded-lg';
intakeItem.innerHTML = `
<div class="flex items-center">
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center mr-3">
<i class="fas fa-tint text-blue-500"></i>
</div>
<div>
<p class="font-medium text-blue-800">${containerName}</p>
<p class="text-xs text-blue-600">${timeString}</p>
</div>
</div>
<span class="font-bold text-blue-800">${amount} ml</span>
`;
intakeList.prepend(intakeItem);
updateUI();
// Show feedback based on progress
const percent = Math.min(Math.round((currentIntake / DAILY_GOAL_ML) * 100), 100);
if (percent >= 100) {
showFeedback("Great job! You've reached your daily goal!", "text-green-600");
} else if (percent >= 75) {
showFeedback("Almost there! Keep going!", "text-blue-600");
} else if (percent >= 50) {
showFeedback("Halfway there! You're doing well.", "text-blue-600");
} else if (percent >= 25) {
showFeedback("Good start! Keep hydrating.", "text-blue-600");
}
}
function updateUI() {
const percent = Math.min((currentIntake / DAILY_GOAL_ML) * 100, 100);
const dashoffset = 283 - (283 * percent / 100);
progressCircle.style.strokeDashoffset = dashoffset;
progressPercent.textContent = `${Math.round(percent)}%`;
totalIntake.textContent = `${currentIntake} ml`;
totalGlasses.textContent = glassesCount;
// Update progress text
if (percent >= 100) {
progressText.innerHTML = `
<p class="text-lg font-semibold text-green-600">Goal Achieved!</p>
<p class="text-green-600">${glassesCount} of 8 glasses (${(currentIntake/1000).toFixed(1)}L of 2L)</p>
`;
} else {
progressText.innerHTML = `
<p class="text-lg font-semibold text-blue-800">${glassesCount >= 4 ? 'Good progress!' : 'Keep drinking!'}</p>
<p class="text-blue-600">${glassesCount} of 8 glasses (${(currentIntake/1000).toFixed(1)}L of 2L)</p>
`;
}
}
function showFeedback(message, colorClass) {
const feedback = document.createElement('div');
feedback.className = `fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-white shadow-lg rounded-lg px-6 py-3 flex items-center ${colorClass} animate-bounce`;
feedback.innerHTML = `
<i class="fas fa-check-circle mr-2"></i>
<span>${message}</span>
`;
document.body.appendChild(feedback);
setTimeout(() => {
feedback.classList.remove('animate-bounce');
feedback.classList.add('opacity-0', 'transition-opacity', 'duration-300');
setTimeout(() => feedback.remove(), 300);
}, 3000);
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=manmohanai/faceimotion" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>