Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,384 changes: 4,384 additions & 0 deletions .matplotlib-cache/fontlist-v390.json

Large diffs are not rendered by default.

Empty file.
Binary file modified __pycache__/utilities.cpython-313.pyc
Binary file not shown.
570 changes: 516 additions & 54 deletions app.py

Large diffs are not rendered by default.

Empty file added flask-ui.err.log
Empty file.
Empty file added flask-ui.out.log
Empty file.
59 changes: 44 additions & 15 deletions static/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -601,13 +601,20 @@ function showResults(results) {

function generateResultsHTML(results) {
const hasTumor = results.has_tumor;
const predictionStatus = results.prediction_status || (hasTumor ? 'TUMOR_DETECTED' : 'NO_TUMOR');
const isUncertainScan = predictionStatus === 'UNCERTAIN_SCAN';
const needsAttention = hasTumor || isUncertainScan;
const confidence = (results.confidence * 100).toFixed(1);
const report = results.detailed_report || {};
const safetyCheck = results.safety_check || {};
const uncertainty = safetyCheck.uncertainty || {};

// Doctor's verdict based on results
const doctorVerdict = hasTumor
const doctorVerdict = hasTumor
? `Based on my examination and the AI analysis, I've identified an abnormal tissue mass in the MRI scan. The ${confidence}% confidence level suggests this requires immediate attention. I recommend scheduling a follow-up consultation with a neurologist for further evaluation and to discuss treatment options.`
: `After carefully reviewing this MRI scan along with our AI analysis, I'm pleased to report that no tumor indicators were detected. The brain tissue appears healthy with normal density patterns. However, please continue with regular check-ups as recommended by your primary physician.`;
: isUncertainScan
? `The primary model did not find a dominant tumor signal, but the secondary safety check flagged this scan as uncertain. Because low-confidence or high-variance negatives can hide subtle abnormalities, I recommend manual radiology review before treating this result as fully clear.`
: `After carefully reviewing this MRI scan along with our AI analysis, I'm pleased to report that no tumor indicators were detected. The brain tissue appears healthy with normal density patterns. However, please continue with regular check-ups as recommended by your primary physician.`;

let html = `
<!-- Share & Actions Bar -->
Expand Down Expand Up @@ -648,9 +655,9 @@ function generateResultsHTML(results) {
<span class="verdict-title">Senior Neuroradiologist</span>
</div>
</div>
<div class="verdict-stamp ${hasTumor ? 'concern' : 'clear'}">
<i class="fas ${hasTumor ? 'fa-exclamation-circle' : 'fa-check-circle'}"></i>
<span>${hasTumor ? 'Requires Attention' : 'All Clear'}</span>
<div class="verdict-stamp ${needsAttention ? 'concern' : 'clear'}">
<i class="fas ${needsAttention ? 'fa-exclamation-circle' : 'fa-check-circle'}"></i>
<span>${hasTumor ? 'Requires Attention' : (isUncertainScan ? 'Review Recommended' : 'All Clear')}</span>
</div>
</div>
<div class="verdict-content">
Expand All @@ -666,9 +673,9 @@ function generateResultsHTML(results) {
</div>

<div class="results-header">
<div class="result-badge ${hasTumor ? 'positive' : 'negative'}">
<i class="fas ${hasTumor ? 'fa-exclamation-triangle' : 'fa-check-circle'}"></i>
<span>${hasTumor ? 'Tumor Detected' : 'No Tumor Detected'}</span>
<div class="result-badge ${needsAttention ? 'positive' : 'negative'}">
<i class="fas ${needsAttention ? 'fa-exclamation-triangle' : 'fa-check-circle'}"></i>
<span>${hasTumor ? 'Tumor Detected' : (isUncertainScan ? 'Review Recommended' : 'No Tumor Detected')}</span>
</div>
<div class="confidence-score">${confidence}%</div>
<div class="confidence-label">AI Confidence Score</div>
Expand Down Expand Up @@ -711,13 +718,13 @@ function generateResultsHTML(results) {

<!-- Report Summary Cards -->
<div class="report-summary-grid">
<div class="summary-card ${hasTumor ? 'alert' : 'success'}">
<div class="summary-card ${needsAttention ? 'alert' : 'success'}">
<div class="summary-icon">
<i class="fas ${hasTumor ? 'fa-exclamation-triangle' : 'fa-check-circle'}"></i>
<i class="fas ${needsAttention ? 'fa-exclamation-triangle' : 'fa-check-circle'}"></i>
</div>
<div class="summary-content">
<span class="summary-label">Detection Status</span>
<span class="summary-value">${hasTumor ? 'Abnormality Detected' : 'No Abnormality'}</span>
<span class="summary-value">${hasTumor ? 'Abnormality Detected' : (isUncertainScan ? 'Manual Review Recommended' : 'No Abnormality')}</span>
</div>
</div>

Expand All @@ -730,6 +737,18 @@ function generateResultsHTML(results) {
<span class="summary-value">${chars.confidence_score || confidence + '%'}</span>
</div>
</div>

${isUncertainScan ? `
<div class="summary-card alert">
<div class="summary-icon">
<i class="fas fa-shield-alt"></i>
</div>
<div class="summary-content">
<span class="summary-label">Safety Check</span>
<span class="summary-value">Uncertain Scan</span>
</div>
</div>
` : ''}

${hasTumor ? `
<div class="summary-card" style="border-left: 4px solid ${severity.color || '#f59e0b'}">
Expand Down Expand Up @@ -823,11 +842,11 @@ function generateResultsHTML(results) {
// No tumor recommendations
html += `
<div class="report-details-grid">
<div class="details-card full-width success-card">
<div class="details-card full-width ${isUncertainScan ? '' : 'success-card'}">
<h4><i class="fas fa-clipboard-list"></i> Recommendations</h4>
<ul class="recommendations-list">
${(report.recommendations || []).map(rec => `
<li><i class="fas fa-check"></i> ${rec}</li>
<li><i class="fas ${isUncertainScan ? 'fa-chevron-right' : 'fa-check'}"></i> ${rec}</li>
`).join('')}
</ul>
</div>
Expand All @@ -853,6 +872,16 @@ function generateResultsHTML(results) {
<span class="meta-label">Ensemble</span>
<span class="meta-value">${metadata.ensemble_enabled ? 'Yes' : 'No'}</span>
</div>
<div class="metadata-item">
<span class="meta-label">Safety Status</span>
<span class="meta-value">${hasTumor ? 'Not Needed' : (isUncertainScan ? 'Review Recommended' : 'Clear')}</span>
</div>
${safetyCheck.applied && uncertainty.tumor_probability_variance !== undefined ? `
<div class="metadata-item">
<span class="meta-label">Uncertainty Variance</span>
<span class="meta-value">${Number(uncertainty.tumor_probability_variance).toFixed(4)}</span>
</div>
` : ''}
<div class="metadata-item">
<span class="meta-label">AI Version</span>
<span class="meta-value">${metadata.ai_version || '2.0.0'}</span>
Expand Down Expand Up @@ -1901,8 +1930,8 @@ async function loadScanHistory() {
}
</div>
<div class="history-item-info">
<div class="history-item-result ${scan.has_tumor ? 'tumor-detected' : 'no-tumor'}">
${scan.has_tumor ? (scan.severity || 'Tumor Detected') : 'No Tumor'}
<div class="history-item-result ${(scan.has_tumor || scan.prediction_status === 'UNCERTAIN_SCAN') ? 'tumor-detected' : 'no-tumor'}">
${scan.has_tumor ? (scan.severity || 'Tumor Detected') : (scan.prediction_status === 'UNCERTAIN_SCAN' ? 'Review Recommended' : 'No Tumor')}
</div>
<div class="history-item-confidence">
Confidence: ${(scan.confidence * 100).toFixed(1)}%
Expand Down
Binary file added uploads/tumor_mri.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 45 additions & 10 deletions utilities.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,46 @@

import pandas as pd
import numpy as np
import seaborn as sns
import cv2
import tensorflow as tf
import os
from skimage import io
try:
from skimage import io
except ImportError:
io = None
from PIL import Image
from tensorflow.keras import backend as K

#creating a custom datagenerator:


class MonteCarloDropout(tf.keras.layers.Dropout):
"""Dropout layer that stays stochastic during inference for uncertainty sampling."""

def call(self, inputs, training = None):
return super().call(inputs, training = True)


def model_supports_mc_dropout(model):
"""Return True when the model contains dropout layers that can be sampled at inference time."""
return any(isinstance(layer, tf.keras.layers.Dropout) for layer in model.layers)


def build_mc_dropout_model(model):
"""
Clone a model so only dropout stays active during inference.
Batch normalization and the rest of the network remain in inference mode.
"""
if not model_supports_mc_dropout(model):
return None

def clone_with_mc_dropout(layer):
if isinstance(layer, tf.keras.layers.Dropout):
return MonteCarloDropout.from_config(layer.get_config())
return layer.__class__.from_config(layer.get_config())

mc_model = tf.keras.models.clone_model(model, clone_function = clone_with_mc_dropout)
mc_model.set_weights(model.get_weights())
return mc_model

class DataGenerator(tf.keras.utils.Sequence):
def __init__(self, ids , mask, image_dir = './', batch_size = 16, img_h = 256, img_w = 256, shuffle = True):

Expand Down Expand Up @@ -59,6 +89,8 @@ def on_epoch_end(self):

def __data_generation(self, list_ids, list_mask):
'generate the data corresponding the indexes in a given batch of images'
if io is None:
raise ImportError('scikit-image is required for DataGenerator image loading.')

# create empty arrays of shape (batch_size,height,width,depth)
#Depth is 3 for input and depth is taken as 1 for output becasue mask consist only of 1 channel.
Expand Down Expand Up @@ -109,6 +141,9 @@ def __data_generation(self, list_ids, list_mask):

def prediction(test, model, model_seg):
'''

if io is None:
raise ImportError('scikit-image is required for batch prediction image loading.')
Predcition function which takes dataframe containing ImageID as Input and perform 2 type of prediction on the image
Initially, image is passed through the classification network which predicts whether the image has defect or not, if the model
is 99% sure that the image has no defect, then the image is labeled as no-defect, if the model is not sure, it passes the image to the
Expand Down Expand Up @@ -254,30 +289,30 @@ def precision_metric(y_true, y_pred):

def predict_with_tta_classification(model, img):
# Original
pred = model.predict(img)
pred = model.predict(img, verbose = 0)

# Horizontal flip
img_flip = np.flip(img, axis=2)
pred_flip = model.predict(img_flip)
pred_flip = model.predict(img_flip, verbose = 0)

# Vertical flip
img_vflip = np.flip(img, axis=1)
pred_vflip = model.predict(img_vflip)
pred_vflip = model.predict(img_vflip, verbose = 0)

return (pred + pred_flip + pred_vflip) / 3

def predict_with_tta_segmentation(model, img):
# Original
pred = model.predict(img)
pred = model.predict(img, verbose = 0)

# Horizontal flip
img_flip = np.flip(img, axis=2)
pred_flip = model.predict(img_flip)
pred_flip = model.predict(img_flip, verbose = 0)
pred_flip = np.flip(pred_flip, axis=2)

# Vertical flip
img_vflip = np.flip(img, axis=1)
pred_vflip = model.predict(img_vflip)
pred_vflip = model.predict(img_vflip, verbose = 0)
pred_vflip = np.flip(pred_vflip, axis=1)

return (pred + pred_flip + pred_vflip) / 3