|
|
|
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Lesion Detection</title> |
|
|
<link rel="stylesheet" href="{{ url_for('static', path='css/detect.css') }}"> |
|
|
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|
|
<link rel="preconnect" href="https://fonts.gstatic.com"> |
|
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Thai:wght@100;200;300;400;500;600;700&display=swap" |
|
|
rel="stylesheet"> |
|
|
<link rel="icon" type="image/x-icon" href="{{ url_for('static', path='image/production.png') }}"> |
|
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" |
|
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> |
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css"> |
|
|
<style> |
|
|
:root { |
|
|
--primary-color: #1da2e0; |
|
|
--secondary-color: #c8e5fa; |
|
|
} |
|
|
body { |
|
|
box-sizing: border-box; |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
font-family: 'IBM Plex Sans Thai', sans-serif; |
|
|
min-height: 100vh; |
|
|
} |
|
|
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); z-index: 1060; display: flex; justify-content: center; align-items: center; } |
|
|
.spinner-container { color: white; } |
|
|
.spinner { animation: rotate 2s linear infinite; width: 50px; height: 50px; } |
|
|
.path { stroke: #93d3f4; stroke-linecap: round; animation: dash 1.5s ease-in-out infinite; } |
|
|
@keyframes rotate { 100% { transform: rotate(360deg); } } |
|
|
@keyframes dash { |
|
|
0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; } |
|
|
50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; } |
|
|
100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; } |
|
|
} |
|
|
#symptomTextArea::placeholder { |
|
|
color: #adb5bd; |
|
|
opacity: 1; |
|
|
} |
|
|
.background-color { |
|
|
background-color: #f8f9fa; |
|
|
} |
|
|
.footer { |
|
|
margin-top: auto; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
|
|
|
<body> |
|
|
|
|
|
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm"> |
|
|
<div class="container-fluid"> |
|
|
<a class="navbar-brand" href="/"> |
|
|
<img src="{{ url_for('static', path='image/Logo.png') }}" alt="Logo" class="img-fluid logo" |
|
|
style="max-width: 100px; height: auto;" /> |
|
|
</a> |
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> |
|
|
<span class="navbar-toggler-icon"></span> |
|
|
</button> |
|
|
<div class="collapse navbar-collapse" id="navbarNav"> |
|
|
<ul class="navbar-nav ms-auto"> |
|
|
<li class="nav-item"><a class="nav-link active" href="/detect">ตรวจจับรอยโรค</a></li> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
</nav> |
|
|
|
|
|
|
|
|
<div class="detect-section flex-grow-1 background-color"> |
|
|
<div class="container py-4"> |
|
|
<div class="row justify-content-center"> |
|
|
<div class="col-md-8"> |
|
|
<div class="input-card shadow-lg p-4 bg-white rounded-3"> |
|
|
<h2 class="text-center mb-4 fw-bold">ตรวจจับรอยโรคในช่องปาก</h2> |
|
|
<form id="sectionForm" method="post" enctype="multipart/form-data" action="/uploaded"> |
|
|
<div class="mb-4"> |
|
|
<label for="file" class="form-label fs-5">อัปโหลดรูปภาพ</label> |
|
|
<input class="form-control" type="file" id="file" name="file" required accept="image/*"> |
|
|
</div> |
|
|
<div> |
|
|
<p class="fs-5">ประวัติ/อาการผู้ป่วย</p> |
|
|
<p class="text-muted small">โปรดเลือกจากรายการ หรือพิมพ์เพิ่มเติม (หากไม่เลือกหรือพิมพ์ ระบบจะบันทึกว่า “ไม่มีอาการผิดปกติ” โดยอัตโนมัติ)</p> |
|
|
</div> |
|
|
<div class="row"> |
|
|
<div class="col-md-6"> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check6" name="checkboxes" value="noSymptoms"><label class="form-check-label" for="check6">ไม่มีอาการผิดปกติ</label></div> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check1" name="checkboxes" value="drinkAlcohol"><label class="form-check-label" for="check1">ดื่มเครื่องดื่มแอลกอฮอล์</label></div> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check2" name="checkboxes" value="smoking"><label class="form-check-label" for="check2">สูบบุหรี่</label></div> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check3" name="checkboxes" value="chewBetelNut"><label class="form-check-label" for="check3">เคี้ยวหมาก</label></div> |
|
|
</div> |
|
|
<div class="col-md-6"> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check4" name="checkboxes" value="eatSpicyFood"><label class="form-check-label" for="check4">รับประทานอาหารเผ็ดแล้วรู้สึกระคายเคือง</label></div> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check5" name="checkboxes" value="wipeOff"><label class="form-check-label" for="check5">คราบขาวที่สามารถลอกออก</label></div> |
|
|
<div class="form-check mb-1"><input class="form-check-input symptom-checkbox" type="checkbox" id="check7" name="checkboxes" value="alwaysHurts"><label class="form-check-label" for="check7">มีอาการเจ็บหรือระคายเคืองตลอดเวลา</label></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-4"> |
|
|
<label for="symptomTextArea" class="form-label">รายละเอียดอาการเพิ่มเติม:</label> |
|
|
<textarea class="form-control" id="symptomTextArea" name="symptom_text" rows="4" placeholder="เช่น มีอาการปวดเป็นบางครั้ง, เป็นมานาน 2 สัปดาห์ ..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="mt-4 d-flex justify-content-center"> |
|
|
<button class="button" type="submit">Submit</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="loading" class="loading-overlay d-none"> |
|
|
<div class="spinner-container text-center"> |
|
|
<svg class="spinner" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"> |
|
|
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="4"></circle> |
|
|
</svg> |
|
|
<p class="mt-2">กำลังประมวลผล, กรุณารอสักครู่...</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
{% if image_b64_data %} |
|
|
<div class="modal fade" id="outputModal" tabindex="-1"> |
|
|
<div class="modal-dialog modal-xl modal-dialog-centered"> |
|
|
<div class="modal-content"> |
|
|
<div class="modal-header"> |
|
|
<h5 class="modal-title">ผลการตรวจจับรอยโรค</h5> |
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
|
|
</div> |
|
|
<div class="modal-body"> |
|
|
<div class="row g-3"> |
|
|
<div class="col-lg-8"> |
|
|
<div class="row"> |
|
|
<div class="col-md-6 text-center mb-2"> |
|
|
<h6>ภาพต้นฉบับ</h6> |
|
|
<img src="data:image/jpeg;base64, {{ image_b64_data }}" alt="Original Image" class="img-fluid rounded shadow-sm"> |
|
|
</div> |
|
|
<div class="col-md-6 text-center"> |
|
|
<h6 class="d-inline-block">Grad-CAM Heatmap</h6> |
|
|
<i class="bi bi-info-circle-fill ms-1 info-icon" |
|
|
data-bs-toggle="tooltip" |
|
|
data-bs-placement="top" |
|
|
title="Grad-CAM Heatmap แสดงถึงความสนใจของโมเดล โดยพื้นที่สีแดงคือบริเวณที่โมเดลให้ความสำคัญสูงสุดในการตัดสินใจ"></i> |
|
|
<img src="data:image/jpeg;base64, {{ gradcam_b64_data }}" alt="Grad-CAM Heatmap" class="img-fluid rounded shadow-sm"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="col-lg-4 d-flex align-items-center"> |
|
|
<div class="w-100"> |
|
|
<h4 class="mb-3">ผลการวิเคราะห์</h4> |
|
|
<p class="fs-6">ผลการวิเคราะห์รอยโรคจากภาพร่วมกับประวัติผู้ป่วย:</p> |
|
|
<div class="alert alert-primary" role="alert"> |
|
|
<h5 class="alert-heading">ประเภทรอยโรคที่คาดการณ์:</h5> |
|
|
<p class="fs-4 fw-bold mb-0">{{ name_out }}</p> |
|
|
</div> |
|
|
<div class="alert alert-danger" role="alert"> |
|
|
<h5 class="alert-heading">โอกาสเป็นรอยโรค:</h5> |
|
|
<p class="fs-4 fw-bold mb-0">{{ eva_output }} %</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ปิด</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
<footer class="footer bg-dark text-white p-4 text-center"> |
|
|
© 2025 MYCompany. All Rights Reserved. |
|
|
</footer> |
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> |
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
|
|
|
|
const form = document.getElementById('sectionForm'); |
|
|
const loadingOverlay = document.getElementById('loading'); |
|
|
if (form) { |
|
|
form.addEventListener('submit', function () { |
|
|
|
|
|
if (form.checkValidity()) { |
|
|
loadingOverlay.classList.remove('d-none'); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const outputModalElement = document.getElementById('outputModal'); |
|
|
if (outputModalElement) { |
|
|
const outputModal = new bootstrap.Modal(outputModalElement); |
|
|
outputModal.show(); |
|
|
} |
|
|
|
|
|
|
|
|
const noSymptomsCheckbox = document.getElementById('check6'); |
|
|
const otherCheckboxes = document.querySelectorAll('.symptom-checkbox:not(#check6)'); |
|
|
|
|
|
if (noSymptomsCheckbox) { |
|
|
noSymptomsCheckbox.addEventListener('change', function() { |
|
|
if (this.checked) { |
|
|
otherCheckboxes.forEach(checkbox => { |
|
|
|
|
|
if (!['check1', 'check2', 'check3'].includes(checkbox.id)) { |
|
|
checkbox.checked = false; |
|
|
checkbox.disabled = true; |
|
|
} |
|
|
}); |
|
|
} else { |
|
|
otherCheckboxes.forEach(checkbox => { |
|
|
checkbox.disabled = false; |
|
|
}); |
|
|
} |
|
|
}); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |