Kalbe-x-Bangkit commited on
Commit
8e16878
1 Parent(s): fcc071b

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +472 -0
app.py ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import cv2
3
+ import numpy as np
4
+ import pydicom
5
+ import tensorflow as tf
6
+ import keras
7
+ from pydicom.dataset import Dataset, FileDataset
8
+ from pydicom.uid import generate_uid
9
+ from google.cloud import storage
10
+ import os
11
+ import io
12
+ from PIL import Image
13
+ import uuid
14
+ import pandas as pd
15
+ import tensorflow as tf
16
+ from datetime import datetime
17
+ import SimpleITK as sitk
18
+ from tensorflow import image
19
+ from tensorflow.python.keras.models import load_model
20
+ from pydicom.pixel_data_handlers.util import apply_voi_lut
21
+
22
+ # Environment Configuration
23
+ os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = "./da-kalbe-63ee33c9cdbb.json"
24
+ bucket_name = "da-kalbe-ml-result-png"
25
+ storage_client = storage.Client()
26
+ bucket_result = storage_client.bucket(bucket_name)
27
+ bucket_name_load = "da-ml-models"
28
+ bucket_load = storage_client.bucket(bucket_name_load)
29
+
30
+ model_path = os.path.join("model.h5")
31
+ model = tf.keras.models.load_model(model_path)
32
+
33
+ H, W = 512, 512
34
+
35
+ test_samples_folder = 'object_detection_test_samples'
36
+
37
+ def cal_iou(y_true, y_pred):
38
+ x1 = max(y_true[0], y_pred[0])
39
+ y1 = max(y_true[1], y_pred[1])
40
+ x2 = min(y_true[2], y_pred[2])
41
+ y2 = min(y_true[3], y_pred[3])
42
+
43
+ intersection_area = max(0, x2 - x1 + 1) * max(0, y2 - y1 + 1)
44
+
45
+ true_area = (y_true[2] - y_true[0] + 1) * (y_true[3] - y_true[1] + 1)
46
+ bbox_area = (y_pred[2] - y_pred[0] + 1) * (y_pred[3] - y_pred[1] + 1)
47
+
48
+ iou = intersection_area / float(true_area + bbox_area - intersection_area)
49
+ return iou
50
+
51
+ df = pd.read_excel('BBox_List_2017.xlsx')
52
+ labels_dict = dict(zip(df['Image Index'], df['Finding Label']))
53
+
54
+ def predict(image):
55
+ H, W = 512, 512
56
+
57
+ image_resized = cv2.resize(image, (W, H))
58
+ image_normalized = (image_resized - 127.5) / 127.5
59
+ image_normalized = np.expand_dims(image_normalized, axis=0)
60
+
61
+ # Prediction
62
+ pred_bbox = model.predict(image_normalized, verbose=0)[0]
63
+
64
+ # Rescale the bbox points
65
+ pred_x1 = int(pred_bbox[0] * image.shape[1])
66
+ pred_y1 = int(pred_bbox[1] * image.shape[0])
67
+ pred_x2 = int(pred_bbox[2] * image.shape[1])
68
+ pred_y2 = int(pred_bbox[3] * image.shape[0])
69
+
70
+ return (pred_x1, pred_y1, pred_x2, pred_y2)
71
+
72
+ st.title("AI Integration for Chest X-Ray Imaging")
73
+
74
+ # Concept 1: Select from test samples
75
+ # st.header("Select Test Sample Images")
76
+ # test_sample_images = [os.path.join(test_samples_folder, f) for f in os.listdir(test_samples_folder) if f.endswith('.jpg') or f.endswith('.png')]
77
+ # test_sample_selected = st.selectbox("Select a test sample image", test_sample_images)
78
+ # if test_sample_selected:
79
+ # st.image(test_sample_selected, caption='Selected Test Sample Image', use_column_width=True)
80
+
81
+
82
+ # Utility Functions
83
+ def upload_to_gcs(image_data: io.BytesIO, filename: str, content_type='application/dicom'):
84
+ """Uploads an image to Google Cloud Storage."""
85
+ try:
86
+ blob = bucket_result.blob(filename)
87
+ blob.upload_from_file(image_data, content_type=content_type)
88
+ st.write("File ready to be seen in OHIF Viewer.")
89
+ except Exception as e:
90
+ st.error(f"An unexpected error occurred: {e}")
91
+
92
+ def load_dicom_from_gcs(file_name: str = "dicom_00000001_000.dcm"):
93
+ # Get the blob object
94
+ blob = bucket_load.blob(file_name)
95
+
96
+ # Download the file as a bytes object
97
+ dicom_bytes = blob.download_as_bytes()
98
+
99
+ # Wrap bytes object into BytesIO (file-like object)
100
+ dicom_stream = io.BytesIO(dicom_bytes)
101
+
102
+ # Load the DICOM file
103
+ ds = pydicom.dcmread(dicom_stream)
104
+
105
+ return ds
106
+
107
+ def png_to_dicom(image_path: str, image_name: str, dicom: str = None):
108
+ if dicom is None:
109
+ ds = load_dicom_from_gcs()
110
+ else:
111
+ ds = load_dicom_from_gcs(dicom)
112
+
113
+ jpg_image = Image.open(image_path) # Open the image using the path
114
+ print("Image Mode:", jpg_image.mode)
115
+ if jpg_image.mode == 'L':
116
+ np_image = np.array(jpg_image.getdata(), dtype=np.uint8)
117
+ ds.Rows = jpg_image.height
118
+ ds.Columns = jpg_image.width
119
+ ds.PhotometricInterpretation = "MONOCHROME1"
120
+ ds.SamplesPerPixel = 1
121
+ ds.BitsStored = 8
122
+ ds.BitsAllocated = 8
123
+ ds.HighBit = 7
124
+ ds.PixelRepresentation = 0
125
+ ds.PixelData = np_image.tobytes()
126
+ ds.save_as(image_name)
127
+
128
+ elif jpg_image.mode == 'RGBA':
129
+ np_image = np.array(jpg_image.getdata(), dtype=np.uint8)[:, :3]
130
+ ds.Rows = jpg_image.height
131
+ ds.Columns = jpg_image.width
132
+ ds.PhotometricInterpretation = "RGB"
133
+ ds.SamplesPerPixel = 3
134
+ ds.BitsStored = 8
135
+ ds.BitsAllocated = 8
136
+ ds.HighBit = 7
137
+ ds.PixelRepresentation = 0
138
+ ds.PixelData = np_image.tobytes()
139
+ ds.save_as(image_name)
140
+ elif jpg_image.mode == 'RGB':
141
+ np_image = np.array(jpg_image.getdata(), dtype=np.uint8)[:, :3] # Remove alpha if present
142
+ ds.Rows = jpg_image.height
143
+ ds.Columns = jpg_image.width
144
+ ds.PhotometricInterpretation = "RGB"
145
+ ds.SamplesPerPixel = 3
146
+ ds.BitsStored = 8
147
+ ds.BitsAllocated = 8
148
+ ds.HighBit = 7
149
+ ds.PixelRepresentation = 0
150
+ ds.PixelData = np_image.tobytes()
151
+ ds.save_as(image_name)
152
+ else:
153
+ raise ValueError("Unsupported image mode:", jpg_image.mode)
154
+ return ds
155
+
156
+ def save_dicom_to_bytes(dicom):
157
+ dicom_bytes = io.BytesIO()
158
+ dicom.save_as(dicom_bytes)
159
+ dicom_bytes.seek(0)
160
+ return dicom_bytes
161
+
162
+ def upload_folder_images(original_image_path, enhanced_image_path):
163
+ # Extract the base name of the uploaded image without the extension
164
+ folder_name = os.path.splitext(uploaded_file.name)[0]
165
+ # Create the folder in Cloud Storage
166
+ bucket_result.blob(folder_name + '/').upload_from_string('', content_type='application/x-www-form-urlencoded')
167
+ enhancement_name = enhancement_type.split('_')[-1]
168
+ # Convert images to DICOM
169
+ original_dicom = png_to_dicom(original_image_path, "original_image.dcm")
170
+ enhanced_dicom = png_to_dicom(enhanced_image_path, enhancement_name + ".dcm")
171
+
172
+ # Convert DICOM to byte stream for uploading
173
+ original_dicom_bytes = io.BytesIO()
174
+ enhanced_dicom_bytes = io.BytesIO()
175
+ original_dicom.save_as(original_dicom_bytes)
176
+ enhanced_dicom.save_as(enhanced_dicom_bytes)
177
+ original_dicom_bytes.seek(0)
178
+ enhanced_dicom_bytes.seek(0)
179
+
180
+ # Upload images to GCS
181
+ upload_to_gcs(original_dicom_bytes, folder_name + '/' + 'original_image.dcm', content_type='application/dicom')
182
+ upload_to_gcs(enhanced_dicom_bytes, folder_name + '/' + enhancement_name + '.dcm', content_type='application/dicom')
183
+
184
+
185
+ def get_mean_std_per_batch(image_path, df, H=320, W=320):
186
+ sample_data = []
187
+ for idx, img in enumerate(df.sample(100)["Image Index"].values):
188
+ # path = image_dir + img
189
+ sample_data.append(
190
+ np.array(keras.utils.load_img(image_path, target_size=(H, W))))
191
+
192
+ mean = np.mean(sample_data[0])
193
+ std = np.std(sample_data[0])
194
+ return mean, std
195
+
196
+ def load_image(img_path, preprocess=True, height=320, width=320):
197
+ mean, std = get_mean_std_per_batch(img_path, df, height, width)
198
+ x = keras.utils.load_img(img_path, target_size=(height, width))
199
+ x = keras.utils.img_to_array(x)
200
+ if preprocess:
201
+ x -= mean
202
+ x /= std
203
+ x = np.expand_dims(x, axis=0)
204
+ return x
205
+
206
+ def grad_cam(input_model, img_array, cls, layer_name):
207
+ grad_model = tf.keras.models.Model(
208
+ [input_model.inputs],
209
+ [input_model.get_layer(layer_name).output, input_model.output]
210
+ )
211
+
212
+ with tf.GradientTape() as tape:
213
+ conv_outputs, predictions = grad_model(img_array)
214
+ loss = predictions[:, cls]
215
+
216
+ output = conv_outputs[0]
217
+ grads = tape.gradient(loss, conv_outputs)[0]
218
+ gate_f = tf.cast(output > 0, 'float32')
219
+ gate_r = tf.cast(grads > 0, 'float32')
220
+ guided_grads = gate_f * gate_r * grads
221
+
222
+ weights = tf.reduce_mean(guided_grads, axis=(0, 1))
223
+
224
+ cam = np.dot(output, weights)
225
+
226
+ for index, w in enumerate(weights):
227
+ cam += w * output[:, :, index]
228
+
229
+ cam = cv2.resize(cam.numpy(), (320, 320), cv2.INTER_LINEAR)
230
+ cam = np.maximum(cam, 0)
231
+ cam = cam / cam.max()
232
+
233
+ return cam
234
+
235
+
236
+ # Compute Grad-CAM
237
+ def compute_gradcam(model, img_path, layer_name='bn'):
238
+ preprocessed_input = load_image(img_path)
239
+ predictions = model.predict(preprocessed_input)
240
+
241
+ original_image = load_image(img_path, preprocess=False)
242
+
243
+ # Assuming you have 14 classes as previously mentioned
244
+ labels = ['Cardiomegaly', 'Emphysema', 'Effusion', 'Hernia', 'Infiltration', 'Mass',
245
+ 'Nodule', 'Atelectasis', 'Pneumothorax', 'Pleural_Thickening',
246
+ 'Pneumonia', 'Fibrosis', 'Edema', 'Consolidation']
247
+
248
+ for i in range(len(labels)):
249
+ st.write(f"Generating gradcam for class {labels[i]}")
250
+ gradcam = grad_cam(model, preprocessed_input, i, layer_name)
251
+ gradcam = (gradcam * 255).astype(np.uint8)
252
+ gradcam = cv2.applyColorMap(gradcam, cv2.COLORMAP_JET)
253
+ gradcam = cv2.addWeighted(gradcam, 0.5, original_image.squeeze().astype(np.uint8), 0.5, 0)
254
+ st.image(gradcam, caption=f"{labels[i]}: p={predictions[0][i]:.3f}", use_column_width=True)
255
+
256
+ def calculate_mse(original_image, enhanced_image):
257
+ mse = np.mean((original_image - enhanced_image) ** 2)
258
+ return mse
259
+
260
+ def calculate_psnr(original_image, enhanced_image):
261
+ mse = calculate_mse(original_image, enhanced_image)
262
+ if mse == 0:
263
+ return float('inf')
264
+ max_pixel_value = 255.0
265
+ psnr = 20 * np.log10(max_pixel_value / np.sqrt(mse))
266
+ return psnr
267
+
268
+ def calculate_maxerr(original_image, enhanced_image):
269
+ maxerr = np.max((original_image - enhanced_image) ** 2)
270
+ return maxerr
271
+
272
+ def calculate_l2rat(original_image, enhanced_image):
273
+ l2norm_ratio = np.sum(original_image ** 2) / np.sum((original_image - enhanced_image) ** 2)
274
+ return l2norm_ratio
275
+
276
+ def process_image(original_image, enhancement_type, fix_monochrome=True):
277
+ if fix_monochrome and original_image.shape[-1] == 3:
278
+ original_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
279
+
280
+ image = original_image - np.min(original_image)
281
+ image = image / np.max(original_image)
282
+ image = (image * 255).astype(np.uint8)
283
+
284
+ enhanced_image = enhance_image(image, enhancement_type)
285
+
286
+ mse = calculate_mse(original_image, enhanced_image)
287
+ psnr = calculate_psnr(original_image, enhanced_image)
288
+ maxerr = calculate_maxerr(original_image, enhanced_image)
289
+ l2rat = calculate_l2rat(original_image, enhanced_image)
290
+
291
+ return enhanced_image, mse, psnr, maxerr, l2rat
292
+
293
+ def apply_clahe(image):
294
+ clahe = cv2.createCLAHE(clipLimit=40.0, tileGridSize=(8, 8))
295
+ return clahe.apply(image)
296
+
297
+ def invert(image):
298
+ return cv2.bitwise_not(image)
299
+
300
+ def hp_filter(image, kernel=None):
301
+ if kernel is None:
302
+ kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
303
+ return cv2.filter2D(image, -1, kernel)
304
+
305
+ def unsharp_mask(image, radius=5, amount=2):
306
+ def usm(image, radius, amount):
307
+ blurred = cv2.GaussianBlur(image, (0, 0), radius)
308
+ sharpened = cv2.addWeighted(image, 1.0 + amount, blurred, -amount, 0)
309
+ return sharpened
310
+ return usm(image, radius, amount)
311
+
312
+ def hist_eq(image):
313
+ return cv2.equalizeHist(image)
314
+
315
+ def enhance_image(image, enhancement_type):
316
+ if enhancement_type == "Invert":
317
+ return invert(image)
318
+ elif enhancement_type == "High Pass Filter":
319
+ return hp_filter(image)
320
+ elif enhancement_type == "Unsharp Masking":
321
+ return unsharp_mask(image)
322
+ elif enhancement_type == "Histogram Equalization":
323
+ return hist_eq(image)
324
+ elif enhancement_type == "CLAHE":
325
+ return apply_clahe(image)
326
+ else:
327
+ raise ValueError(f"Unknown enhancement type: {enhancement_type}")
328
+
329
+ # Function to add a button to redirect to the URL
330
+ def redirect_button(url):
331
+ button = st.button('Go to OHIF Viewer')
332
+ if button:
333
+ st.markdown(f'<meta http-equiv="refresh" content="0;url={url}" />', unsafe_allow_html=True)
334
+
335
+ def load_model():
336
+ model = tf.keras.models.load_model('./model.h5')
337
+ return model
338
+
339
+ ###########################################################################################
340
+ ########################### Streamlit Interface ###########################################
341
+ ###########################################################################################
342
+
343
+
344
+ st.sidebar.title("Configuration")
345
+ uploaded_file = st.sidebar.file_uploader("Upload Original Image", type=["png", "jpg", "jpeg", "dcm"])
346
+ enhancement_type = st.sidebar.selectbox(
347
+ "Enhancement Type",
348
+ ["Invert", "High Pass Filter", "Unsharp Masking", "Histogram Equalization", "CLAHE"]
349
+ )
350
+
351
+ # File uploader for DICOM files
352
+ if uploaded_file is not None:
353
+ if hasattr(uploaded_file, 'name'):
354
+ file_extension = uploaded_file.name.split(".")[-1] # Get the file extension
355
+ if file_extension.lower() == "dcm":
356
+ # Process DICOM file
357
+ dicom_data = pydicom.dcmread(uploaded_file)
358
+ pixel_array = dicom_data.pixel_array
359
+ # Process the pixel_array further if needed
360
+ # Extract all metadata
361
+ metadata = {elem.keyword: elem.value for elem in dicom_data if elem.keyword}
362
+ metadata_dict = {str(key): str(value) for key, value in metadata.items()}
363
+ df = pd.DataFrame.from_dict(metadata_dict, orient='index', columns=['Value'])
364
+
365
+ # Display metadata in the left-most column
366
+ with st.expander("Lihat Metadata"):
367
+ st.write("Metadata:")
368
+ st.dataframe(df)
369
+
370
+ # Read the pixel data
371
+ pixel_array = dicom_data.pixel_array
372
+ img_array = pixel_array.astype(float)
373
+ img_array = (np.maximum(img_array, 0) / img_array.max()) * 255.0 # Normalize to 0-255
374
+ img_array = np.uint8(img_array) # Convert to uint8
375
+ img = Image.fromarray(img_array)
376
+
377
+ col1, col2 = st.columns(2)
378
+ # Check the number of dimensions of the image
379
+ if img_array.ndim == 3:
380
+ n_slices = img_array.shape[0]
381
+ if n_slices > 1:
382
+ slice_ix = st.sidebar.slider('Slice', 0, n_slices - 1, int(n_slices / 2))
383
+ # Display the selected slice
384
+ st.image(img_array[slice_ix, :, :], caption=f"Slice {slice_ix}", use_column_width=True)
385
+ else:
386
+ # If there's only one slice, just display it
387
+ st.image(img_array[0, :, :], caption="Single Slice Image", use_column_width=True)
388
+ elif img_array.ndim == 2:
389
+ # If the image is 2D, just display it
390
+ with col1:
391
+ st.image(img_array, caption="Original Image", use_column_width=True)
392
+ else:
393
+ st.error("Unsupported image dimensions")
394
+
395
+ original_image = img_array
396
+
397
+ # Example: convert to grayscale if it's a color image
398
+ if len(pixel_array.shape) > 2:
399
+ pixel_array = pixel_array[:, :, 0] # Take only the first channel
400
+ # Perform image enhancement and evaluation on pixel_array
401
+ enhanced_image, mse, psnr, maxerr, l2rat = process_image(pixel_array, enhancement_type)
402
+ else:
403
+ # Process regular image file
404
+ original_image = np.array(keras.utils.load_img(uploaded_file, color_mode='rgb' if enhancement_type == "Invert" else 'grayscale'))
405
+ # Perform image enhancement and evaluation on original_image
406
+ enhanced_image, mse, psnr, maxerr, l2rat = process_image(original_image, enhancement_type)
407
+ col1, col2 = st.columns(2)
408
+ with col1:
409
+ st.image(original_image, caption="Original Image", use_column_width=True)
410
+ with col2:
411
+ st.image(enhanced_image, caption='Enhanced Image', use_column_width=True)
412
+
413
+ col1, col2 = st.columns(2)
414
+ col3, col4 = st.columns(2)
415
+
416
+ col1.metric("MSE", round(mse,3))
417
+ col2.metric("PSNR", round(psnr,3))
418
+ col3.metric("Maxerr", round(maxerr,3))
419
+ col4.metric("L2Rat", round(l2rat,3))
420
+
421
+ # Save enhanced image to a file
422
+ enhanced_image_path = "enhanced_image.png"
423
+ cv2.imwrite(enhanced_image_path, enhanced_image)
424
+
425
+
426
+ # Save enhanced image to a file
427
+ enhanced_image_path = "enhanced_image.png"
428
+ cv2.imwrite(enhanced_image_path, enhanced_image)
429
+
430
+ # Save original image to a file
431
+ original_image_path = "original_image.png"
432
+ cv2.imwrite(original_image_path, original_image)
433
+
434
+ # Add the redirect button
435
+ col1, col2, col3 = st.columns(3)
436
+ with col1:
437
+ redirect_button("https://new-ohif-viewer-k7c3gdlxua-et.a.run.app/")
438
+
439
+ with col2:
440
+ if st.button('Auto Detect'):
441
+ name = uploaded_file.name.split("/")[-1].split(".")[0]
442
+ true_bbox_row = df[df['Image Index'] == uploaded_file.name]
443
+
444
+ if not true_bbox_row.empty:
445
+ x1, y1 = int(true_bbox_row['Bbox [x']), int(true_bbox_row['y'])
446
+ x2, y2 = int(true_bbox_row['x_max']), int(true_bbox_row['y_max'])
447
+ true_bbox = [x1, y1, x2, y2]
448
+ label = true_bbox_row['Finding Label'].values[0]
449
+
450
+ pred_bbox = predict(image)
451
+ iou = cal_iou(true_bbox, pred_bbox)
452
+
453
+ image = cv2.rectangle(image, (x1, y1), (x2, y2), (255, 0, 0), 5) # BLUE
454
+ image = cv2.rectangle(image, (pred_bbox[0], pred_bbox[1]), (pred_bbox[2], pred_bbox[3]), (0, 0, 255), 5) # RED
455
+
456
+ x_pos = int(image.shape[1] * 0.05)
457
+ y_pos = int(image.shape[0] * 0.05)
458
+ font_size = 0.7
459
+
460
+ cv2.putText(image, f"IoU: {iou:.4f}", (x_pos, y_pos), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 0, 0), 2)
461
+ cv2.putText(image, f"Label: {label}", (x_pos, y_pos + 30), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 255, 255), 2)
462
+
463
+ st.image(image, channels="BGR")
464
+ else:
465
+ st.write("No bounding box and label found for this image.")
466
+
467
+ with col3:
468
+ if st.button('Generate Grad-CAM'):
469
+ model = load_model()
470
+ # Compute and show Grad-CAM
471
+ st.write("Generating Grad-CAM visualizations")
472
+ compute_gradcam(model, uploaded_file)