delete me

This commit is contained in:
DanielS
2026-05-29 14:14:04 +02:00
parent bd1bd52c6c
commit 5462784c1f
621 changed files with 2047 additions and 750 deletions

View File

@@ -36,10 +36,17 @@ let isWebcamActive = false;
let webcamStream = null;
let simulatedStreamInterval = null;
let currentFrameBase64 = null;
let activeCameraDeviceId = null; // stores selected camera device ID
let isDetecting = false;
let autoDetectEnabled = true;
let trainingPollInterval = null;
// Learn/Anlern-Modus State
let learnBackgroundFrame = null; // stores ImageData or Image object for canvas subtraction
let learnSelectedClass = null; // selected article key for training
let learnSessionImages = []; // list of filenames in the current session
let isSerialCapturing = false;
// Scanner Emulator State
let autoScanEnabled = true;
let stableClass = null;
@@ -56,6 +63,9 @@ let shoppingCart = [];
const videoEl = document.getElementById("webcam");
const canvasEl = document.getElementById("camera-canvas");
const ctx = canvasEl.getContext("2d");
const learnCanvasEl = document.getElementById("learn-camera-canvas");
const learnCtx = learnCanvasEl ? learnCanvasEl.getContext("2d") : null;
const toggleCameraBtn = document.getElementById("toggle-camera-btn");
const triggerScaleBtn = document.getElementById("trigger-scale-btn");
const autoDetectToggle = document.getElementById("auto-detect-toggle");
@@ -86,19 +96,121 @@ function switchTab(tabName) {
if (tabName === 'articles') {
loadArticlesDatabase();
}
if (tabName === 'learn') {
checkBackgroundCalibration();
loadLearnArticles();
updateLearnBadge();
}
}
// --- CAMERA & DETECTION LOGIC ---
async function startWebcam() {
async function initCameraSelection() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
const selectCheckout = document.getElementById("camera-select");
const selectLearn = document.getElementById("learn-camera-select");
if (selectCheckout && selectLearn) {
selectCheckout.innerHTML = "";
selectLearn.innerHTML = "";
if (videoDevices.length === 0) {
const opt = document.createElement("option");
opt.value = "";
opt.textContent = "Keine Kamera gefunden";
selectCheckout.appendChild(opt);
selectLearn.appendChild(opt.cloneNode(true));
return;
}
videoDevices.forEach((device, idx) => {
const label = device.label || `Kamera ${idx + 1}`;
const opt = document.createElement("option");
opt.value = device.deviceId;
opt.textContent = label;
selectCheckout.appendChild(opt);
selectLearn.appendChild(opt.cloneNode(true));
});
// Sync values to current active camera
if (activeCameraDeviceId) {
selectCheckout.value = activeCameraDeviceId;
selectLearn.value = activeCameraDeviceId;
} else {
activeCameraDeviceId = videoDevices[0].deviceId;
selectCheckout.value = activeCameraDeviceId;
selectLearn.value = activeCameraDeviceId;
}
// Hook up change handlers
selectCheckout.onchange = (e) => switchCameraDevice(e.target.value);
selectLearn.onchange = (e) => switchCameraDevice(e.target.value);
}
} catch (err) {
console.error("Fehler beim Abrufen der Kameras:", err);
}
}
async function switchCameraDevice(deviceId) {
activeCameraDeviceId = deviceId;
// Sync dropdown values across tabs
const selectCheckout = document.getElementById("camera-select");
const selectLearn = document.getElementById("learn-camera-select");
if (selectCheckout) selectCheckout.value = deviceId;
if (selectLearn) selectLearn.value = deviceId;
if (isWebcamActive) {
// Stop current tracks and restart webcam stream with the new device
if (webcamStream) {
webcamStream.getTracks().forEach(track => track.stop());
webcamStream = null;
}
await startWebcam(deviceId);
}
}
async function startWebcam(deviceId = null) {
try {
const targetDeviceId = deviceId || activeCameraDeviceId;
const videoConstraints = {
width: 640,
height: 480
};
if (targetDeviceId) {
videoConstraints.deviceId = { exact: targetDeviceId };
} else {
videoConstraints.facingMode = "environment";
}
webcamStream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480, facingMode: "environment" }
video: videoConstraints
});
videoEl.srcObject = webcamStream;
videoEl.style.display = "none";
isWebcamActive = true;
// Show camera selector dropdowns
const selectCheckout = document.getElementById("camera-select");
const selectLearn = document.getElementById("learn-camera-select");
if (selectCheckout) selectCheckout.style.display = "inline-block";
if (selectLearn) selectLearn.style.display = "inline-block";
cameraTypeBadge.textContent = "LIVE-WEBCAM";
cameraTypeBadge.style.background = "var(--color-primary)";
const learnStreamBadge = document.getElementById("learn-camera-type-badge");
if (learnStreamBadge) {
learnStreamBadge.textContent = "LIVE-WEBCAM";
learnStreamBadge.style.background = "var(--color-primary)";
}
toggleCameraBtn.innerHTML = `
<svg width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/></svg>
Kamera deaktivieren
@@ -106,6 +218,9 @@ async function startWebcam() {
document.getElementById("stream-status").textContent = "LIVE";
document.getElementById("stream-status").classList.add("live");
// Initialize options list
await initCameraSelection();
// Stop simulated stream if active
if (simulatedStreamInterval) {
clearInterval(simulatedStreamInterval);
@@ -128,25 +243,42 @@ function stopWebcam() {
videoEl.srcObject = null;
videoEl.style.display = "none";
isWebcamActive = false;
// Hide camera selectors when webcam is inactive
const selectCheckout = document.getElementById("camera-select");
const selectLearn = document.getElementById("learn-camera-select");
if (selectCheckout) selectCheckout.style.display = "none";
if (selectLearn) selectLearn.style.display = "none";
toggleCameraBtn.innerHTML = `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
Webcam aktivieren
`;
startSimulatedStream();
}
function processWebcamFrame() {
if (!isWebcamActive) return;
// Draw current video frame to canvas
ctx.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
// Get frame as base64
currentFrameBase64 = canvasEl.toDataURL("image/jpeg", 0.7);
// Auto-detect if enabled
if (autoDetectEnabled && !isDetecting) {
runDetection();
if (activeTab === 'checkout') {
// Draw current video frame to canvas
ctx.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
// Get frame as base64
currentFrameBase64 = canvasEl.toDataURL("image/jpeg", 0.7);
// Auto-detect if enabled
if (autoDetectEnabled && !isDetecting) {
runDetection();
}
} else if (activeTab === 'learn') {
if (learnCanvasEl && learnCtx) {
// Draw current video frame to learn canvas
learnCtx.drawImage(videoEl, 0, 0, learnCanvasEl.width, learnCanvasEl.height);
// Get frame as base64
currentFrameBase64 = learnCanvasEl.toDataURL("image/jpeg", 0.7);
// Draw dynamic green bounding box
drawLearnFrameGreenBox();
}
}
setTimeout(() => {
@@ -155,10 +287,22 @@ function processWebcamFrame() {
}
function startSimulatedStream() {
cameraTypeBadge.textContent = "SIMULATIONS-MODUS";
cameraTypeBadge.style.background = "var(--color-bg-dark)";
document.getElementById("stream-status").textContent = "Simuliert";
document.getElementById("stream-status").classList.add("live");
const streamBadge = document.getElementById("camera-type-badge");
const learnStreamBadge = document.getElementById("learn-camera-type-badge");
if (streamBadge) {
streamBadge.textContent = "SIMULATIONS-MODUS";
streamBadge.style.background = "var(--color-bg-dark)";
}
if (learnStreamBadge) {
learnStreamBadge.textContent = "SIMULATIONS-MODUS";
learnStreamBadge.style.background = "var(--color-bg-dark)";
}
const streamStatus = document.getElementById("stream-status");
if (streamStatus) {
streamStatus.textContent = "Simuliert";
streamStatus.classList.add("live");
}
const loadSimulatedFrame = async () => {
try {
@@ -167,11 +311,21 @@ function startSimulatedStream() {
if (data.status === "success" && data.image) {
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0, canvasEl.width, canvasEl.height);
currentFrameBase64 = data.image;
if (autoDetectEnabled && !isDetecting) {
runDetection();
if (activeTab === 'checkout') {
ctx.drawImage(img, 0, 0, canvasEl.width, canvasEl.height);
currentFrameBase64 = data.image;
if (autoDetectEnabled && !isDetecting) {
runDetection();
}
} else if (activeTab === 'learn') {
if (learnCanvasEl && learnCtx) {
learnCtx.drawImage(img, 0, 0, learnCanvasEl.width, learnCanvasEl.height);
currentFrameBase64 = data.image;
// Draw dynamic green bounding box
drawLearnFrameGreenBox();
}
}
};
img.src = data.image;
@@ -248,14 +402,14 @@ function drawDetections(predictions) {
function updateQuickButtons(predictions) {
quickSelectButtons.innerHTML = "";
// Filter predictions with confidence > 0.10
const candidates = predictions.filter(p => p.confidence > 0.10);
// Filter predictions with confidence > 0.85
const candidates = predictions.filter(p => p.confidence > 0.85);
if (candidates.length === 0) {
quickSelectButtons.innerHTML = `
<div class="empty-state">
<p>Keine Objekte erkannt</p>
<span>Confidence Schwellenwert unter 10%</span>
<span>Confidence Schwellenwert unter 85%</span>
</div>
`;
return;
@@ -446,6 +600,23 @@ async function startTraining() {
}
}
// Cancel active model training
async function cancelTraining() {
const cancelBtn = document.getElementById("cancel-training-btn");
if (cancelBtn) cancelBtn.disabled = true;
try {
const response = await fetch("/api/cancel_training", { method: "POST" });
const data = await response.json();
alert(data.message);
} catch (err) {
console.error("Failed to cancel training:", err);
alert("Fehler beim Abbrechen des Trainings.");
} finally {
if (cancelBtn) cancelBtn.disabled = false;
}
}
// Poll training API
function startTrainingPolling() {
if (trainingPollInterval) return;
@@ -497,6 +668,19 @@ function updateTrainingStatusUI(data) {
statusBox.classList.add("active-pulse");
}
// Toggle start and cancel buttons based on status
const startBtn = document.getElementById("start-training-btn");
const cancelBtn = document.getElementById("cancel-training-btn");
if (startBtn && cancelBtn) {
if (data.status === "training") {
startBtn.style.display = "none";
cancelBtn.style.display = "inline-block";
} else {
startBtn.style.display = "inline-block";
cancelBtn.style.display = "none";
}
}
// Plot Chart if loss history is available
if (data.train_loss && data.train_loss.length > 0) {
updateChart(data.train_loss, data.val_loss || []);
@@ -900,7 +1084,7 @@ async function processStabilityAndScan(predictions) {
const progressWrapper = document.getElementById("stability-progress-wrapper");
const progressFill = document.getElementById("stability-progress-fill");
const STABLE_CONF_THRESHOLD = 0.75;
const STABLE_CONF_THRESHOLD = 0.85;
if (topPrediction && topPrediction.confidence >= STABLE_CONF_THRESHOLD) {
const detectedClass = topPrediction.class;
@@ -986,6 +1170,518 @@ async function triggerManualScan() {
}
}
// --- LEARN ARTICLE TAB (ANLERN-MODUS) LOGIC ---
let learnCurrentBBox = null; // stores {xmin, ymin, xmax, ymax}
function computeBBoxFromDiff(currData, bgData, width, height, threshold = 25) {
let minX = width, minY = height, maxX = 0, maxY = 0;
let changedCount = 0;
for (let y = 0; y < height; y += 4) { // sample every 4th pixel for speed
for (let x = 0; x < width; x += 4) {
const idx = (y * width + x) * 4;
const rDiff = Math.abs(currData[idx] - bgData[idx]);
const gDiff = Math.abs(currData[idx+1] - bgData[idx+1]);
const bDiff = Math.abs(currData[idx+2] - bgData[idx+2]);
if (rDiff + gDiff + bDiff > threshold * 3) {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
changedCount++;
}
}
}
// If too few pixels changed, it's probably noise or empty
if (changedCount < 100) return null;
// Add padding
const padding = 15;
minX = Math.max(0, minX - padding);
minY = Math.max(0, minY - padding);
maxX = Math.min(width, maxX + padding);
maxY = Math.min(height, maxY + padding);
return { xmin: minX, ymin: minY, xmax: maxX, ymax: maxY };
}
function drawLearnFrameGreenBox() {
if (!learnCanvasEl || !learnCtx || !currentFrameBase64) return;
if (learnBackgroundFrame) {
try {
const width = learnCanvasEl.width;
const height = learnCanvasEl.height;
// Get current frame image data
const currImgData = learnCtx.getImageData(0, 0, width, height);
// Calculate bbox
const bbox = computeBBoxFromDiff(currImgData.data, learnBackgroundFrame.data, width, height);
learnCurrentBBox = bbox;
if (bbox) {
// Draw green bounding box outline
learnCtx.lineWidth = 3;
learnCtx.strokeStyle = "#10b981"; // Green
learnCtx.strokeRect(bbox.xmin, bbox.ymin, bbox.xmax - bbox.xmin, bbox.ymax - bbox.ymin);
// Draw label box background
learnCtx.fillStyle = "rgba(16, 185, 129, 0.85)";
const labelText = "Ware erkannt (Auto-Crop)";
learnCtx.font = "bold 12px sans-serif";
const textWidth = learnCtx.measureText(labelText).width;
learnCtx.fillRect(bbox.xmin - 1, bbox.ymin - 22, textWidth + 12, 22);
// Label text
learnCtx.fillStyle = "#ffffff";
learnCtx.fillText(labelText, bbox.xmin + 5, bbox.ymin - 6);
}
} catch (e) {
console.error("Error drawing green box:", e);
}
} else {
learnCurrentBBox = null;
}
}
async function calibrateBackground() {
if (!currentFrameBase64) {
alert("Warte auf Kamerabild...");
return;
}
try {
const response = await fetch("/api/calibrate_background", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ image: currentFrameBase64 })
});
const data = await response.json();
if (data.status === "success") {
// Save background frame locally as ImageData for real-time diffing
const width = learnCanvasEl.width;
const height = learnCanvasEl.height;
learnBackgroundFrame = learnCtx.getImageData(0, 0, width, height);
// Update UI indicators
const statusIndicator = document.getElementById("learn-calibration-indicator");
if (statusIndicator) {
statusIndicator.textContent = "Kalibriert";
statusIndicator.style.color = "var(--color-success)";
statusIndicator.classList.add("live");
}
const checkbox = document.getElementById("learn-bg-ok-indicator");
if (checkbox) {
checkbox.checked = true;
}
alert(data.message);
} else {
alert("Kalibrierung fehlgeschlagen: " + data.message);
}
} catch (err) {
console.error("Calibration error:", err);
alert("Serverfehler bei Kalibrierung.");
}
}
async function checkBackgroundCalibration() {
try {
const response = await fetch("/api/has_background");
const data = await response.json();
const statusIndicator = document.getElementById("learn-calibration-indicator");
const checkbox = document.getElementById("learn-bg-ok-indicator");
if (data.has_background) {
if (statusIndicator) {
statusIndicator.textContent = "Kalibriert";
statusIndicator.style.color = "var(--color-success)";
statusIndicator.classList.add("live");
}
if (checkbox) {
checkbox.checked = true;
}
// Load background image from backend and draw it on a hidden canvas to get ImageData
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
const hiddenCanvas = document.createElement("canvas");
hiddenCanvas.width = learnCanvasEl.width;
hiddenCanvas.height = learnCanvasEl.height;
const hiddenCtx = hiddenCanvas.getContext("2d");
hiddenCtx.drawImage(img, 0, 0, hiddenCanvas.width, hiddenCanvas.height);
learnBackgroundFrame = hiddenCtx.getImageData(0, 0, hiddenCanvas.width, hiddenCanvas.height);
};
img.src = "/api/background_image?t=" + Date.now(); // Cache busting
} else {
learnBackgroundFrame = null;
if (statusIndicator) {
statusIndicator.textContent = "Nicht kalibriert";
statusIndicator.style.color = "var(--text-muted)";
statusIndicator.classList.remove("live");
}
if (checkbox) {
checkbox.checked = false;
}
}
} catch (err) {
console.error("Failed to check background calibration:", err);
}
}
async function resetBackgroundCalibration() {
if (!confirm("Hintergrundkalibrierung wirklich zurücksetzen?")) return;
try {
const response = await fetch("/api/reset_background", { method: "POST" });
const data = await response.json();
if (data.status === "success") {
learnBackgroundFrame = null;
const statusIndicator = document.getElementById("learn-calibration-indicator");
if (statusIndicator) {
statusIndicator.textContent = "Nicht kalibriert";
statusIndicator.style.color = "var(--text-muted)";
statusIndicator.classList.remove("live");
}
const checkbox = document.getElementById("learn-bg-ok-indicator");
if (checkbox) {
checkbox.checked = false;
}
alert(data.message);
}
} catch (err) {
console.error("Reset calibration error:", err);
}
}
function loadLearnArticles() {
const listContainer = document.getElementById("learn-articles-list");
if (!listContainer) return;
listContainer.innerHTML = "";
// Check if database loaded, if not, wait
if (Object.keys(articlesDatabase).length === 0) {
setTimeout(loadLearnArticles, 200);
return;
}
Object.keys(articlesDatabase).forEach(key => {
const art = articlesDatabase[key];
const item = document.createElement("div");
item.className = "learn-article-item";
item.id = `learn-item-${key}`;
if (learnSelectedClass === key) {
item.classList.add("selected");
}
item.onclick = () => selectLearnArticle(key);
item.innerHTML = `
<span>${art.name}</span>
<span class="item-plu">EAN: ${art.ean || 'Keine'}</span>
`;
listContainer.appendChild(item);
});
}
function filterLearnArticles() {
const query = document.getElementById("learn-search-input").value.toLowerCase().trim();
const items = document.querySelectorAll(".learn-article-item");
items.forEach(item => {
const text = item.textContent.toLowerCase();
if (text.includes(query)) {
item.style.display = "flex";
} else {
item.style.display = "none";
}
});
}
function selectLearnArticle(classKey) {
learnSelectedClass = classKey;
document.querySelectorAll(".learn-article-item").forEach(el => el.classList.remove("selected"));
const selectedEl = document.getElementById(`learn-item-${classKey}`);
if (selectedEl) {
selectedEl.classList.add("selected");
}
const display = document.getElementById("learn-selected-article-display");
if (display) {
const name = articlesDatabase[classKey]?.name || classKey;
display.textContent = name;
}
}
function updateLearnBadge() {
fetch("/api/capture_count")
.then(r => r.json())
.then(data => {
const badge = document.getElementById("learn-session-count-badge");
if (badge) badge.textContent = `Eigene Bilder Gesamt: ${data.count}`;
const displayCheckout = document.getElementById("capture-count-display");
if (displayCheckout) displayCheckout.textContent = `Eigene Bilder: ${data.count}`;
})
.catch(err => console.error(err));
}
async function startSerialCapture() {
if (!learnSelectedClass) {
alert("Bitte wählen Sie zuerst einen Artikel aus der Liste aus.");
return;
}
if (!learnBackgroundFrame) {
alert("Bitte führen Sie zuerst die Hintergrund-Kalibrierung durch (Hintergrund speichern).");
return;
}
if (isSerialCapturing) return;
isSerialCapturing = true;
// Clear session gallery UI and list
const galleryGrid = document.getElementById("learn-gallery-grid");
galleryGrid.innerHTML = "";
learnSessionImages = [];
const startBtn = document.getElementById("learn-start-capture-btn");
startBtn.disabled = true;
startBtn.innerHTML = `<span>Aufnahme läuft...</span>`;
const progressText = document.getElementById("learn-progress-text");
const progressFill = document.getElementById("learn-progress-fill");
let count = 0;
const total = 10;
const captureNext = async () => {
// Sound feedback
playScannerBeep();
// Capture frame
const frameData = learnCanvasEl.toDataURL("image/jpeg", 0.95);
const currentBbox = learnCurrentBBox ? { ...learnCurrentBBox } : null;
try {
const response = await fetch("/api/capture_training_image", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
image: frameData,
class_name: learnSelectedClass,
bbox: currentBbox
})
});
const data = await response.json();
if (data.status === "success" && data.filename) {
learnSessionImages.push(data.filename);
addPhotoToGallery(data.filename);
}
} catch (err) {
console.error("Serial capture step failed:", err);
}
count++;
if (progressText) progressText.textContent = `${count} / ${total} Bilder`;
if (progressFill) progressFill.style.width = `${(count / total) * 100}%`;
if (count >= total) {
clearInterval(captureInterval);
isSerialCapturing = false;
startBtn.disabled = false;
startBtn.innerHTML = `
<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 cx="12" cy="12" r="3" fill="currentColor"/></svg>
START (SERIENAUFNAHME)
`;
updateLearnBadge();
alert("Serienaufnahme abgeschlossen! Sie können fehlerhafte Bilder unten in der Galerie löschen.");
}
};
// Start interval
await captureNext(); // First capture immediately
const captureInterval = setInterval(captureNext, 1000);
}
function addPhotoToGallery(filename) {
const galleryGrid = document.getElementById("learn-gallery-grid");
const emptyState = document.getElementById("learn-gallery-empty");
if (emptyState) emptyState.remove();
const wrapper = document.createElement("div");
wrapper.className = "gallery-item-wrapper";
wrapper.id = `gallery-item-${filename.replace(".", "_")}`;
wrapper.innerHTML = `
<img class="gallery-item-img" src="/api/captured_images/${filename}" alt="${filename}">
<button class="gallery-item-delete-btn" onclick="deleteSessionImage('${filename}')" title="Bild löschen">X</button>
`;
galleryGrid.appendChild(wrapper);
}
async function deleteSessionImage(filename) {
if (!confirm("Dieses Bild aus der Session löschen?")) return;
try {
const response = await fetch("/api/delete_captured_image", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename: filename })
});
const data = await response.json();
if (data.status === "success") {
// Remove from UI
const wrapper = document.getElementById(`gallery-item-${filename.replace(".", "_")}`);
if (wrapper) wrapper.remove();
// Remove from list
learnSessionImages = learnSessionImages.filter(f => f !== filename);
// If empty, show empty state
const galleryGrid = document.getElementById("learn-gallery-grid");
if (galleryGrid && galleryGrid.children.length === 0) {
galleryGrid.innerHTML = `
<div class="empty-state" id="learn-gallery-empty" style="grid-column: 1 / -1; padding: 40px 0;">
<p>Noch keine Aufnahmen in dieser Session</p>
<span>Wähle ein Produkt und starte die Serienaufnahme</span>
</div>
`;
}
updateLearnBadge();
console.log(data.message);
} else {
alert("Löschen fehlgeschlagen: " + data.message);
}
} catch (err) {
console.error("Delete image error:", err);
alert("Serverfehler beim Löschen des Bildes.");
}
}
// --- RESET ALL CAPTURED TRAINING DATA ---
async function resetAllCapturedData() {
const confirmed = confirm(
"⚠️ Alle selbst erfassten Trainingsbilder löschen?\n\n" +
"Dies entfernt alle user_capture_* Bilder und Labels sowie die Hintergrundkalibrierung.\n" +
"Das vortrainierte Basis-Modell bleibt erhalten.\n\n" +
"Dieser Vorgang kann NICHT rückgängig gemacht werden!"
);
if (!confirmed) return;
const resetBtnLearn = document.getElementById("reset-all-captures-btn");
const resetBtnDash = document.getElementById("dashboard-reset-btn");
if (resetBtnLearn) { resetBtnLearn.disabled = true; resetBtnLearn.textContent = "Wird gelöscht..."; }
if (resetBtnDash) { resetBtnDash.disabled = true; resetBtnDash.textContent = "Wird gelöscht..."; }
try {
const response = await fetch("/api/reset_dataset", { method: "POST" });
const data = await response.json();
if (data.status === "success") {
// 1. Clear gallery UI
const galleryGrid = document.getElementById("learn-gallery-grid");
if (galleryGrid) {
galleryGrid.innerHTML = `
<div class="empty-state" id="learn-gallery-empty" style="grid-column: 1 / -1; padding: 40px 0;">
<p>Noch keine Aufnahmen in dieser Session</p>
<span>Wähle ein Produkt und starte die Serienaufnahme</span>
</div>
`;
}
learnSessionImages = [];
// 2. Reset background calibration state
learnBackgroundFrame = null;
learnCurrentBBox = null;
const statusIndicator = document.getElementById("learn-calibration-indicator");
if (statusIndicator) {
statusIndicator.textContent = "Nicht kalibriert";
statusIndicator.style.color = "var(--text-muted)";
statusIndicator.classList.remove("live");
}
const checkbox = document.getElementById("learn-bg-ok-indicator");
if (checkbox) checkbox.checked = false;
// 3. Update count badges
const badge = document.getElementById("learn-session-count-badge");
if (badge) badge.textContent = "Eigene Bilder Gesamt: 0";
const countDisplay = document.getElementById("capture-count-display");
if (countDisplay) countDisplay.textContent = "Eigene Bilder: 0";
alert("✅ " + data.message);
} else {
alert("Fehler beim Zurücksetzen: " + (data.detail || data.message));
}
} catch (err) {
console.error("Reset error:", err);
alert("Serverfehler beim Zurücksetzen der Trainingsdaten.");
} finally {
if (resetBtnLearn) {
resetBtnLearn.disabled = false;
resetBtnLearn.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
Alle Trainingsbilder löschen
`;
}
if (resetBtnDash) {
resetBtnDash.disabled = false;
resetBtnDash.innerHTML = `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
Eigene Bilder & Kalibrierung zurücksetzen
`;
}
}
}
// --- RESET TRAINED AI MODEL ---
async function resetAIModel() {
const confirmed = confirm(
"⚠️ Trainiertes KI-Modell wirklich zurücksetzen?\n\n" +
"Dies löscht das trainierte PyTorch- und ONNX-Modell vom Server.\n" +
"Das System läuft danach im Simulations-Modus, bis Sie ein neues Modell trainieren.\n\n" +
"Dieser Vorgang kann NICHT rückgängig gemacht werden!"
);
if (!confirmed) return;
const resetBtn = document.getElementById("dashboard-reset-model-btn");
if (resetBtn) {
resetBtn.disabled = true;
resetBtn.textContent = "Zurücksetzen...";
}
try {
const response = await fetch("/api/reset_model", { method: "POST" });
const data = await response.json();
if (response.ok && data.status === "success") {
alert("✅ " + data.message);
// Refresh dashboard training status if polling is active
if (activeTab === 'dashboard') {
const statusResponse = await fetch("/api/train_status");
const statusData = await statusResponse.json();
updateTrainingStatusUI(statusData);
}
} else {
alert("Fehler beim Zurücksetzen des Modells: " + (data.detail || data.message));
}
} catch (err) {
console.error("Reset model error:", err);
alert("Serverfehler beim Zurücksetzen des KI-Modells.");
} finally {
if (resetBtn) {
resetBtn.disabled = false;
resetBtn.innerHTML = `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/></svg>
Trainiertes KI-Modell zurücksetzen
`;
}
}
}
// --- INTERACTIVE EVENT LISTENERS ---
toggleCameraBtn.addEventListener("click", () => {
if (isWebcamActive) {
@@ -1022,4 +1718,8 @@ window.addEventListener("DOMContentLoaded", () => {
// Initialize capture classes
initCapturePanel();
// Check background calibration on startup
checkBackgroundCalibration();
updateLearnBadge();
});