const form = document.getElementById("measureForm"); const imageInput = document.getElementById("imageInput"); const statusText = document.getElementById("statusText"); const inputPreview = document.getElementById("inputPreview"); const debugPreview = document.getElementById("debugPreview"); const inputFrame = document.getElementById("inputFrame"); const debugFrame = document.getElementById("debugFrame"); const jsonOutput = document.getElementById("jsonOutput"); const jsonLink = document.getElementById("jsonLink"); const defaultSampleUrl = window.DEFAULT_SAMPLE_URL || ""; const failReasonMessageMap = { card_not_detected: "Credit card not detected. Place a full card flat beside your hand.", hand_not_detected: "Hand not detected. Include your full palm in frame and keep fingers fully visible.", finger_isolation_failed: "Could not isolate the selected finger. Keep one target finger extended and separated.", finger_mask_too_small: "Finger region is too small. Move closer and use a higher-resolution photo.", contour_extraction_failed: "Finger contour extraction failed. Improve lighting and reduce background clutter.", axis_estimation_failed: "Finger axis estimation failed. Keep the finger straight and fully visible.", zone_localization_failed: "Ring zone localization failed. Keep more of the finger base visible.", width_measurement_failed: "Width measurement failed. Retake with phone parallel to the table and steady focus.", sobel_edge_refinement_failed: "Edge refinement failed. Turn on flash or use stronger, even lighting.", width_unreasonable: "Measured width is out of range. Retake with the phone parallel to the table.", disagreement_with_contour: "Edge methods disagree too much. Retake with cleaner edges and more even lighting.", }; const formatFailReasonStatus = (failReason) => { if (!failReason) { return "Measurement failed."; } if (failReason.startsWith("quality_score_low_")) { return `Low edge quality detected. Turn on flash and retake. (${failReason})`; } if (failReason.startsWith("consistency_low_")) { return `Edge detection was inconsistent. Keep phone parallel to table and retry. (${failReason})`; } const friendlyMessage = failReasonMessageMap[failReason]; if (friendlyMessage) { return `${friendlyMessage} (${failReason})`; } return `Measurement failed: ${failReason}`; }; const setStatus = (text) => { statusText.textContent = text; }; const showImage = (imgEl, frameEl, url) => { if (!url) return; imgEl.src = url; frameEl.classList.add("show"); frameEl.querySelector(".placeholder").style.display = "none"; }; const buildMeasureSettings = () => { const fingerSelect = form.querySelector('select[name="finger_index"]'); return { finger_index: fingerSelect ? fingerSelect.value : "index", edge_method: "sobel", }; }; const runMeasurement = async (endpoint, formData, inputUrlFallback = "") => { setStatus("Measuring… Please wait."); jsonOutput.textContent = "{\n \"status\": \"processing\"\n}"; try { const response = await fetch(endpoint, { method: "POST", body: formData, }); if (!response.ok) { const error = await response.json(); setStatus(error.error || "Measurement failed"); return; } const data = await response.json(); jsonOutput.textContent = JSON.stringify(data.result, null, 2); jsonLink.href = data.result_json_url || "#"; showImage(inputPreview, inputFrame, data.input_image_url || inputUrlFallback); showImage(debugPreview, debugFrame, data.result_image_url); if (data.success) { setStatus("Measurement complete. Results updated."); } else { const failReason = data?.result?.fail_reason; setStatus(formatFailReasonStatus(failReason)); } } catch (error) { setStatus("Network error. Please retry."); } }; imageInput.addEventListener("change", () => { const file = imageInput.files[0]; if (!file) { setStatus("Sample image loaded. Upload your own photo or click Start Measurement."); if (defaultSampleUrl) { showImage(inputPreview, inputFrame, defaultSampleUrl); } return; } const url = URL.createObjectURL(file); showImage(inputPreview, inputFrame, url); setStatus("Image ready. Click to start measurement."); }); form.addEventListener("submit", async (event) => { event.preventDefault(); const settings = buildMeasureSettings(); const formData = new FormData(); formData.append("finger_index", settings.finger_index); formData.append("edge_method", settings.edge_method); const file = imageInput.files[0]; if (file) { formData.append("image", file); await runMeasurement("/api/measure", formData); return; } await runMeasurement("/api/measure-default", formData, defaultSampleUrl); }); if (defaultSampleUrl) { showImage(inputPreview, inputFrame, defaultSampleUrl); setStatus("Sample image loaded. Upload your own photo or click Start Measurement."); }