Upload 10 files
Browse files- .gitattributes +2 -0
- app.py +73 -0
- ensemble_model_best(92.3).h5 +3 -0
- gradcam_utils.py +71 -0
- models.py +27 -0
- templates/import.html +29 -0
- templates/index.html +73 -0
- templates/result.html +22 -0
- uploads/IM-0006-0001.jpeg +3 -0
- uploads/IM-0011-0001-0002.jpeg +3 -0
- uploads/person3_virus_16.jpeg +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
uploads/IM-0006-0001.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
uploads/IM-0011-0001-0002.jpeg filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import numpy as np
|
| 3 |
+
from PIL import Image
|
| 4 |
+
import cv2
|
| 5 |
+
from flask import Flask, request, render_template
|
| 6 |
+
from werkzeug.utils import secure_filename
|
| 7 |
+
from tensorflow.keras.models import load_model
|
| 8 |
+
from gradcam_utils import generate_and_merge_heatmaps
|
| 9 |
+
|
| 10 |
+
app = Flask(__name__)
|
| 11 |
+
UPLOAD_FOLDER = 'static/uploads'
|
| 12 |
+
HEATMAP_PATH = 'static/heatmap.jpg'
|
| 13 |
+
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
| 14 |
+
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
| 15 |
+
|
| 16 |
+
# Load your trained ensemble model
|
| 17 |
+
model = load_model('ensemble_model_best(92.3).h5')
|
| 18 |
+
|
| 19 |
+
# Load the three base models (if required for gradcam)
|
| 20 |
+
from models import create_vgg19_model, create_efficientnet_model, create_densenet_model
|
| 21 |
+
vgg_model = create_vgg19_model()
|
| 22 |
+
efficientnet_model = create_efficientnet_model()
|
| 23 |
+
densenet_model = create_densenet_model()
|
| 24 |
+
|
| 25 |
+
print('Model loaded. Visit http://127.0.0.1:5000/')
|
| 26 |
+
|
| 27 |
+
def get_className(classNo):
|
| 28 |
+
return "Normal" if classNo == 0 else "Pneumonia"
|
| 29 |
+
|
| 30 |
+
def getResult(img_path):
|
| 31 |
+
image = cv2.imread(img_path)
|
| 32 |
+
image = Image.fromarray(image, 'RGB')
|
| 33 |
+
image = image.resize((224, 224))
|
| 34 |
+
image = np.array(image)
|
| 35 |
+
input_img = np.expand_dims(image, axis=0) / 255.0
|
| 36 |
+
result = model.predict(input_img)
|
| 37 |
+
result01 = np.argmax(result, axis=1)
|
| 38 |
+
return result01
|
| 39 |
+
|
| 40 |
+
@app.route('/', methods=['GET'])
|
| 41 |
+
def index():
|
| 42 |
+
return render_template('index.html')
|
| 43 |
+
|
| 44 |
+
@app.route('/predict', methods=['POST'])
|
| 45 |
+
def upload():
|
| 46 |
+
if request.method == 'POST':
|
| 47 |
+
f = request.files['file']
|
| 48 |
+
filename = secure_filename(f.filename)
|
| 49 |
+
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 50 |
+
f.save(file_path)
|
| 51 |
+
|
| 52 |
+
# Get prediction
|
| 53 |
+
value = getResult(file_path)
|
| 54 |
+
result = get_className(value[0])
|
| 55 |
+
|
| 56 |
+
# Generate Grad-CAM heatmap
|
| 57 |
+
heatmap_img = generate_and_merge_heatmaps(
|
| 58 |
+
file_path, vgg_model, efficientnet_model, densenet_model
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# Save heatmap image
|
| 62 |
+
cv2.imwrite(HEATMAP_PATH, cv2.cvtColor(heatmap_img, cv2.COLOR_RGB2BGR))
|
| 63 |
+
|
| 64 |
+
return render_template(
|
| 65 |
+
'result.html',
|
| 66 |
+
prediction=result,
|
| 67 |
+
original_image=file_path,
|
| 68 |
+
heatmap_image=HEATMAP_PATH
|
| 69 |
+
)
|
| 70 |
+
return None
|
| 71 |
+
|
| 72 |
+
if __name__ == '__main__':
|
| 73 |
+
app.run(host='0.0.0.0', port=5000, debug=True)
|
ensemble_model_best(92.3).h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b66958912a20cc6764eaec0045841bdd789cb950cd3677feb54cf1a32558ca5c
|
| 3 |
+
size 304608784
|
gradcam_utils.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import cv2
|
| 3 |
+
import tensorflow as tf
|
| 4 |
+
from tensorflow.keras.preprocessing.image import img_to_array, load_img
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from matplotlib.colors import LinearSegmentedColormap
|
| 7 |
+
|
| 8 |
+
def preprocess_image(img_path, target_size):
|
| 9 |
+
img = load_img(img_path, target_size=target_size)
|
| 10 |
+
img = img_to_array(img)
|
| 11 |
+
img = np.expand_dims(img, axis=0)
|
| 12 |
+
img = img / 255.0 # Normalize
|
| 13 |
+
return img
|
| 14 |
+
|
| 15 |
+
def make_gradcam_heatmap(model, img_tensor, last_conv_layer_name, classifier_layer_names):
|
| 16 |
+
grad_model = tf.keras.models.Model(
|
| 17 |
+
[model.inputs], [model.get_layer(last_conv_layer_name).output, model.output]
|
| 18 |
+
)
|
| 19 |
+
with tf.GradientTape() as tape:
|
| 20 |
+
conv_outputs, predictions = grad_model(img_tensor)
|
| 21 |
+
loss = predictions[:, 1] # Targeting class 1 for pneumonia
|
| 22 |
+
|
| 23 |
+
grads = tape.gradient(loss, conv_outputs)
|
| 24 |
+
pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))
|
| 25 |
+
|
| 26 |
+
conv_outputs = conv_outputs[0]
|
| 27 |
+
heatmap = conv_outputs @ pooled_grads[..., tf.newaxis]
|
| 28 |
+
heatmap = tf.squeeze(heatmap)
|
| 29 |
+
heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
|
| 30 |
+
heatmap = tf.where(tf.math.is_nan(heatmap), tf.zeros_like(heatmap), heatmap)
|
| 31 |
+
return heatmap.numpy()
|
| 32 |
+
|
| 33 |
+
def create_custom_colormap():
|
| 34 |
+
colors = ['blue', 'green', 'yellow', 'red']
|
| 35 |
+
n_bins = 256
|
| 36 |
+
cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins)
|
| 37 |
+
return cmap
|
| 38 |
+
|
| 39 |
+
def apply_custom_colormap(heatmap, cmap):
|
| 40 |
+
colored_heatmap = cmap(heatmap)
|
| 41 |
+
colored_heatmap = np.uint8(colored_heatmap * 255)
|
| 42 |
+
return colored_heatmap
|
| 43 |
+
|
| 44 |
+
def enhance_heatmap(heatmap, gamma=0.7, percentile=99):
|
| 45 |
+
heatmap = np.power(heatmap, gamma)
|
| 46 |
+
heatmap = heatmap / np.percentile(heatmap, percentile)
|
| 47 |
+
heatmap = np.clip(heatmap, 0, 1)
|
| 48 |
+
return heatmap
|
| 49 |
+
|
| 50 |
+
def generate_and_merge_heatmaps(img_path, vgg_model, efficientnet_model, densenet_model, img_size=(224, 224), output_size=(5, 5)):
|
| 51 |
+
img_tensor = preprocess_image(img_path, img_size)
|
| 52 |
+
|
| 53 |
+
vgg_heatmap = make_gradcam_heatmap(vgg_model, img_tensor, 'block5_conv4', ['flatten', 'dense'])
|
| 54 |
+
efficientnet_heatmap = make_gradcam_heatmap(efficientnet_model, img_tensor, 'top_conv', ['flatten', 'dense'])
|
| 55 |
+
densenet_heatmap = make_gradcam_heatmap(densenet_model, img_tensor, 'conv5_block16_concat', ['flatten', 'dense'])
|
| 56 |
+
|
| 57 |
+
vgg_heatmap_resized = cv2.resize(vgg_heatmap, img_size)
|
| 58 |
+
efficientnet_heatmap_resized = cv2.resize(efficientnet_heatmap, img_size)
|
| 59 |
+
densenet_heatmap_resized = cv2.resize(densenet_heatmap, img_size)
|
| 60 |
+
|
| 61 |
+
merged_heatmap = (vgg_heatmap_resized + efficientnet_heatmap_resized + densenet_heatmap_resized) / 3.0
|
| 62 |
+
enhanced_heatmap = enhance_heatmap(merged_heatmap)
|
| 63 |
+
custom_cmap = create_custom_colormap()
|
| 64 |
+
colored_heatmap = apply_custom_colormap(enhanced_heatmap, custom_cmap)
|
| 65 |
+
|
| 66 |
+
img = cv2.imread(img_path)
|
| 67 |
+
img = cv2.resize(img, img_size)
|
| 68 |
+
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 69 |
+
|
| 70 |
+
superimposed_img = cv2.addWeighted(img, 0.6, colored_heatmap[:, :, :3], 0.4, 0)
|
| 71 |
+
return superimposed_img
|
models.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from tensorflow.keras.applications import VGG19, EfficientNetB0, DenseNet121
|
| 2 |
+
from tensorflow.keras.models import Model
|
| 3 |
+
from tensorflow.keras.layers import Dense, Flatten, GlobalAveragePooling2D, Input
|
| 4 |
+
|
| 5 |
+
def create_vgg19_model():
|
| 6 |
+
base_model = VGG19(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
|
| 7 |
+
x = Flatten()(base_model.output)
|
| 8 |
+
x = Dense(128, activation='relu')(x)
|
| 9 |
+
output = Dense(2, activation='softmax')(x)
|
| 10 |
+
model = Model(inputs=base_model.input, outputs=output)
|
| 11 |
+
return model
|
| 12 |
+
|
| 13 |
+
def create_efficientnet_model():
|
| 14 |
+
base_model = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
|
| 15 |
+
x = GlobalAveragePooling2D()(base_model.output)
|
| 16 |
+
x = Dense(128, activation='relu')(x)
|
| 17 |
+
output = Dense(2, activation='softmax')(x)
|
| 18 |
+
model = Model(inputs=base_model.input, outputs=output)
|
| 19 |
+
return model
|
| 20 |
+
|
| 21 |
+
def create_densenet_model():
|
| 22 |
+
base_model = DenseNet121(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
|
| 23 |
+
x = GlobalAveragePooling2D()(base_model.output)
|
| 24 |
+
x = Dense(128, activation='relu')(x)
|
| 25 |
+
output = Dense(2, activation='softmax')(x)
|
| 26 |
+
model = Model(inputs=base_model.input, outputs=output)
|
| 27 |
+
return model
|
templates/import.html
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
| 8 |
+
<title>Advance Project : PNEUMONIA DETECTION Using Deep Learning</title>
|
| 9 |
+
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
|
| 10 |
+
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
|
| 11 |
+
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
|
| 12 |
+
<link href="{{ url_for('static', filename='css/test.css') }}" rel="stylesheet">
|
| 13 |
+
<script src="{{ url_for('static', filename='js/newjs.js') }}" type="text/javascript"></script>
|
| 14 |
+
</head>
|
| 15 |
+
|
| 16 |
+
<body>
|
| 17 |
+
<nav class="navbar navbar-dark bg-dark">
|
| 18 |
+
<div class="container">
|
| 19 |
+
<a class="navbar-brand" href="#">Advance Project : PNEUMONIA DETECTION Using Deep Learning</a>
|
| 20 |
+
</div>
|
| 21 |
+
</nav>
|
| 22 |
+
|
| 23 |
+
{% block content %}
|
| 24 |
+
{% endblock %}
|
| 25 |
+
|
| 26 |
+
</body>
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
</html>
|
templates/index.html
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>PNEUMONIA DETECTION Using Deep Learning</title>
|
| 6 |
+
<style>
|
| 7 |
+
body {
|
| 8 |
+
margin: 0;
|
| 9 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 10 |
+
background-color: #ffffff;
|
| 11 |
+
text-align: center;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
.navbar {
|
| 15 |
+
background-color: #2c3e50;
|
| 16 |
+
color: white;
|
| 17 |
+
padding: 15px;
|
| 18 |
+
font-size: 18px;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
h1 {
|
| 22 |
+
margin-top: 40px;
|
| 23 |
+
font-size: 28px;
|
| 24 |
+
color: #222;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.upload-box {
|
| 28 |
+
margin-top: 30px;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.custom-file-input {
|
| 32 |
+
padding: 10px 15px;
|
| 33 |
+
border: none;
|
| 34 |
+
background-color: #28a745;
|
| 35 |
+
color: white;
|
| 36 |
+
font-size: 16px;
|
| 37 |
+
border-radius: 6px;
|
| 38 |
+
cursor: pointer;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.custom-file-input:hover {
|
| 42 |
+
background-color: #218838;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
input[type="file"] {
|
| 46 |
+
display: none;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
label {
|
| 50 |
+
display: inline-block;
|
| 51 |
+
margin-bottom: 10px;
|
| 52 |
+
}
|
| 53 |
+
</style>
|
| 54 |
+
</head>
|
| 55 |
+
<body>
|
| 56 |
+
<div class="navbar">
|
| 57 |
+
Advance Project : PNEUMONIA DETECTION Using Deep Learning
|
| 58 |
+
</div>
|
| 59 |
+
|
| 60 |
+
<h1>PNEUMONIA DETECTION Using Deep Learning</h1>
|
| 61 |
+
|
| 62 |
+
<div class="upload-box">
|
| 63 |
+
<form action="/predict" method="post" enctype="multipart/form-data">
|
| 64 |
+
<label class="custom-file-input">
|
| 65 |
+
Choose File
|
| 66 |
+
<input type="file" name="file" accept="image/*" required>
|
| 67 |
+
</label>
|
| 68 |
+
<br><br>
|
| 69 |
+
<button class="custom-file-input" type="submit">Predict</button>
|
| 70 |
+
</form>
|
| 71 |
+
</div>
|
| 72 |
+
</body>
|
| 73 |
+
</html>
|
templates/result.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<title>Pneumonia Detection Result</title>
|
| 5 |
+
<style>
|
| 6 |
+
body { font-family: Arial, sans-serif; text-align: center; padding: 40px; }
|
| 7 |
+
img { margin: 20px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
| 8 |
+
</style>
|
| 9 |
+
</head>
|
| 10 |
+
<body>
|
| 11 |
+
<h1>Prediction: {{ prediction }}</h1>
|
| 12 |
+
|
| 13 |
+
<h2>Original Image</h2>
|
| 14 |
+
<img src="{{ original_image }}" width="300" alt="Original X-ray">
|
| 15 |
+
|
| 16 |
+
<h2>Grad-CAM Heatmap</h2>
|
| 17 |
+
<img src="{{ heatmap_image }}" width="300" alt="Grad-CAM Heatmap">
|
| 18 |
+
|
| 19 |
+
<br><br>
|
| 20 |
+
<a href="/">Back to Home</a>
|
| 21 |
+
</body>
|
| 22 |
+
</html>
|
uploads/IM-0006-0001.jpeg
ADDED
|
Git LFS Details
|
uploads/IM-0011-0001-0002.jpeg
ADDED
|
Git LFS Details
|
uploads/person3_virus_16.jpeg
ADDED
|