
Vision Uncertainty Quantifier
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vuq - Vision Uncertainty Quantifier | Quinsim-Vision</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: #0f1115;
color: #f9fafb;
overflow: hidden;
}
/* Header */
.header {
background: #1a1d24;
border-bottom: 1px solid #374151;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
height: 73px;
}
.header-left {
display: flex;
align-items: center;
gap: 32px;
}
.logo {
font-size: 20px;
font-weight: 700;
background: linear-gradient(to right, #3b82f6, #8b5cf6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.breadcrumb {
color: #9ca3af;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
}
.breadcrumb .active {
color: #f9fafb;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.icon-btn {
background: none;
border: none;
color: #9ca3af;
cursor: pointer;
padding: 8px;
transition: color 0.2s;
}
.icon-btn:hover {
color: #f9fafb;
}
.btn-secondary {
background: #252932;
border: none;
color: #f9fafb;
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.btn-secondary:hover {
background: #2d323d;
}
/* Main Layout */
.main-container {
display: flex;
height: calc(100vh - 73px);
}
/* Left Panel */
.left-panel {
flex: 1;
padding: 24px;
display: flex;
flex-direction: column;
}
.image-container {
background: #1a1d24;
border: 1px solid #374151;
border-radius: 8px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.image-display {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 16px;
}
.upload-zone {
text-align: center;
}
.upload-icon {
width: 64px;
height: 64px;
background: #252932;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
}
.upload-icon svg {
width: 32px;
height: 32px;
stroke: #3b82f6;
}
.upload-zone h3 {
font-size: 18px;
margin-bottom: 8px;
}
.upload-zone p {
color: #9ca3af;
font-size: 14px;
margin-bottom: 16px;
}
.btn-primary {
background: #3b82f6;
border: none;
color: #f9fafb;
padding: 10px 24px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-primary:hover {
background: #2563eb;
}
.btn-primary:disabled {
background: #374151;
cursor: not-allowed;
}
.upload-hint {
color: #9ca3af;
font-size: 12px;
margin-top: 16px;
}
#uploadInput {
display: none;
}
#uploadedImage {
max-width: 100%;
max-height: 100%;
object-fit: contain;
display: none;
}
.uncertainty-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
display: none;
background:
radial-gradient(circle at 30% 40%, rgba(239, 68, 68, 0.4) 0%, transparent 40%),
radial-gradient(circle at 70% 60%, rgba(239, 68, 68, 0.3) 0%, transparent 35%),
radial-gradient(circle at 50% 80%, rgba(239, 68, 68, 0.2) 0%, transparent 30%);
mix-blend-mode: multiply;
}
.image-controls {
background: #252932;
padding: 12px 16px;
border-top: 1px solid #374151;
display: none;
justify-content: space-between;
align-items: center;
}
.control-btn {
background: none;
border: none;
color: #9ca3af;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: color 0.2s;
}
.control-btn:hover {
color: #f9fafb;
}
.control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Right Sidebar */
.right-sidebar {
width: 420px;
background: #1a1d24;
border-left: 1px solid #374151;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-section {
padding: 24px;
border-bottom: 1px solid #374151;
}
.sidebar-section h2 {
font-size: 18px;
font-weight: 600;
margin-bottom: 16px;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
color: #9ca3af;
font-size: 14px;
margin-bottom: 8px;
}
select, input[type="range"] {
width: 100%;
}
select {
background: #252932;
border: 1px solid #374151;
color: #f9fafb;
padding: 10px 12px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: border-color 0.2s;
}
select:focus {
outline: none;
border-color: #3b82f6;
}
input[type="range"] {
height: 6px;
background: #374151;
border-radius: 3px;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #3b82f6;
border-radius: 50%;
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
width: 16px;
height: 16px;
background: #3b82f6;
border-radius: 50%;
cursor: pointer;
border: none;
}
.radio-group {
display: flex;
gap: 16px;
}
.radio-label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
}
input[type="radio"] {
accent-color: #3b82f6;
cursor: pointer;
}
.btn-analyze {
width: 100%;
background: #3b82f6;
border: none;
color: #f9fafb;
padding: 14px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: background 0.2s;
margin-top: 8px;
}
.btn-analyze:hover:not(:disabled) {
background: #2563eb;
}
.btn-analyze:disabled {
background: #374151;
cursor: not-allowed;
}
.spinner {
width: 16px;
height: 16px;
border: 2px solid #f9fafb;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Results Section */
.results-section {
padding: 24px;
display: none;
flex: 1;
}
.results-section.show {
display: block;
}
.result-card {
background: #252932;
border-radius: 8px;
padding: 24px;
margin-bottom: 24px;
}
.trust-score-card {
text-align: center;
}
.trust-score-label {
color: #9ca3af;
font-size: 14px;
margin-bottom: 12px;
}
.trust-score-gauge {
width: 128px;
height: 128px;
margin: 0 auto 12px;
position: relative;
}
.trust-score-value {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 32px;
font-weight: 700;
}
.trust-score-status {
color: #9ca3af;
font-size: 12px;
}
.result-card h3 {
color: #9ca3af;
font-size: 14px;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.histogram {
height: 128px;
display: flex;
align-items: flex-end;
gap: 2px;
margin-bottom: 8px;
}
.histogram-bar {
flex: 1;
background: linear-gradient(to top, #ef4444, #f87171);
border-radius: 2px 2px 0 0;
}
.histogram-labels {
display: flex;
justify-content: space-between;
color: #9ca3af;
font-size: 12px;
}
.alert-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
background: #1a1d24;
border-radius: 6px;
margin-bottom: 8px;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.alert-item:hover {
background: #252932;
}
.alert-confidence {
color: #ef4444;
}
.error-bar-item {
margin-bottom: 12px;
}
.error-bar-header {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
font-size: 12px;
}
.error-bar-uncertainty {
color: #9ca3af;
}
.error-bar-track {
height: 4px;
background: #1a1d24;
border-radius: 2px;
overflow: hidden;
}
.error-bar-fill {
height: 100%;
background: #3b82f6;
border-radius: 2px;
}
.action-buttons {
display: flex;
flex-direction: column;
gap: 12px;
}
.btn-action {
width: 100%;
background: #252932;
border: none;
color: #f9fafb;
padding: 12px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: background 0.2s;
}
.btn-action:hover {
background: #2d323d;
}
.empty-state {
padding: 60px 24px;
text-align: center;
color: #9ca3af;
}
.empty-state p {
font-size: 14px;
line-height: 1.6;
}
.alert-icon {
color: #f59e0b;
}
</style>
```
</head>
<body>
<!-- Header -->
<header class="header">
<div class="header-left">
<div class="logo">Quinsim-Vision</div>
<div class="breadcrumb">
<span class="active">Vuq</span>
<span>|</span>
<span>Vision Uncertainty Quantifier</span>
</div>
</div>
<div class="header-right">
<button class="icon-btn" title="Help">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</button>
<button class="btn-secondary">Back to Dashboard</button>
</div>
</header>
<!-- Main Container -->
<div class="main-container">
<!-- Left Panel -->
<div class="left-panel">
<div class="image-container">
<div class="image-display" id="imageDisplay">
<!-- Upload Zone -->
<div class="upload-zone" id="uploadZone">
<div class="upload-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
</div>
<h3>Upload an image</h3>
<p>Drag and drop or click to browse</p>
<button class="btn-primary" onclick="document.getElementById('uploadInput').click()">
Choose File
</button>
<input type="file" id="uploadInput" accept="image" onchange="handleImageUpload(event)">
<p class="upload-hint">Supports: JPG, PNG, BMP</p>
</div>
<!-- Uploaded Image -->
<img id="uploadedImage" alt="Uploaded">
<div class="uncertainty-overlay" id="uncertaintyOverlay"></div>
</div>
<!-- Image Controls -->
<div class="image-controls" id="imageControls">
<button class="control-btn" id="toggleOverlay" onclick="toggleOverlay()" disabled>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path id="eyeIcon" d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<span id="overlayText">Hide Uncertainty Overlay</span>
</button>
<button class="control-btn" onclick="resetUpload()">
Upload New Image
</button>
</div>
</div>
</div>
<!-- Right Sidebar -->
<div class="right-sidebar">
<!-- Parameters Section -->
<div class="sidebar-section">
<h2>Parameters</h2>
<div class="form-group">
<label class="form-label">Uncertainty Method</label>
<select id="method">
<option value="mc-dropout">MC-Dropout</option>
<option value="deep-ensembles">Deep Ensembles</option>
<option value="bayesian-layers">Bayesian Layers</option>
<option value="surrogate-uq">Surrogate UQ</option>
</select>
</div>
<div class="form-group">
<label class="form-label">
Confidence Threshold: <span id="thresholdValue">0.50</span>
</label>
<input type="range" id="threshold" min="0" max="1" step="0.01" value="0.5" oninput="updateThreshold(this.value)">
</div>
<div class="form-group">
<label class="form-label">Analysis Level</label>
<div class="radio-group">
<label class="radio-label">
<input type="radio" name="level" value="pixel" checked>
<span>Pixel-level</span>
</label>
<label class="radio-label">
<input type="radio" name="level" value="object">
<span>Object-level</span>
</label>
</div>
</div>
<button class="btn-analyze" id="analyzeBtn" onclick="runAnalysis()" disabled>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="5 3 19 12 5 21 5 3"></polygon>
</svg>
<span id="analyzeBtnText">Run Analysis</span>
</button>
</div>
<!-- Results Section -->
<div class="results-section" id="resultsSection">
<h2>Results</h2>
<!-- Trust Score -->
<div class="result-card trust-score-card">
<p class="trust-score-label">Trust Score</p>
<div class="trust-score-gauge">
<svg width="128" height="128" style="transform: rotate(-90deg)">
<circle cx="64" cy="64" r="56" fill="none" stroke="#374151" stroke-width="8"></circle>
<circle id="trustScoreCircle" cx="64" cy="64" r="56" fill="none" stroke="#10b981" stroke-width="8" stroke-dasharray="0 352" stroke-linecap="round"></circle>
</svg>
<div class="trust-score-value" id="trustScoreValue">72</div>
</div>
<p class="trust-score-status" id="trustScoreStatus">High Confidence</p>
</div>
<!-- Uncertainty Distribution -->
<div class="result-card">
<h3>Uncertainty Distribution</h3>
<div class="histogram" id="histogram"></div>
<div class="histogram-labels">
<span>Low</span>
<span>Uncertainty Level</span>
<span>High</span>
</div>
</div>
<!-- Low-Confidence Alerts -->
<div class="result-card">
<h3>
<svg class="alert-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
Low-Confidence Detections
</h3>
<div id="alertsList"></div>
</div>
<!-- Error Bars -->
<div class="result-card" id="errorBarsCard">
<h3>Error Bars Per Object</h3>
<div id="errorBarsList"></div>
</div>
<!-- Action Buttons -->
<div class="action-buttons">
<button class="btn-action">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Download Report (PDF)
</button>
<button class="btn-action">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="12" y1="18" x2="12" y2="12"></line>
<line x1="9" y1="15" x2="15" y2="15"></line>
</svg>
Export Data (JSON)
</button>
</div>
</div>
<!-- Empty State -->
<div class="empty-state" id="emptyState">
<p>Configure parameters and run analysis<br>to see results here</p>
</div>
</div>
</div>
<script>
let uploadedImageData = null;
let overlayVisible = true;
let analysisComplete = false;
function handleImageUpload(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
uploadedImageData = e.target.result;
displayImage();
};
reader.readAsDataURL(file);
}
}
function displayImage() {
document.getElementById('uploadZone').style.display = 'none';
document.getElementById('uploadedImage').src = uploadedImageData;
document.getElementById('uploadedImage').style.display = 'block';
document.getElementById('imageControls').style.display = 'flex';
document.getElementById('analyzeBtn').disabled = false;
// Hide results
document.getElementById('resultsSection').classList.remove('show');
document.getElementById('emptyState').style.display = 'block';
document.getElementById('uncertaintyOverlay').style.display = 'none';
document.getElementById('toggleOverlay').disabled = true;
analysisComplete = false;
}
function resetUpload() {
uploadedImageData = null;
document.getElementById('uploadZone').style.display = 'block';
document.getElementById('uploadedImage').style.display = 'none';
document.getElementById('imageControls').style.display = 'none';
document.getElementById('analyzeBtn').disabled = true;
document.getElementById('resultsSection').classList.remove('show');
document.getElementById('emptyState').style.display = 'block';
document.getElementById('uncertaintyOverlay').style.display = 'none';
document.getElementById('uploadInput').value = '';
analysisComplete = false;
}
function updateThreshold(value) {
document.getElementById('thresholdValue').textContent = parseFloat(value).toFixed(2);
}
function toggleOverlay() {
overlayVisible = !overlayVisible;
const overlay = document.getElementById('uncertaintyOverlay');
const icon = document.getElementById('eyeIcon');
const text = document.getElementById('overlayText');
if (overlayVisible) {
overlay.style.display = 'block';
text.textContent = 'Hide Uncertainty Overlay';
icon.setAttribute('d', 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z');
} else {
overlay.style.display = 'none';
text.textContent = 'Show Uncertainty Overlay';
icon.setAttribute('d', 'M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24');
}
}
function runAnalysis() {
const btn = document.getElementById('analyzeBtn');
const btnText = document.getElementById('analyzeBtnText');
// Show loading state
btn.disabled = true;
btnText.textContent = 'Analyzing...';
btn.innerHTML = '<div class="spinner"></div><span>Analyzing...</span>';
// Simulate API call
setTimeout(() => {
// Generate random results
const trustScore = Math.floor(Math.random() * 30) + 60;
const uncertaintyData = Array.from({ length: 20 }, () => Math.random() * 100);
// Update trust score
updateTrustScore(trustScore);
// Update histogram
updateHistogram(uncertaintyData);
// Update alerts
updateAlerts([
{ id: 1, object: 'Vehicle', confidence: 0.42 },
{ id: 2, object: 'Pedestrian', confidence: 0.38 },
{ id: 3, object: 'Sign', confidence: 0.35 }
]);
// Update error bars
const level = document.querySelector('input[name="level"]:checked').value;
if (level === 'object') {
updateErrorBars([
{ id: 1, class: 'Vehicle', confidence: 0.85, uncertainty: 0.12 },
{ id: 2, class: 'Pedestrian', confidence: 0.78, uncertainty: 0.18 },
{ id: 3, class: 'Road', confidence: 0.92, uncertainty: 0.06 },
{ id: 4, class: 'Sign', confidence: 0.71, uncertainty: 0.15 }
]);
} else {
document.getElementById('errorBarsCard').style.display = 'none';
}
// Show results
document.getElementById('resultsSection').classList.add('show');
document.getElementById('emptyState').style.display = 'none';
document.getElementById('uncertaintyOverlay').style.display = 'block';
document.getElementById('toggleOverlay').disabled = false;
analysisComplete = true;
// Reset button
btn.disabled = false;
btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg><span>Run Analysis</span>';
}, 2000);
}
function updateTrustScore(score) {
const circle = document.getElementById('trustScoreCircle');
const value = document.getElementById('trustScoreValue');
const status = document.getElementById('trustScoreStatus');
const circumference = 2 * Math.PI * 56;
const offset = circumference - (score / 100) * circumference;
let color, statusText;
if (score >= 70) {
color = '#10b981';
statusText = 'High Confidence';
} else if (score >= 40) {
color = '#f59e0b';
statusText = 'Moderate Confidence';
} else {
color = '#ef4444';
statusText = 'Low Confidence';
}
circle.setAttribute('stroke', color);
circle.setAttribute('stroke-dasharray', `${circumference - offset} ${circumference}`);
circle.style.filter = `drop-shadow(0 0 8px ${color}40)`;
value.textContent = score;
status.textContent = statusText;
}
