Spaces:
Sleeping
Sleeping
<html lang="ko"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>λ°©μ¬μ λ³΄κ³ μ μ€λͺ λꡬ</title> | |
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> | |
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link | |
href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500&family=Google+Sans+Text:wght@500&display=swap" | |
rel="stylesheet"> | |
<link | |
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" | |
rel="stylesheet"> | |
<!-- Link to the correct JS file --> | |
<script src="https://unpkg.com/compromise" defer></script> | |
<script src="{{ url_for('static', filename='js/demo.js') }}" defer></script> | |
</head> | |
<body> | |
<div class="info"> | |
<!-- New Info Page Content --> | |
<div class="info-page-container"> | |
<div class="info-content"> | |
<div class="info-header"> | |
<span class="info-title-demo">λ°©μ¬μ μ€λͺ λꡬ λ°λͺ¨</span><br> | |
<span class="info-title-demo info-subtitle-demo">κΈ°λ° κΈ°μ :</span> | |
<span class="info-title-med info-subtitle-demo">MedGemma</span> | |
</div> | |
<div class="info-text">λ°©μ¬μ μμκ³Όμ μνΈμμ©μ΄ νμ΅μ ν¬κ² ν₯μμν¬ μ μλ κ΅μ‘ μλ리μ€λ₯Ό μκ°ν΄λ³΄μΈμ. μ΄ λ°λͺ¨λ MedGemmaλ₯Ό κΈ°λ°μΌλ‘ λ°©μ¬μ μμκ³Ό κ΄λ ¨ λ³΄κ³ μλ₯Ό μ¬μ΄ μΈμ΄λ‘ λ²μνκ³ , μμμ κ΄λ ¨ μμμ μκ°μ μΌλ‘ κ°μ‘°νμ¬ νμν μ μλ μ μ©ν λꡬλ₯Ό μ 곡νλ λ°©λ²μ 보μ¬μ€λλ€.</div> | |
<div class="info-disclaimer-text"><span class="info-disclaimer-title">λ©΄μ± μ‘°ν</span> μ΄ λ°λͺ¨λ μ€λͺ λͺ©μ μΌλ‘λ§ μ 곡λλ©° μμ±λκ±°λ μΉμΈλ μ νμ λνλ΄μ§ μμ΅λλ€. νμ§, μμ μ± λλ ν¨λ₯μ λν κ·μ μ΄λ νμ€ μ€μλ₯Ό λνλ΄μ§ μμ΅λλ€. μ€μ μμ© νλ‘κ·Έλ¨μλ μΆκ° κ°λ°, κ΅μ‘ λ° μ μμ΄ νμν©λλ€. μ΄ λ°λͺ¨μμ κ°μ‘°λ κ²½νμ νμλ μμ μ λν MedGemmaμ κΈ°λ³Έ κΈ°λ₯μ 보μ¬μ£Όλ©°, κ°λ°μμ μ¬μ©μκ° κ°λ₯ν μμ© νλ‘κ·Έλ¨μ νμνκ³ μΆκ° κ°λ°μ μκ°μ μ£Όλλ‘ λκΈ° μν κ²μ λλ€.</div> | |
<button class="info-button" id="view-demo-button">λ°λͺ¨ 보기</button> | |
</div> | |
</div> | |
</div> | |
<div class="main"> | |
<div class="nav-button nav-button-back" id="back-to-info-button"> | |
<div class="nav-button-inner"> | |
<span class="material-symbols-outlined nav-button-icon">keyboard_arrow_left</span> | |
<span class="nav-button-text">λ€λ‘</span> | |
</div> | |
</div> | |
<div class="case-selector-tabs-container" id="case-selector-tabs-container"> | |
<div>X-λ μ΄</div> | |
<div class="case-selector-tabs" id="case-selector-tabs"> | |
{% if available_reports %} | |
{% for report in available_reports %} | |
{% if report.image_type == 'CXR' %} | |
<div class="nav-button nav-button-case" data-report-name="{{ report.name }}"> | |
<div class="nav-button-inner"> | |
<span class="nav-button-text">{{ report.name }}</span> | |
</div> | |
</div> | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
<span class="no-cases-available">μ¬μ© κ°λ₯ν μΌμ΄μ€κ° μμ΅λλ€</span> | |
{% endif %} | |
</div> | |
<div>CT</div> | |
<div class="case-selector-tabs" id="case-selector-tabs2"> | |
{% if available_reports %} | |
{% for report in available_reports %} | |
{% if report.image_type == 'CT' %} | |
<div class="nav-button nav-button-case" data-report-name="{{ report.name }}"> | |
<div class="nav-button-inner"> | |
<span class="nav-button-text">{{ report.name }}</span> | |
</div> | |
</div> | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
<span class="no-cases-available">μ¬μ© κ°λ₯ν μΌμ΄μ€κ° μμ΅λλ€</span> | |
{% endif %} | |
</div> | |
<div style="margin-top: 20px; border-top: 1px solid #ddd; padding-top: 20px;">μ΄λ―Έμ§ μ λ‘λ</div> | |
<div class="upload-section" style="padding: 20px; background-color: #f5f5f5; border-radius: 8px; margin-top: 10px;"> | |
<form id="upload-form" enctype="multipart/form-data"> | |
<div style="margin-bottom: 15px;"> | |
<label for="image-upload" style="display: block; margin-bottom: 5px; font-weight: 500;">μλ£ μμ μ ν:</label> | |
<input type="file" id="image-upload" name="image" accept=".png,.jpg,.jpeg,.gif,.bmp,.dcm,.dicom" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> | |
</div> | |
<div style="margin-bottom: 15px;"> | |
<label for="image-type-select" style="display: block; margin-bottom: 5px; font-weight: 500;">μμ μ ν:</label> | |
<select id="image-type-select" name="image_type" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> | |
<option value="CXR">νλΆ X-λ μ΄</option> | |
<option value="CT">CT</option> | |
<option value="OTHER">κΈ°ν μλ£ μμ</option> | |
</select> | |
</div> | |
<div style="margin-bottom: 15px;"> | |
<label for="context-input" style="display: block; margin-bottom: 5px; font-weight: 500;">μΆκ° μ 보 (μ νμ¬ν):</label> | |
<textarea id="context-input" name="context" rows="3" placeholder="νμ μ¦μμ΄λ κ²μ¬ λͺ©μ λ±μ μ λ ₯νμΈμ" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; resize: vertical;"></textarea> | |
</div> | |
<button type="submit" class="nav-button" style="width: 100%; padding: 12px; background-color: #4285f4; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer;"> | |
<span class="material-symbols-outlined" style="vertical-align: middle; margin-right: 5px;">upload</span> | |
νλ κ°μ΄λ λ°κΈ° | |
</button> | |
</form> | |
</div> | |
</div> | |
<div class="nav-button nav-button-info" id="info-button"> | |
<div class="nav-button-inner"> | |
<span class="material-symbols-outlined nav-button-icon">code_blocks</span> | |
<span class="nav-button-text">μ΄ λ°λͺ¨μ λν μμΈ μ 보</span> | |
</div> | |
</div> | |
<div class="image-section"> | |
<div id="image-modality-header" class="image-header">{{ image_type | default('μλ£ μμ', true) }} | |
</div> <!-- Updated: ID added, initial text changed --> | |
<div id="image-container" class="image-container"> <!-- Container for relative positioning --> | |
<img id="report-image" src="" alt="μλ£ μμ" style="display: none;"> | |
<div id="image-loading" class="loading" style="display: none;">μ΄λ―Έμ§ λ‘λ© μ€...</div> | |
<div id="image-error" class="error-message" style="display: none;"></div> | |
</div> | |
<div id="ct-image-note" class="image-note"> | |
μ΄κ²μ CTμ λ¨μΌ μ¬λΌμ΄μ€λ₯Ό 보μ¬μ€λλ€. λ³΄κ³ μμ λͺ¨λ μμλ₯Ό μκ°νν μλ μμ΅λλ€. | |
</div> | |
</div> | |
<div class="report-section"> | |
<div id="app-loading" class="loading" style="display: none;">λ³΄κ³ μ μμΈ μ 보 λ‘λ© μ€...</div> | |
<div id="app-error" class="error-message" style="display: none;"></div> | |
<div class="explanation-section"> | |
<div id="explanation-output" class="explanation-box"> | |
<div id="explanation-loading" class="loading">μ€λͺ μ μμ±νκ³ μμ΅λλ€... μ μλ§ κΈ°λ€λ €μ£ΌμΈμ.</div> | |
<div class="explanation-header">μ΄κ²μ΄ μλ―Ένλ λ° | |
</div> | |
<div id="explanation-content"> | |
λ¬Έμ₯μ ν΄λ¦νλ©΄ μ¬κΈ°μ μ€λͺ μ΄ νμλ©λλ€. | |
</div> | |
</div> | |
</div> | |
<div class="report-text-area"> | |
<div id="report-text-display" > | |
<!-- Report text will be loaded here --> | |
λ³΄κ³ μλ₯Ό μ ννλ©΄ ν μ€νΈκ° νμλ©λλ€. | |
</div> | |
<div class="floating-disclaimer"> | |
<span class="material-symbols-outlined disclaimer-icon">warning</span> | |
<span class="disclaimer-text">μ΄ λ°λͺ¨λ MedGemmaμ κΈ°λ³Έ κΈ°λ₯μ μ€λͺ νκΈ° μν λͺ©μ μΌλ‘λ§ μ 곡λ©λλ€. μμ±λκ±°λ μΉμΈλ μ νμ λνλ΄μ§ μμΌλ©°, μ§λ³μ΄λ μνλ₯Ό μ§λ¨νκ±°λ μΉλ£λ₯Ό μ μνκΈ° μν κ²μ΄ μλλ©°, μνμ μ‘°μΈμ μ¬μ©ν΄μλ μ λ©λλ€.</span> | |
</div> | |
</div> | |
</div> | |
<!-- Embed available reports data for JavaScript --> | |
<script id="reports-data" type="application/json"> | |
</script> | |
<div id="explanation-error" class="error-message" style="display: none;"></div> | |
</div> | |
<!-- Immersive Info Dialog --> | |
<div id="immersive-info-dialog" class="dialog-overlay" style="display: none;" role="dialog" aria-modal="true" | |
aria-labelledby="dialog-title"> | |
<div class="dialog-box"> | |
<button id="dialog-close-button" class="dialog-close-btn" aria-label="λνμμ λ«κΈ°"> | |
<span class="material-symbols-outlined">close</span> | |
</button> | |
<h2 id="dialog-title" class="dialog-title-text">μ΄ λ°λͺ¨μ λν μμΈ μ 보</h2> | |
<div class="dialog-body-scrollable"> | |
<p><b>λͺ¨λΈ:</b> μ΄ λ°λͺ¨λ ꡬκΈμ MedGemma-4Bλ₯Ό λ μ μ μΌλ‘ μ¬μ©ν©λλ€. μ΄λ Gemma 3 κΈ°λ° λͺ¨λΈλ‘, νλΆ X-λ μ΄μ κ°μ μλ£ ν μ€νΈ λ° μ΄λ―Έμ§λ₯Ό μ΄ν΄νλλ‘ λ―ΈμΈ μ‘°μ λμμ΅λλ€. μλ£ λ°μ΄ν°μ κ³ κΈ ν΄μμ μ 곡νμ¬ AI κΈ°λ° μλ£ μ ν리μΌμ΄μ κ°λ°μ κ°μννλ MedGemmaμ λ₯λ ₯μ 보μ¬μ€λλ€.</p> | |
<p><b>λͺ¨λΈ μ κ·Ό λ° μ¬μ©:</b> ꡬκΈμ MedGemma-4Bλ <a | |
href="https://huggingface.co/google/medgemma-4b-it" target="_blank">HuggingFace<img | |
class="hf-logo" | |
src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg"> | |
</a>μ | |
<a href="https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/medgemma" target="_blank">Model | |
Garden <img class="hf-logo" | |
src="https://www.gstatic.com/cloud/images/icons/apple-icon.png"></a>μμ μ¬μ©ν μ μμ΅λλ€. | |
λͺ¨λΈ μ¬μ© λ° μ ν μ¬νμ λν μμΈν λ΄μ©μ <a | |
href="https://developers.google.com/health-ai-developer-foundations?referral=rad_explain" | |
target="_blank">HAI-DEF | |
κ°λ°μ μ¬μ΄νΈ</a>μμ νμΈνμΈμ. | |
</p> | |
<p><b>Health AI Developer Foundations (HAI-DEF)</b>λ κ°λ°μκ° μλ£ λΆμΌμ© AI λͺ¨λΈμ ꡬμΆν μ μλλ‘ νλ μ€ν μ¨μ΄νΈ λͺ¨λΈ λ° κ΄λ ¨ 리μμ€ λͺ¨μμ μ 곡ν©λλ€.</p> | |
<p><b>λ°λͺ¨κ° λ§μμ λμ ¨λμ?</b> μ¬λ¬λΆμ νΌλλ°±μ κΈ°λ€λ¦½λλ€! μ΄ λ°λͺ¨κ° λμμ΄ λμλ€λ©΄, μλ¨μ λ§ν¬λ HuggingFace νμ΄μ§μμ β‘ λ²νΌμ ν΄λ¦νμ¬ κ°μ¬μ λ§μμ ννν΄μ£ΌμΈμ.</p> | |
<p><b>λ λ§μ λ°λͺ¨ νμ:</b> HuggingFace Spaces λλ Colabμ ν΅ν΄ μΆκ° λ°λͺ¨λ₯Ό νμΈνμΈμ: | |
</p> | |
<ul> | |
<li><a href="https://huggingface.co/spaces/google/cxr-foundation-demo?referral=rad_explain" | |
target="_blank"> | |
CXR Foundations λ°λͺ¨ <img class="hf-logo" | |
src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg"></a> | |
- | |
λΈλΌμ°μ μμ μ€νλλ λ°μ΄ν° ν¨μ¨μ μ΄κ³ μ λ‘μ· CXR μ΄λ―Έμ§ λΆλ₯λ₯Ό 보μ¬μ€λλ€.</li> | |
<li><a href="https://huggingface.co/spaces/google/path-foundation-demo?referral=rad_explain" | |
target="_blank"> | |
Path Foundations λ°λͺ¨ <img class="hf-logo" | |
src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg"></a> | |
- | |
λΈλΌμ°μ μμ μ€νλλ λ°μ΄ν° ν¨μ¨μ μΈ λΆλ₯ λ° λ³λ¦¬ μ¬λΌμ΄λ λ΄ μ΄μμΉ νμ§λ₯Ό κ°μ‘°ν©λλ€.</li> | |
<li><a href="https://github.com/Google-Health/medgemma/tree/main/notebooks/fine_tune_with_hugging_face.ipynb" target="_blank"> | |
MedGemma λ―ΈμΈ μ‘°μ Colab <img class="hf-logo" | |
src="https://upload.wikimedia.org/wikipedia/commons/d/d0/Google_Colaboratory_SVG_Logo.svg"></a> | |
- | |
μ΄ λͺ¨λΈμ λ―ΈμΈ μ‘°μ νλ λ°©λ²μ μμ λ₯Ό νμΈνμΈμ.</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<script> | |
// μ΄λ―Έμ§ μ λ‘λ νΌ μ²λ¦¬ | |
document.addEventListener('DOMContentLoaded', function() { | |
const uploadForm = document.getElementById('upload-form'); | |
if (uploadForm) { | |
uploadForm.addEventListener('submit', async function(e) { | |
e.preventDefault(); | |
const formData = new FormData(this); | |
const submitButton = this.querySelector('button[type="submit"]'); | |
const originalButtonText = submitButton.innerHTML; | |
// λ‘λ© μν νμ | |
submitButton.disabled = true; | |
submitButton.innerHTML = '<span class="material-symbols-outlined" style="vertical-align: middle; margin-right: 5px;">hourglass_empty</span>κ°μ΄λ μμ± μ€...'; | |
// κΈ°μ‘΄ μλ¬ λ©μμ§ μ κ±° | |
const errorDiv = document.getElementById('explanation-error'); | |
if (errorDiv) { | |
errorDiv.style.display = 'none'; | |
errorDiv.textContent = ''; | |
} | |
try { | |
const response = await fetch('/upload_and_analyze', { | |
method: 'POST', | |
body: formData | |
}); | |
const result = await response.json(); | |
if (response.ok) { | |
// μ λ‘λλ μ΄λ―Έμ§ νμ | |
const fileInput = document.getElementById('image-upload'); | |
const file = fileInput.files[0]; | |
if (file) { | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
const reportImage = document.getElementById('report-image'); | |
if (reportImage) { | |
reportImage.src = e.target.result; | |
reportImage.style.display = 'block'; | |
} | |
}; | |
reader.readAsDataURL(file); | |
} | |
// λΆμ κ²°κ³Ό νμ | |
const explanationContent = document.getElementById('explanation-content'); | |
if (explanationContent) { | |
explanationContent.innerHTML = result.analysis.replace(/\n/g, '<br>'); | |
} | |
// λ³΄κ³ μ ν μ€νΈ μμμ μ λ‘λ μ 보 νμ | |
const reportTextDisplay = document.getElementById('report-text-display'); | |
if (reportTextDisplay) { | |
reportTextDisplay.innerHTML = '<div style="padding: 20px; background-color: #e8f4f8; border-radius: 8px;">' + | |
'<h3 style="margin-top: 0;">μμ νλ κ°μ΄λ</h3>' + | |
'<p>νμΌλͺ : ' + file.name + '</p>' + | |
'<p>μμ μ ν: ' + document.getElementById('image-type-select').selectedOptions[0].text + '</p>' + | |
(document.getElementById('context-input').value ? '<p>μΆκ° μ 보: ' + document.getElementById('context-input').value + '</p>' : '') + | |
'<p style="margin-top: 10px; padding: 10px; background-color: #fff3cd; border-radius: 4px;">β οΈ μ λ‘λνμ μμμ λν μΌλ°μ μΈ νλ κ°μ΄λλ₯Ό μ 곡ν©λλ€.</p>' + | |
'</div>'; | |
} | |
// μ€λͺ λ‘λ© λ©μμ§ μ¨κΈ°κΈ° | |
const explanationLoading = document.getElementById('explanation-loading'); | |
if (explanationLoading) { | |
explanationLoading.style.display = 'none'; | |
} | |
// μ΄λ―Έμ§ ν€λ μ λ°μ΄νΈ | |
const imageHeader = document.getElementById('image-modality-header'); | |
if (imageHeader) { | |
imageHeader.textContent = document.getElementById('image-type-select').selectedOptions[0].text; | |
} | |
} else { | |
// μλ¬ νμ | |
if (errorDiv) { | |
errorDiv.textContent = result.error || 'κ°μ΄λ μμ± μ€ μ€λ₯κ° λ°μνμ΅λλ€.'; | |
errorDiv.style.display = 'block'; | |
} | |
} | |
} catch (error) { | |
console.error('μ λ‘λ μ€λ₯:', error); | |
if (errorDiv) { | |
errorDiv.textContent = 'λ€νΈμν¬ μ€λ₯κ° λ°μνμ΅λλ€. λ€μ μλν΄μ£ΌμΈμ.'; | |
errorDiv.style.display = 'block'; | |
} | |
} finally { | |
// λ²νΌ μλ μνλ‘ λ³΅μ | |
submitButton.disabled = false; | |
submitButton.innerHTML = originalButtonText; | |
} | |
}); | |
} | |
}); | |
</script> | |
</body> | |
</html> |