| {% extends "base.html" %} |
|
|
| {% block title %}Upload Scan β AI Medical Intelligence Pipeline{% endblock %} |
|
|
| {% block content %} |
| <section class="breadcrumb"> |
| <a href="{{ url_for('home') }}">Home</a> |
| <span class="sep">/</span> |
| <span>Upload Scans</span> |
| </section> |
|
|
| <section class="upload-hero"> |
| <h1>Upload DICOM Scans</h1> |
| <p> |
| Upload one or many CT brain scans for AI-powered hemorrhage screening. |
| A single exam may contain hundreds of slices β all modes below handle |
| that seamlessly. |
| </p> |
| </section> |
|
|
| {% with messages = get_flashed_messages(with_categories=true) %} |
| {% if messages %} |
| <div class="flash-messages"> |
| {% for category, message in messages %} |
| <div class="flash flash-{{ category }}">{{ message }}</div> |
| {% endfor %} |
| </div> |
| {% endif %} |
| {% endwith %} |
|
|
| |
| <div class="upload-tabs" role="tablist"> |
| <button class="upload-tab active" data-tab="single" role="tab">Single File</button> |
| <button class="upload-tab" data-tab="multi" role="tab">Multi-File / ZIP</button> |
| {% if local_mode %} |
| <button class="upload-tab" data-tab="dirscan" role="tab">Scan Directory</button> |
| {% endif %} |
| </div> |
|
|
| |
| |
| |
| <section class="panel upload-panel tab-panel active" id="tab-single"> |
| <form method="post" action="{{ url_for('analyze') }}" enctype="multipart/form-data" id="singleForm"> |
|
|
| <div class="dropzone" id="dropzoneSingle"> |
| <svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" |
| stroke-linecap="round" stroke-linejoin="round"> |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> |
| <polyline points="17 8 12 3 7 8" /> |
| <line x1="12" y1="3" x2="12" y2="15" /> |
| </svg> |
| <p class="dropzone-text">Drag & drop a .dcm file here</p> |
| <p class="muted small">or click to browse</p> |
| <input type="file" name="file" id="singleInput" accept=".dcm" hidden /> |
| </div> |
|
|
| <div class="file-info" id="singleInfo" style="display: none"> |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /> |
| <polyline points="14 2 14 8 20 8" /> |
| </svg> |
| <span id="singleFileName"></span> |
| <button type="button" class="btn btn-sm btn-ghost js-clear-single">Remove</button> |
| </div> |
|
|
| <button type="submit" class="btn btn-primary" id="singleSubmit" disabled> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M22 12h-4l-3 9L9 3l-3 9H2" /> |
| </svg> |
| Analyze Scan |
| </button> |
| </form> |
|
|
| <div class="loading-overlay" id="singleOverlay" style="display: none"> |
| <div class="spinner"></div> |
| <p>Running AI analysis…</p> |
| <p class="muted small">This may take a moment on first run while the model loads.</p> |
| </div> |
| </section> |
|
|
| |
| |
| |
| <section class="panel upload-panel tab-panel" id="tab-multi"> |
| <form method="post" action="{{ url_for('analyze') }}" enctype="multipart/form-data" id="multiForm"> |
|
|
| <div class="dropzone" id="dropzoneMulti"> |
| <svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" |
| stroke-linecap="round" stroke-linejoin="round"> |
| <rect x="2" y="7" width="20" height="14" rx="2" /> |
| <path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" /> |
| </svg> |
| <p class="dropzone-text">Drag & drop .dcm files or a .zip archive</p> |
| <p class="muted small">Select multiple files, or a single .zip containing DICOM slices</p> |
| <input type="file" name="file" id="multiInput" accept=".dcm,.zip" multiple hidden /> |
| </div> |
|
|
| <div class="file-info" id="multiInfo" style="display: none"> |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <rect x="2" y="7" width="20" height="14" rx="2" /> |
| <path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" /> |
| </svg> |
| <span id="multiFileName"></span> |
| <button type="button" class="btn btn-sm btn-ghost js-clear-multi">Remove all</button> |
| </div> |
|
|
| <button type="submit" class="btn btn-primary" id="multiSubmit" disabled> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M22 12h-4l-3 9L9 3l-3 9H2" /> |
| </svg> |
| Analyze Batch |
| </button> |
| </form> |
|
|
| <div class="loading-overlay" id="multiOverlay" style="display: none"> |
| <div class="spinner"></div> |
| <p>Uploading files…</p> |
| <p class="muted small">Large batches may take a moment to upload.</p> |
| </div> |
| </section> |
|
|
| |
| |
| |
| {% if local_mode %} |
| <section class="panel upload-panel tab-panel" id="tab-dirscan"> |
| <form method="post" action="{{ url_for('analyze_directory') }}" id="dirForm"> |
| <label class="dir-label" for="dirPath"> |
| Server-side directory containing .dcm files |
| </label> |
| <div class="dir-input-row"> |
| <input type="text" name="dir_path" id="dirPath" class="input" placeholder="D:\scans\patient_001" |
| spellcheck="false" autocomplete="off" /> |
| <button type="submit" class="btn btn-primary" id="dirSubmit"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <circle cx="11" cy="11" r="8" /> |
| <line x1="21" y1="21" x2="16.65" y2="16.65" /> |
| </svg> |
| Scan & Analyze |
| </button> |
| </div> |
| <p class="muted small" style="margin-top: 8px"> |
| The server will recursively find all <code>.dcm</code> files in this |
| directory and its sub-folders, then run inference on each. |
| This option is only available when running locally. |
| </p> |
| </form> |
| </section> |
| {% endif %} |
|
|
| |
| <section class="panel" style="margin-top: 16px"> |
| <h3>How It Works</h3> |
| <div class="steps-grid"> |
| <div class="step"> |
| <div class="step-num">1</div> |
| <div class="step-text"> |
| <strong>Upload</strong> |
| <p class="muted small">Select DICOM files, a ZIP, or enter a directory path</p> |
| </div> |
| </div> |
| <div class="step"> |
| <div class="step-num">2</div> |
| <div class="step-text"> |
| <strong>Process</strong> |
| <p class="muted small">CT windowing & preprocessing on each slice</p> |
| </div> |
| </div> |
| <div class="step"> |
| <div class="step-num">3</div> |
| <div class="step-text"> |
| <strong>Analyze</strong> |
| <p class="muted small">EfficientNet-B4 model with calibrated scoring</p> |
| </div> |
| </div> |
| <div class="step"> |
| <div class="step-num">4</div> |
| <div class="step-text"> |
| <strong>Report</strong> |
| <p class="muted small">Grad-CAM visualization & clinical report per slice</p> |
| </div> |
| </div> |
| </div> |
| </section> |
| {% endblock %} |
|
|
| {% block scripts %} |
| <script src="{{ url_for('static', filename='js/upload.js') }}" defer></script> |
| {% endblock %} |