Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- templates/index.html +228 -207
templates/index.html
CHANGED
|
@@ -284,7 +284,7 @@
|
|
| 284 |
<i class="fas fa-eye text-indigo-600 dark:text-purple-300 text-xl"></i>
|
| 285 |
</div>
|
| 286 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Spatial Engine</h4>
|
| 287 |
-
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">YOLOv8
|
| 288 |
</div>
|
| 289 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 290 |
<div class="w-12 h-12 rounded-2xl bg-fuchsia-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-fuchsia-200/50 dark:border-white/10 shadow-sm">
|
|
@@ -317,20 +317,40 @@
|
|
| 317 |
|
| 318 |
<!-- Interactive Media Player Area -->
|
| 319 |
<div class="rounded-[32px] overflow-hidden shadow-2xl border border-white/50 dark:border-white/20 relative bg-black aspect-video flex items-center justify-center group">
|
| 320 |
-
<!-- Display Image -->
|
| 321 |
<img id="previewImg" src="" class="w-full h-full object-contain z-10 hidden" alt="Detection View">
|
| 322 |
-
<!--
|
| 323 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
<!-- Live Tag -->
|
| 326 |
-
<div class="absolute top-6 left-6 z-20">
|
| 327 |
<span id="liveBadge" class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 328 |
-
<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span>
|
| 329 |
</span>
|
| 330 |
</div>
|
| 331 |
</div>
|
| 332 |
|
| 333 |
-
<!-- Media Controls
|
| 334 |
<div class="flex justify-center gap-4 mt-8" id="mediaControls">
|
| 335 |
<button onclick="takeSnapshot()" class="px-5 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest text-indigo-900 dark:text-white bg-white/50 dark:bg-white/10 border border-indigo-200/50 dark:border-white/20 hover:scale-105 transition-transform shadow-sm flex items-center gap-2">
|
| 336 |
<i class="fas fa-camera"></i> Snapshot
|
|
@@ -339,7 +359,7 @@
|
|
| 339 |
<i class="fas fa-brain"></i> AI Tracking
|
| 340 |
</button>
|
| 341 |
<button onclick="goOriginal()" id="oBtn" class="px-5 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest text-indigo-900 dark:text-white bg-white/50 dark:bg-white/10 border border-indigo-200/50 dark:border-white/20 hover:scale-105 transition-transform shadow-sm flex items-center gap-2">
|
| 342 |
-
<i class="fas fa-play"></i>
|
| 343 |
</button>
|
| 344 |
</div>
|
| 345 |
|
|
@@ -734,268 +754,269 @@
|
|
| 734 |
if(document.getElementById('vLoc')) document.getElementById('vLoc').innerText = "Not Supported";
|
| 735 |
}
|
| 736 |
const now = new Date();
|
| 737 |
-
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = now.toLocaleTimeString([], {hour: '2-digit', minute:
|
| 738 |
}
|
| 739 |
|
| 740 |
-
fetchRealLocationData();
|
| 741 |
-
|
| 742 |
-
const fileInput = document.getElementById('fileInput');
|
| 743 |
-
const uploadSection = document.getElementById('uploadSection');
|
| 744 |
-
const resultsDiv = document.getElementById('results');
|
| 745 |
-
const emergencyBanner = document.getElementById('emergencyBanner');
|
| 746 |
-
const ipCamModal = document.getElementById('ipCamModal');
|
| 747 |
-
const predictiveModal = document.getElementById('predictiveModal');
|
| 748 |
-
|
| 749 |
let streamId = null;
|
| 750 |
let poller = null;
|
| 751 |
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 758 |
|
| 759 |
async function connectIpCam() {
|
| 760 |
-
const url = document.getElementById('ipCamUrl').value
|
| 761 |
if(!url) return;
|
| 762 |
closeIpCamModal();
|
| 763 |
-
|
| 764 |
}
|
| 765 |
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
uploadSection.classList.add('hidden');
|
| 769 |
-
resultsDiv.classList.remove('hidden');
|
| 770 |
-
|
| 771 |
const fd = new FormData();
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
headers: {'Content-Type': 'application/json'},
|
| 780 |
-
body: JSON.stringify({url: document.getElementById('ipCamUrl').value})
|
| 781 |
-
});
|
| 782 |
-
}
|
| 783 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
const data = await res.json();
|
| 785 |
streamId = data.stream_id;
|
| 786 |
|
| 787 |
-
if(
|
| 788 |
-
|
| 789 |
-
if(document.getElementById('vWx')) document.getElementById('vWx').innerText = "--";
|
| 790 |
-
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = "--";
|
| 791 |
} else {
|
| 792 |
-
|
| 793 |
-
if(document.getElementById('vWx')) document.getElementById('vWx').innerText = data.weather || "--";
|
| 794 |
-
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = data.time || "--";
|
| 795 |
}
|
| 796 |
-
|
| 797 |
-
|
|
|
|
|
|
|
|
|
|
| 798 |
activateTrackingUI();
|
| 799 |
|
| 800 |
-
// Set the source directly so the image connects right away
|
| 801 |
const locText = document.getElementById('vLoc') ? document.getElementById('vLoc').innerText : "Unknown";
|
| 802 |
document.getElementById('previewImg').src = `/video_feed/${streamId}?loc=${encodeURIComponent(locText)}&t=${Date.now()}`;
|
|
|
|
| 803 |
|
| 804 |
if(poller) clearInterval(poller);
|
| 805 |
poller = setInterval(update, 1000);
|
| 806 |
}
|
| 807 |
|
| 808 |
function activateTrackingUI() {
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
document.getElementById('
|
|
|
|
|
|
|
|
|
|
| 812 |
if(document.getElementById('tBtn')) document.getElementById('tBtn').classList.add('opacity-50');
|
| 813 |
if(document.getElementById('oBtn')) document.getElementById('oBtn').classList.remove('opacity-50');
|
| 814 |
}
|
| 815 |
|
| 816 |
function goTracking() { activateTrackingUI(); }
|
| 817 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 818 |
|
| 819 |
function takeSnapshot() {
|
| 820 |
-
let feed =
|
| 821 |
-
if(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 822 |
const c = document.createElement('canvas');
|
| 823 |
-
c.width = feed.naturalWidth || feed.
|
|
|
|
| 824 |
c.getContext('2d').drawImage(feed, 0, 0, c.width, c.height);
|
| 825 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
}
|
| 827 |
}
|
| 828 |
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
| 835 |
-
document.getElementById('predictiveFormView').classList.remove('hidden');
|
| 836 |
-
document.getElementById('predictiveResultView').classList.add('hidden');
|
| 837 |
}
|
| 838 |
|
| 839 |
-
|
| 840 |
-
|
| 841 |
-
|
| 842 |
-
|
| 843 |
-
|
| 844 |
-
|
| 845 |
-
const payload = {
|
| 846 |
-
Weather: document.getElementById('tab_weather').value, Road_Type: document.getElementById('tab_road_type').value,
|
| 847 |
-
Time_of_Day: document.getElementById('tab_time').value, Traffic_Density: document.getElementById('tab_density').value,
|
| 848 |
-
Speed_Limit: document.getElementById('tab_speed').value, Number_of_Vehicles: document.getElementById('tab_vehicles').value,
|
| 849 |
-
Driver_Alcohol: document.getElementById('tab_alcohol').value, Road_Condition: document.getElementById('tab_road_cond').value,
|
| 850 |
-
Vehicle_Type: document.getElementById('tab_vehicle').value, Driver_Age: document.getElementById('tab_age').value,
|
| 851 |
-
Driver_Experience: document.getElementById('tab_exp').value, Road_Light_Condition: document.getElementById('tab_light').value
|
| 852 |
-
};
|
| 853 |
-
|
| 854 |
-
try {
|
| 855 |
-
const response = await fetch('/predict_traffic_risk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
| 856 |
-
const data = await response.json();
|
| 857 |
-
if (data.error) throw new Error(data.error);
|
| 858 |
-
|
| 859 |
-
document.getElementById('predictiveFormView').classList.add('hidden');
|
| 860 |
-
document.getElementById('predictiveResultView').classList.remove('hidden');
|
| 861 |
-
|
| 862 |
-
const riskProb = data.risk_probability_percentage;
|
| 863 |
-
const willHappen = data.will_accident_happen;
|
| 864 |
-
|
| 865 |
-
const badge = document.getElementById('riskBadge');
|
| 866 |
-
const icon = document.getElementById('riskIcon');
|
| 867 |
-
document.getElementById('riskLabelText').innerText = data.status;
|
| 868 |
-
|
| 869 |
-
if (willHappen || riskProb >= 50) {
|
| 870 |
-
badge.className = "inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm bg-red-100 text-red-600 border-red-300 dark:bg-red-500/20 dark:text-red-400 dark:border-red-500/30";
|
| 871 |
-
icon.className = "fas fa-exclamation-triangle";
|
| 872 |
-
} else {
|
| 873 |
-
badge.className = "inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm bg-emerald-100 text-emerald-600 border-emerald-300 dark:bg-emerald-500/20 dark:text-emerald-400 dark:border-emerald-500/30";
|
| 874 |
-
icon.className = "fas fa-shield-check";
|
| 875 |
-
}
|
| 876 |
-
animateValue("riskProbabilityValue", 0, Math.floor(riskProb), 1000);
|
| 877 |
-
} catch (err) { alert(`Error: ${err.message}`); resetPredictModal(); }
|
| 878 |
-
finally { btn.disabled = false; spinner.classList.add('hidden'); }
|
| 879 |
}
|
| 880 |
|
| 881 |
async function update() {
|
| 882 |
if(!streamId) return;
|
| 883 |
try {
|
| 884 |
-
const
|
| 885 |
-
const
|
| 886 |
-
if(!
|
| 887 |
-
|
| 888 |
-
document.getElementById('
|
| 889 |
-
document.getElementById('
|
| 890 |
-
document.getElementById('
|
| 891 |
-
document.getElementById('
|
| 892 |
-
document.getElementById('rcnnBar').style.width =
|
| 893 |
-
|
| 894 |
-
document.getElementById('
|
| 895 |
-
|
| 896 |
-
const
|
| 897 |
const statusIcon = document.getElementById('statusIcon');
|
| 898 |
-
const
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
statusBadge.className = "inline-flex items-center gap-2 px-3 py-1.5 rounded-xl text-[10px] font-black mb-4 uppercase tracking-[0.2em] shadow-sm bg-red-100 text-red-600 border border-red-300 dark:bg-red-500/20 dark:text-red-400";
|
| 908 |
-
statusIcon.className = "fas fa-skull-crossbones";
|
| 909 |
-
if(severityLevel) {
|
| 910 |
-
severityLevel.innerText = d.severity;
|
| 911 |
-
severityLevel.className = "font-black text-sm uppercase text-red-600 dark:text-red-300";
|
| 912 |
-
}
|
| 913 |
-
if(d.alert_sent && document.getElementById('emergencyBanner')) document.getElementById('emergencyBanner').classList.remove('hidden');
|
| 914 |
-
} else if(isError) {
|
| 915 |
-
statusBox.className = "ios-glass-inner p-8 rounded-[32px] mb-8 shadow-sm dark:shadow-lg relative overflow-hidden border-l-4 border-orange-500 transition-colors duration-500";
|
| 916 |
-
statusBadge.className = "inline-flex items-center gap-2 px-3 py-1.5 rounded-xl text-[10px] font-black mb-4 uppercase tracking-[0.2em] shadow-sm bg-orange-100 text-orange-600 border border-orange-300 dark:bg-orange-500/20 dark:text-orange-400";
|
| 917 |
-
statusIcon.className = "fas fa-exclamation-triangle";
|
| 918 |
-
if(severityLevel) {
|
| 919 |
-
severityLevel.innerText = "Offline";
|
| 920 |
-
severityLevel.className = "font-black text-[10px] uppercase text-orange-600 dark:text-orange-400";
|
| 921 |
-
}
|
| 922 |
-
} else if(isAnalyzing) {
|
| 923 |
-
statusBox.className = "ios-glass-inner p-8 rounded-[32px] mb-8 shadow-sm dark:shadow-lg relative overflow-hidden border-l-4 border-indigo-500 transition-colors duration-500";
|
| 924 |
-
statusBadge.className = "inline-flex items-center gap-2 px-3 py-1.5 rounded-xl text-[10px] font-black mb-4 uppercase tracking-[0.2em] border border-indigo-200 shadow-sm bg-white text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-300";
|
| 925 |
-
statusIcon.className = "fas fa-spinner fa-spin";
|
| 926 |
-
if(severityLevel) {
|
| 927 |
-
severityLevel.innerText = "Calculating...";
|
| 928 |
-
severityLevel.className = "font-black text-[10px] uppercase text-indigo-500 animate-pulse";
|
| 929 |
-
}
|
| 930 |
-
} else if(isFinished) {
|
| 931 |
-
statusBox.className = "ios-glass-inner p-8 rounded-[32px] mb-8 shadow-sm dark:shadow-lg relative overflow-hidden border-l-4 border-emerald-500 bg-emerald-50/30 transition-colors duration-500";
|
| 932 |
-
statusBadge.className = "inline-flex items-center gap-2 px-3 py-1.5 rounded-xl text-[10px] font-black mb-4 uppercase tracking-[0.2em] border border-emerald-200 shadow-sm bg-emerald-100 text-emerald-600 dark:bg-emerald-500/20 dark:text-emerald-400";
|
| 933 |
-
statusIcon.className = "fas fa-check-double";
|
| 934 |
-
if(severityLevel) {
|
| 935 |
-
severityLevel.innerText = "Clear";
|
| 936 |
-
severityLevel.className = "font-black text-[10px] uppercase text-emerald-600";
|
| 937 |
}
|
| 938 |
} else {
|
| 939 |
-
|
| 940 |
-
|
| 941 |
-
|
| 942 |
-
|
| 943 |
-
if(severityLevel) {
|
| 944 |
-
severityLevel.innerText = "Monitoring...";
|
| 945 |
-
severityLevel.className = "font-black text-[10px] uppercase text-emerald-600 animate-pulse";
|
| 946 |
-
}
|
| 947 |
}
|
| 948 |
|
| 949 |
-
const
|
| 950 |
-
const
|
| 951 |
-
|
| 952 |
-
|
| 953 |
-
|
| 954 |
-
|
| 955 |
-
|
| 956 |
-
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
div.className = "bg-yellow-400 text-black p-2 rounded-xl shadow-sm border border-yellow-500 flex items-center gap-4";
|
| 960 |
-
let img = p.image ? `<img src="data:image/jpeg;base64,${p.image}" class="h-10 rounded-lg border border-black/20 bg-white object-contain shadow-inner">` : "";
|
| 961 |
-
div.innerHTML = `${img} <span class="font-black text-xl tracking-widest px-2">${p.text}</span>`;
|
| 962 |
-
pBox.appendChild(div);
|
| 963 |
-
}
|
| 964 |
-
});
|
| 965 |
}
|
| 966 |
-
if(validPlates) alprSection.classList.remove('hidden');
|
| 967 |
-
else alprSection.classList.add('hidden');
|
| 968 |
-
|
| 969 |
} catch(e) {}
|
| 970 |
}
|
| 971 |
|
| 972 |
function resetApp() {
|
| 973 |
-
if(fileInput) fileInput.value = '';
|
| 974 |
-
if(emergencyBanner) emergencyBanner.classList.add('hidden');
|
| 975 |
-
if(uploadSection) uploadSection.classList.remove('hidden');
|
| 976 |
-
if(resultsDiv) resultsDiv.classList.add('hidden');
|
| 977 |
if(poller) clearInterval(poller);
|
|
|
|
| 978 |
document.getElementById('previewImg').src = "";
|
| 979 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 980 |
}
|
| 981 |
|
| 982 |
-
function
|
| 983 |
-
|
| 984 |
-
const
|
| 985 |
-
const
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
-
const
|
| 990 |
-
|
| 991 |
-
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 996 |
}
|
| 997 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 998 |
}
|
|
|
|
|
|
|
|
|
|
| 999 |
</script>
|
| 1000 |
</body>
|
| 1001 |
</html>
|
|
|
|
| 284 |
<i class="fas fa-eye text-indigo-600 dark:text-purple-300 text-xl"></i>
|
| 285 |
</div>
|
| 286 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Spatial Engine</h4>
|
| 287 |
+
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">YOLOv8 XAI Tracking overlays.</p>
|
| 288 |
</div>
|
| 289 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 290 |
<div class="w-12 h-12 rounded-2xl bg-fuchsia-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-fuchsia-200/50 dark:border-white/10 shadow-sm">
|
|
|
|
| 317 |
|
| 318 |
<!-- Interactive Media Player Area -->
|
| 319 |
<div class="rounded-[32px] overflow-hidden shadow-2xl border border-white/50 dark:border-white/20 relative bg-black aspect-video flex items-center justify-center group">
|
| 320 |
+
<!-- Display Image (AI Overlays) -->
|
| 321 |
<img id="previewImg" src="" class="w-full h-full object-contain z-10 hidden" alt="Detection View">
|
| 322 |
+
<!-- Raw Video Feed Image -->
|
| 323 |
+
<img id="rawFeedImg" src="" class="w-full h-full object-contain z-10 hidden" alt="Raw View">
|
| 324 |
+
|
| 325 |
+
<!-- Snapshot Container Overlay -->
|
| 326 |
+
<div id="snapshotContainer" class="absolute inset-0 z-30 hidden bg-black flex items-center justify-center">
|
| 327 |
+
<img id="snapshotImg" src="" class="w-full h-full object-contain">
|
| 328 |
+
<!-- Fullscreen / Close Controls -->
|
| 329 |
+
<div class="absolute top-6 right-6 flex gap-3">
|
| 330 |
+
<button onclick="fullscreenSnapshot()" class="w-8 h-8 rounded-full bg-white/20 hover:bg-white/40 border border-white/40 text-white flex items-center justify-center transition-all shadow-lg backdrop-blur-md">
|
| 331 |
+
<i class="fas fa-expand"></i>
|
| 332 |
+
</button>
|
| 333 |
+
<button onclick="closeSnapshot()" class="w-8 h-8 rounded-full bg-red-500/80 hover:bg-red-500 border border-red-400 text-white flex items-center justify-center transition-all shadow-lg backdrop-blur-md">
|
| 334 |
+
<i class="fas fa-times"></i>
|
| 335 |
+
</button>
|
| 336 |
+
</div>
|
| 337 |
+
<!-- Snapshot Tag -->
|
| 338 |
+
<div class="absolute top-6 left-6 z-20">
|
| 339 |
+
<span class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 340 |
+
<i class="fas fa-camera text-blue-400"></i> CAPTURED SNAPSHOT
|
| 341 |
+
</span>
|
| 342 |
+
</div>
|
| 343 |
+
</div>
|
| 344 |
|
| 345 |
<!-- Live Tag -->
|
| 346 |
+
<div id="liveBadgeContainer" class="absolute top-6 left-6 z-20">
|
| 347 |
<span id="liveBadge" class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 348 |
+
<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED
|
| 349 |
</span>
|
| 350 |
</div>
|
| 351 |
</div>
|
| 352 |
|
| 353 |
+
<!-- Media Controls -->
|
| 354 |
<div class="flex justify-center gap-4 mt-8" id="mediaControls">
|
| 355 |
<button onclick="takeSnapshot()" class="px-5 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest text-indigo-900 dark:text-white bg-white/50 dark:bg-white/10 border border-indigo-200/50 dark:border-white/20 hover:scale-105 transition-transform shadow-sm flex items-center gap-2">
|
| 356 |
<i class="fas fa-camera"></i> Snapshot
|
|
|
|
| 359 |
<i class="fas fa-brain"></i> AI Tracking
|
| 360 |
</button>
|
| 361 |
<button onclick="goOriginal()" id="oBtn" class="px-5 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest text-indigo-900 dark:text-white bg-white/50 dark:bg-white/10 border border-indigo-200/50 dark:border-white/20 hover:scale-105 transition-transform shadow-sm flex items-center gap-2">
|
| 362 |
+
<i class="fas fa-play"></i> Raw Feed
|
| 363 |
</button>
|
| 364 |
</div>
|
| 365 |
|
|
|
|
| 754 |
if(document.getElementById('vLoc')) document.getElementById('vLoc').innerText = "Not Supported";
|
| 755 |
}
|
| 756 |
const now = new Date();
|
| 757 |
+
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = now.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
|
| 758 |
}
|
| 759 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 760 |
let streamId = null;
|
| 761 |
let poller = null;
|
| 762 |
|
| 763 |
+
function openIpCamModal() { document.getElementById('ipCamModal').classList.remove('hidden'); }
|
| 764 |
+
function closeIpCamModal() { document.getElementById('ipCamModal').classList.add('hidden'); }
|
| 765 |
+
|
| 766 |
+
function openPredictModal() {
|
| 767 |
+
document.getElementById('predictiveModal').classList.remove('hidden');
|
| 768 |
+
document.body.style.overflow = 'hidden';
|
| 769 |
+
}
|
| 770 |
+
function closePredictModal() {
|
| 771 |
+
document.getElementById('predictiveModal').classList.add('hidden');
|
| 772 |
+
document.body.style.overflow = '';
|
| 773 |
+
}
|
| 774 |
+
function resetPredictModal() {
|
| 775 |
+
document.getElementById('predictiveResultView').classList.add('hidden');
|
| 776 |
+
document.getElementById('predictiveFormView').classList.remove('hidden');
|
| 777 |
+
}
|
| 778 |
|
| 779 |
async function connectIpCam() {
|
| 780 |
+
const url = document.getElementById('ipCamUrl').value;
|
| 781 |
if(!url) return;
|
| 782 |
closeIpCamModal();
|
| 783 |
+
startSession({url: url, location: currentLocationStr}, '/init_stream');
|
| 784 |
}
|
| 785 |
|
| 786 |
+
document.getElementById('fileInput').addEventListener('change', async (e) => {
|
| 787 |
+
if(!e.target.files.length) return;
|
|
|
|
|
|
|
|
|
|
| 788 |
const fd = new FormData();
|
| 789 |
+
fd.append('file', e.target.files[0]);
|
| 790 |
+
startSession(fd, '/init_upload', true);
|
| 791 |
+
});
|
| 792 |
+
|
| 793 |
+
async function startSession(payload, endpoint, isFormData=false) {
|
| 794 |
+
document.getElementById('uploadSection').classList.add('hidden');
|
| 795 |
+
document.getElementById('results').classList.remove('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
|
| 797 |
+
document.getElementById('labelText').innerText = "Initializing Stream...";
|
| 798 |
+
|
| 799 |
+
const opts = { method: 'POST' };
|
| 800 |
+
if(isFormData) { opts.body = payload; }
|
| 801 |
+
else { opts.headers = {'Content-Type': 'application/json'}; opts.body = JSON.stringify(payload); }
|
| 802 |
+
|
| 803 |
+
const res = await fetch(endpoint, opts);
|
| 804 |
const data = await res.json();
|
| 805 |
streamId = data.stream_id;
|
| 806 |
|
| 807 |
+
if(data.location && data.location !== "Nil" && document.getElementById('vLoc')) {
|
| 808 |
+
document.getElementById('vLoc').innerText = data.location;
|
|
|
|
|
|
|
| 809 |
} else {
|
| 810 |
+
fetchRealLocationData(); // fallback
|
|
|
|
|
|
|
| 811 |
}
|
| 812 |
+
|
| 813 |
+
if(data.weather && data.weather !== "Nil" && document.getElementById('vWx')) {
|
| 814 |
+
document.getElementById('vWx').innerText = data.weather;
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
activateTrackingUI();
|
| 818 |
|
|
|
|
| 819 |
const locText = document.getElementById('vLoc') ? document.getElementById('vLoc').innerText : "Unknown";
|
| 820 |
document.getElementById('previewImg').src = `/video_feed/${streamId}?loc=${encodeURIComponent(locText)}&t=${Date.now()}`;
|
| 821 |
+
document.getElementById('rawFeedImg').src = `/raw_feed/${streamId}?loc=${encodeURIComponent(locText)}&t=${Date.now()}`;
|
| 822 |
|
| 823 |
if(poller) clearInterval(poller);
|
| 824 |
poller = setInterval(update, 1000);
|
| 825 |
}
|
| 826 |
|
| 827 |
function activateTrackingUI() {
|
| 828 |
+
document.getElementById('previewImg').classList.remove('hidden');
|
| 829 |
+
document.getElementById('rawFeedImg').classList.add('hidden');
|
| 830 |
+
document.getElementById('snapshotContainer').classList.add('hidden');
|
| 831 |
+
document.getElementById('liveBadgeContainer').classList.remove('hidden');
|
| 832 |
+
|
| 833 |
+
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED';
|
| 834 |
if(document.getElementById('tBtn')) document.getElementById('tBtn').classList.add('opacity-50');
|
| 835 |
if(document.getElementById('oBtn')) document.getElementById('oBtn').classList.remove('opacity-50');
|
| 836 |
}
|
| 837 |
|
| 838 |
function goTracking() { activateTrackingUI(); }
|
| 839 |
+
|
| 840 |
+
function goOriginal() {
|
| 841 |
+
document.getElementById('previewImg').classList.add('hidden');
|
| 842 |
+
document.getElementById('rawFeedImg').classList.remove('hidden');
|
| 843 |
+
document.getElementById('snapshotContainer').classList.add('hidden');
|
| 844 |
+
document.getElementById('liveBadgeContainer').classList.remove('hidden');
|
| 845 |
+
|
| 846 |
+
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-blue-500 shadow-[0_0_8px_blue]"></span> RAW FEED';
|
| 847 |
+
if(document.getElementById('oBtn')) document.getElementById('oBtn').classList.add('opacity-50');
|
| 848 |
+
if(document.getElementById('tBtn')) document.getElementById('tBtn').classList.remove('opacity-50');
|
| 849 |
+
}
|
| 850 |
|
| 851 |
function takeSnapshot() {
|
| 852 |
+
let feed = null;
|
| 853 |
+
if(!document.getElementById('previewImg').classList.contains('hidden')) {
|
| 854 |
+
feed = document.getElementById('previewImg');
|
| 855 |
+
} else if (!document.getElementById('rawFeedImg').classList.contains('hidden')) {
|
| 856 |
+
feed = document.getElementById('rawFeedImg');
|
| 857 |
+
}
|
| 858 |
+
|
| 859 |
+
if(feed && feed.src) {
|
| 860 |
const c = document.createElement('canvas');
|
| 861 |
+
c.width = feed.naturalWidth || feed.width || 640;
|
| 862 |
+
c.height = feed.naturalHeight || feed.height || 480;
|
| 863 |
c.getContext('2d').drawImage(feed, 0, 0, c.width, c.height);
|
| 864 |
+
|
| 865 |
+
document.getElementById('snapshotImg').src = c.toDataURL('image/jpeg');
|
| 866 |
+
|
| 867 |
+
document.getElementById('snapshotContainer').classList.remove('hidden');
|
| 868 |
+
document.getElementById('previewImg').classList.add('hidden');
|
| 869 |
+
document.getElementById('rawFeedImg').classList.add('hidden');
|
| 870 |
+
document.getElementById('liveBadgeContainer').classList.add('hidden');
|
| 871 |
}
|
| 872 |
}
|
| 873 |
|
| 874 |
+
function closeSnapshot() {
|
| 875 |
+
if (document.getElementById('tBtn').classList.contains('opacity-50')) {
|
| 876 |
+
goTracking();
|
| 877 |
+
} else {
|
| 878 |
+
goOriginal();
|
| 879 |
+
}
|
|
|
|
|
|
|
| 880 |
}
|
| 881 |
|
| 882 |
+
function fullscreenSnapshot() {
|
| 883 |
+
const imgSrc = document.getElementById('snapshotImg').src;
|
| 884 |
+
if(imgSrc) {
|
| 885 |
+
const w = window.open('');
|
| 886 |
+
w.document.write(`<html><body style="margin:0;background:#000;display:flex;justify-content:center;align-items:center;height:100vh;"><img src="${imgSrc}" style="max-width:100%;max-height:100%;object-fit:contain;"></body></html>`);
|
| 887 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 888 |
}
|
| 889 |
|
| 890 |
async function update() {
|
| 891 |
if(!streamId) return;
|
| 892 |
try {
|
| 893 |
+
const res = await fetch(`/stream_status/${streamId}`);
|
| 894 |
+
const data = await res.json();
|
| 895 |
+
if(!data || Object.keys(data).length === 0) return;
|
| 896 |
+
|
| 897 |
+
document.getElementById('labelText').innerText = data.label || "Monitoring...";
|
| 898 |
+
document.getElementById('confVal').innerText = data.confidence || 0;
|
| 899 |
+
document.getElementById('cnnBar').style.width = `${data.cnn || 0}%`;
|
| 900 |
+
document.getElementById('cnnVal').innerText = `${data.cnn || 0}%`;
|
| 901 |
+
document.getElementById('rcnnBar').style.width = `${data.rcnn || 0}%`;
|
| 902 |
+
document.getElementById('rcnnVal').innerText = `${data.rcnn || 0}%`;
|
| 903 |
+
document.getElementById('severityLevel').innerText = data.severity || "--";
|
| 904 |
+
|
| 905 |
+
const statusCard = document.getElementById('statusCard');
|
| 906 |
const statusIcon = document.getElementById('statusIcon');
|
| 907 |
+
const emergencyBanner = document.getElementById('emergencyBanner');
|
| 908 |
+
|
| 909 |
+
if(data.is_accident) {
|
| 910 |
+
statusCard.classList.remove('border-indigo-500');
|
| 911 |
+
statusCard.classList.add('border-rose-500', 'bg-rose-50/50', 'dark:bg-rose-900/20');
|
| 912 |
+
statusIcon.className = "fas fa-exclamation-triangle text-rose-600";
|
| 913 |
+
|
| 914 |
+
if(data.severity && (data.severity.toLowerCase() === 'major' || data.severity.toLowerCase() === 'moderate')) {
|
| 915 |
+
emergencyBanner.classList.remove('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 916 |
}
|
| 917 |
} else {
|
| 918 |
+
statusCard.classList.add('border-indigo-500');
|
| 919 |
+
statusCard.classList.remove('border-rose-500', 'bg-rose-50/50', 'dark:bg-rose-900/20');
|
| 920 |
+
statusIcon.className = "fas fa-shield-check text-indigo-600";
|
| 921 |
+
emergencyBanner.classList.add('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 922 |
}
|
| 923 |
|
| 924 |
+
const alprSec = document.getElementById('alprSection');
|
| 925 |
+
const alprCont = document.getElementById('alprContainer');
|
| 926 |
+
if(data.plates && data.plates.length > 0) {
|
| 927 |
+
alprSec.classList.remove('hidden');
|
| 928 |
+
alprCont.innerHTML = data.plates.map(p => `
|
| 929 |
+
<div class="flex items-center gap-2 bg-white/80 dark:bg-black/40 px-3 py-1.5 rounded-lg border border-indigo-100 dark:border-white/10 shadow-sm">
|
| 930 |
+
<img src="data:image/jpeg;base64,${p.image}" class="h-6 rounded object-cover">
|
| 931 |
+
<span class="font-mono font-bold text-xs text-indigo-900 dark:text-white">${p.text}</span>
|
| 932 |
+
</div>
|
| 933 |
+
`).join('');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
}
|
|
|
|
|
|
|
|
|
|
| 935 |
} catch(e) {}
|
| 936 |
}
|
| 937 |
|
| 938 |
function resetApp() {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 939 |
if(poller) clearInterval(poller);
|
| 940 |
+
streamId = null;
|
| 941 |
document.getElementById('previewImg').src = "";
|
| 942 |
+
document.getElementById('rawFeedImg').src = "";
|
| 943 |
+
document.getElementById('uploadSection').classList.remove('hidden');
|
| 944 |
+
document.getElementById('results').classList.add('hidden');
|
| 945 |
+
document.getElementById('emergencyBanner').classList.add('hidden');
|
| 946 |
+
document.getElementById('fileInput').value = "";
|
| 947 |
}
|
| 948 |
|
| 949 |
+
async function submitPredictiveData(e) {
|
| 950 |
+
e.preventDefault();
|
| 951 |
+
const btn = document.getElementById('predictBtn');
|
| 952 |
+
const spinner = document.getElementById('predictSpinner');
|
| 953 |
+
btn.disabled = true;
|
| 954 |
+
spinner.classList.remove('hidden');
|
| 955 |
+
|
| 956 |
+
const payload = {
|
| 957 |
+
Weather_Conditions: document.getElementById('tab_weather').value,
|
| 958 |
+
Light_Conditions: document.getElementById('tab_light').value,
|
| 959 |
+
Time_of_Day: document.getElementById('tab_time').value,
|
| 960 |
+
Road_Type: document.getElementById('tab_road_type').value,
|
| 961 |
+
Road_Surface_Conditions: document.getElementById('tab_road_cond').value,
|
| 962 |
+
Speed_Limit: document.getElementById('tab_speed').value,
|
| 963 |
+
Traffic_Density: document.getElementById('tab_density').value,
|
| 964 |
+
Number_of_Vehicles: document.getElementById('tab_vehicles').value,
|
| 965 |
+
Vehicle_Type: document.getElementById('tab_vehicle').value,
|
| 966 |
+
Driver_Age: document.getElementById('tab_age').value,
|
| 967 |
+
Driver_Experience: document.getElementById('tab_exp').value,
|
| 968 |
+
Driver_Alcohol: document.getElementById('tab_alcohol').value
|
| 969 |
+
};
|
| 970 |
+
|
| 971 |
+
try {
|
| 972 |
+
const res = await fetch('/predict_traffic_risk', {
|
| 973 |
+
method: 'POST',
|
| 974 |
+
headers: {'Content-Type': 'application/json'},
|
| 975 |
+
body: JSON.stringify(payload)
|
| 976 |
+
});
|
| 977 |
+
const data = await res.json();
|
| 978 |
+
|
| 979 |
+
document.getElementById('predictiveFormView').classList.add('hidden');
|
| 980 |
+
document.getElementById('predictiveResultView').classList.remove('hidden');
|
| 981 |
+
|
| 982 |
+
const prob = data.risk_probability_percentage || 0;
|
| 983 |
+
document.getElementById('riskProbabilityValue').innerText = prob;
|
| 984 |
+
|
| 985 |
+
const riskCard = document.getElementById('riskStatusCard');
|
| 986 |
+
const riskBadge = document.getElementById('riskBadge');
|
| 987 |
+
const riskIcon = document.getElementById('riskIcon');
|
| 988 |
+
const riskLabel = document.getElementById('riskLabelText');
|
| 989 |
+
|
| 990 |
+
riskCard.className = 'ios-glass-inner p-10 rounded-[32px] shadow-sm dark:shadow-lg relative overflow-hidden w-full max-w-sm border';
|
| 991 |
+
riskBadge.className = 'inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm text-white';
|
| 992 |
+
|
| 993 |
+
if(prob >= 70) {
|
| 994 |
+
riskCard.classList.add('border-red-500', 'bg-red-50/10', 'dark:bg-red-900/20');
|
| 995 |
+
riskBadge.classList.add('bg-red-500', 'border-red-400');
|
| 996 |
+
riskIcon.className = 'fas fa-exclamation-triangle';
|
| 997 |
+
riskLabel.innerText = 'Critical Risk';
|
| 998 |
+
} else if(prob >= 40) {
|
| 999 |
+
riskCard.classList.add('border-amber-500', 'bg-amber-50/10', 'dark:bg-amber-900/20');
|
| 1000 |
+
riskBadge.classList.add('bg-amber-500', 'border-amber-400');
|
| 1001 |
+
riskIcon.className = 'fas fa-exclamation-circle';
|
| 1002 |
+
riskLabel.innerText = 'Elevated Risk';
|
| 1003 |
+
} else {
|
| 1004 |
+
riskCard.classList.add('border-green-500', 'bg-green-50/10', 'dark:bg-green-900/20');
|
| 1005 |
+
riskBadge.classList.add('bg-green-500', 'border-green-400');
|
| 1006 |
+
riskIcon.className = 'fas fa-shield-check';
|
| 1007 |
+
riskLabel.innerText = 'Low Risk';
|
| 1008 |
}
|
| 1009 |
+
|
| 1010 |
+
} catch (e) {
|
| 1011 |
+
alert("Error running prediction.");
|
| 1012 |
+
} finally {
|
| 1013 |
+
btn.disabled = false;
|
| 1014 |
+
spinner.classList.add('hidden');
|
| 1015 |
+
}
|
| 1016 |
}
|
| 1017 |
+
|
| 1018 |
+
// Setup script initialization call
|
| 1019 |
+
fetchRealLocationData();
|
| 1020 |
</script>
|
| 1021 |
</body>
|
| 1022 |
</html>
|