Spaces:
Sleeping
Sleeping
File size: 14,828 Bytes
da4d2e2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ISL Smart Glove Debugger</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #f4f4f9; color: #333; margin: 0; padding: 20px; display: flex; flex-direction: column; align-items: center; }
.container { max-width: 950px; width: 100%; display: flex; gap: 20px; }
.panel { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); flex: 1; }
h1 { margin-top: 0; color: #222; font-size: 1.5em; width: 100%; max-width: 950px; border-bottom: 2px solid #ddd; padding-bottom: 10px; margin-bottom: 20px; }
h2 { margin-top: 0; color: #444; font-size: 1.2em; border-bottom: 1px solid #eee; padding-bottom: 10px; }
.sensor-list { list-style: none; padding: 0; margin: 0; max-height: 500px; overflow-y: auto; padding-right: 10px; }
.sensor-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f9f9f9; }
.sensor-item label { width: 100px; font-weight: bold; font-size: 0.85em; color: #555; }
.sensor-item input[type=range] { flex: 1; margin: 0 10px; }
.sensor-item input[type=number] { width: 70px; text-align: right; font-family: monospace; font-size: 0.9em; background: #eee; padding: 2px 5px; border: 1px solid #ccc; border-radius: 3px; }
.array-view { background: #1e1e1e; color: #4af626; padding: 15px; border-radius: 6px; font-family: monospace; font-size: 0.9em; width: 100%; height: 100px; resize: none; border: none; outline: none; margin-bottom: 20px; box-shadow: inset 0 2px 4px rgba(0,0,0,0.3); }
.btn { background: #007bff; color: white; border: none; padding: 12px 15px; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 1em; width: 100%; margin-bottom: 10px; transition: background 0.2s; }
.btn:hover { background: #0056b3; }
.btn-secondary { background: #6c757d; }
.btn-secondary:hover { background: #5a6268; }
.btn-danger { background: #dc3545; }
.btn-danger:hover { background: #c82333; }
.result-box { text-align: center; margin-top: 30px; border-top: 2px dashed #eee; padding-top: 20px; }
.result-box h3 { margin: 0; font-size: 0.9em; color: #888; text-transform: uppercase; letter-spacing: 1px; }
.word { font-size: 3em; font-weight: bold; color: #28a745; margin: 10px 0; }
.buffer { background: #e9ecef; padding: 15px; border-radius: 6px; min-height: 24px; font-size: 1.1em; color: #333; margin-top: 10px; border: 1px solid #ddd; }
</style>
</head>
<body>
<h1>ISL Smart Glove Debugger</h1>
<div class="container">
<div class="panel">
<h2>Sensor List Controls</h2>
<ul class="sensor-list" id="sensorList"></ul>
</div>
<div class="panel">
<h2>Raw Features Array (Edit/Paste)</h2>
<textarea class="array-view" id="arrayView" oninput="updateFromText(this.value)">[0.0, 0.0, ...]</textarea>
<button class="btn" onclick="makePrediction()">Send to API / Predict</button>
<button class="btn btn-secondary" onclick="loadExampleData()">Generate Random Valid Gesture</button>
<button class="btn btn-danger" onclick="resetData()">Reset to Zeros</button>
<div class="result-box">
<h3>Predicted Gesture</h3>
<div class="word" id="predictedWord">--</div>
<h3 style="margin-top: 20px;">Sentence Buffer</h3>
<div class="buffer" id="sentenceBuffer">...</div>
<button class="btn btn-danger" style="margin-top: 10px;" onclick="clearBuffer()">Clear Sentence</button>
</div>
</div>
</div>
<script>
const featureNames = [
"Flex 1", "Flex 2", "Flex 3", "Flex 4", "Flex 5",
"Flex 6", "Flex 7", "Flex 8", "Flex 9", "Flex 10",
"MPU1 Ac_X", "MPU1 Ac_Y", "MPU1 Ac_Z",
"MPU1 Gy_X", "MPU1 Gy_Y", "MPU1 Gy_Z",
"MPU2 Ac_X", "MPU2 Ac_Y", "MPU2 Ac_Z",
"MPU2 Gy_X", "MPU2 Gy_Y", "MPU2 Gy_Z"
];
let sensors = new Array(22).fill(0.0);
let wordBuffer = [];
const listEl = document.getElementById('sensorList');
const arrayView = document.getElementById('arrayView');
function renderList() {
listEl.innerHTML = '';
featureNames.forEach((name, i) => {
const li = document.createElement('li');
li.className = 'sensor-item';
// Flex sensors typically map from 0 to 90 degrees or arbitrary voltage.
// MPUs map from -2g to +2g or similar. We use -30 to +30 as a general sandbox.
li.innerHTML = `
<label>${name}</label>
<input type="range" id="slider${i}" min="-30" max="30" step="0.01" value="${sensors[i]}" oninput="updateSensor(${i}, this.value, 'slider')">
<input type="number" id="val${i}" step="0.01" value="${sensors[i].toFixed(2)}" oninput="updateSensor(${i}, this.value, 'number')">
`;
listEl.appendChild(li);
});
updateArrayView();
}
function updateSensor(i, val, source) {
sensors[i] = parseFloat(val) || 0.0;
// Only update the UI elements that AREN'T the source of the change
if (source !== 'slider') {
const slider = document.getElementById(`slider${i}`);
if (slider) slider.value = sensors[i];
}
if (source !== 'number') {
const numInput = document.getElementById(`val${i}`);
if (numInput) numInput.value = sensors[i].toFixed(2);
}
updateArrayView('internal');
}
function updateArrayView(source) {
if (source === 'internal') {
const text = '[\n ' + sensors.map(s => s.toFixed(2)).join(', ') + '\n]';
arrayView.value = text;
}
}
function updateFromText(val) {
try {
// Try to extract numbers from the string (handles brackets, commas, spaces)
const matches = val.match(/-?\d+(\.\d+)?/g);
if (matches && matches.length >= 22) {
matches.slice(0, 22).forEach((num, i) => {
sensors[i] = parseFloat(num);
const slider = document.getElementById(`slider${i}`);
const numInput = document.getElementById(`val${i}`);
if (slider) slider.value = sensors[i];
if (numInput) numInput.value = sensors[i].toFixed(2);
});
}
} catch (e) {
console.error("Parse error", e);
}
}
function loadExampleData() {
// Pick a random predefined sign from the dictionary
const signKeys = Object.keys(preDefinedSigns);
const randomKey = signKeys[Math.floor(Math.random() * signKeys.length)];
const baseValues = preDefinedSigns[randomKey];
for (let i = 0; i < 22; i++) {
// Apply correct noise scales based on sensor type (matching your Python dictionary)
let noise = 0;
if (i < 10) {
// Flex Sensors: +/- 0.05
noise = (Math.random() * 0.10) - 0.05;
} else if ((i - 10) % 6 < 3) {
// Accelerometers (Indices 10,11,12 and 16,17,18): +/- 0.05
// 1.0 noise was destroying the gravity vector!
noise = (Math.random() * 0.10) - 0.05;
} else {
// Gyroscopes (Indices 13,14,15 and 19,20,21): +/- 2.0
noise = (Math.random() * 4.0) - 2.0;
}
sensors[i] = parseFloat((baseValues[i] + noise).toFixed(2));
const slider = document.getElementById(`slider${i}`);
if (slider) slider.value = sensors[i];
const numInput = document.getElementById(`val${i}`);
if (numInput) numInput.value = sensors[i].toFixed(2);
}
updateArrayView();
// Removed auto-predict so you have to click Predict manually
}
function resetData() {
sensors = new Array(22).fill(0.0);
renderList();
}
const preDefinedSigns = {
"namaskar": [0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.80, 0.60, 0.0, 0.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.80, 0.60, 0.0, 0.0, 0.0],
"alvida": [0.10, 0.10, 0.10, 0.10, 0.10, 0.85, 0.00, 0.50, 0.0, 0.0, 20.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"shukriya": [0.10, 0.10, 0.10, 0.10, 0.10, 0.30, 0.80, 0.50, 0.0, 15.0, 5.0, 0.55, 0.55, 0.55, 0.55, 0.55, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"maafi": [0.85, 0.85, 0.85, 0.85, 0.85, -0.40, 0.00, 0.90, 3.0, 3.0, 10.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"haan": [0.90, 0.90, 0.90, 0.90, 0.90, 0.00, 0.50, 0.85, 0.0, 20.0, 0.0, 0.65, 0.65, 0.65, 0.65, 0.65, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"nahi": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.95, 0.0, 0.0, 25.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"madad": [0.10, 0.85, 0.85, 0.85, 0.85, 0.00, 0.60, 0.80, 0.0, 10.0, 5.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"doctor": [0.50, 0.10, 0.85, 0.85, 0.85, 0.00, 0.30, 0.95, 0.0, 5.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"dard": [0.60, 0.60, 0.60, 0.60, 0.60, -0.50, 0.00, 0.85, 10.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, -0.50, 0.00, 0.85, 10.0, 0.0, 0.0],
"call": [0.10, 0.85, 0.85, 0.85, 0.10, 0.50, 0.80, 0.30, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"aag": [0.60, 0.55, 0.55, 0.55, 0.55, 0.10, 0.10, 0.98, 0.0, 0.0, 18.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"accident": [0.85, 0.85, 0.85, 0.85, 0.85, 0.00, 0.10, 0.99, 15.0, 0.0, 0.0, 0.85, 0.85, 0.85, 0.85, 0.85, 0.00, 0.10, 0.99, -15.0, 0.0, 0.0],
"chor": [0.85, 0.55, 0.90, 0.90, 0.90, 0.00, 0.00, 0.95, 0.0, 0.0, 20.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"rona": [0.85, 0.10, 0.90, 0.90, 0.90, 0.40, 0.90, 0.15, 0.0, -15.0, 0.0, 0.85, 0.10, 0.90, 0.90, 0.90, 0.40, 0.90, 0.15, 0.0, -15.0, 0.0],
"paani": [0.90, 0.10, 0.10, 0.10, 0.90, 0.30, 0.80, 0.50, 0.0, -8.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"khana": [0.40, 0.40, 0.40, 0.40, 0.40, 0.00, 0.75, 0.65, 0.0, -8.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"ruko": [0.10, 0.10, 0.10, 0.10, 0.10, 0.85, 0.00, 0.50, 20.0, 5.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, 0.80, 0.00, 0.55, 0.0, 0.0, 0.0],
"aur": [0.40, 0.40, 0.40, 0.40, 0.40, 0.00, 0.20, 0.95, 5.0, 5.0, 0.0, 0.40, 0.40, 0.40, 0.40, 0.40, 0.00, 0.20, 0.95, 5.0, 5.0, 0.0],
"main": [0.85, 0.10, 0.90, 0.90, 0.90, -0.60, 0.00, 0.80, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"tum": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.95, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"ghar": [0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.95, 0.30, 0.0, 0.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.95, 0.30, 0.0, 0.0, 0.0],
"school": [0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.99, 0.0, -12.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, 0.00, 0.10, 0.99, 0.0, 0.0, 0.0],
"khushi": [0.10, 0.10, 0.10, 0.10, 0.10, -0.50, 0.00, 0.87, 0.0, 12.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, -0.50, 0.00, 0.87, 0.0, 12.0, 0.0],
"dukh": [0.10, 0.10, 0.10, 0.10, 0.10, 0.40, 0.90, 0.15, 0.0, -10.0, 0.0, 0.10, 0.10, 0.10, 0.10, 0.10, 0.40, 0.90, 0.15, 0.0, -10.0, 0.0],
"gussa": [0.70, 0.70, 0.70, 0.70, 0.70, 0.00, 0.00, 0.98, 0.0, -18.0, 0.0, 0.70, 0.70, 0.70, 0.70, 0.70, 0.00, 0.00, 0.98, 0.0, -18.0, 0.0],
"neend": [0.10, 0.10, 0.10, 0.10, 0.10, 0.70, 0.70, 0.15, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"ek": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.98, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"do": [0.85, 0.10, 0.10, 0.90, 0.90, 0.00, 0.00, 0.98, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"paanch": [0.10, 0.10, 0.10, 0.10, 0.10, 0.85, 0.00, 0.53, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"kya": [0.20, 0.20, 0.20, 0.20, 0.20, 0.00, 0.00, 0.95, 0.0, 0.0, 15.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"kahan": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.95, 0.0, 0.0, 18.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"dekhna": [0.85, 0.10, 0.10, 0.90, 0.90, 0.40, 0.80, 0.45, 0.0, 0.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"upar": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.98, 0.0, 15.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0],
"neeche": [0.85, 0.10, 0.90, 0.90, 0.90, 0.00, 0.00, 0.98, 0.0, -15.0, 0.0, 0.60, 0.60, 0.60, 0.60, 0.60, 0.00, 0.00, 1.00, 0.0, 0.0, 0.0]
};
function clearBuffer() {
wordBuffer = [];
document.getElementById('sentenceBuffer').innerText = '...';
document.getElementById('predictedWord').innerText = '--';
}
async function makePrediction() {
const btn = document.querySelector('.btn');
const oldText = btn.innerText;
btn.innerText = 'Predicting...';
try {
const response = await fetch('/predict', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ features: sensors, word_buffer: wordBuffer })
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || 'API Error');
}
const data = await response.json();
document.getElementById('predictedWord').innerText = data.predicted_word;
wordBuffer = data.word_buffer;
document.getElementById('sentenceBuffer').innerText = wordBuffer.join(' ') || '...';
} catch (e) {
alert('Failed: ' + e.message);
} finally {
btn.innerText = oldText;
}
}
// Initialize UI on load
renderList();
</script>
</body>
</html>
|