Sentoz commited on
Commit
97f491b
·
verified ·
1 Parent(s): a356dcd

Deploy KidneyDL CT Scan Classifier

Browse files
src/cnnClassifier/pipeline/prediction.py CHANGED
@@ -3,6 +3,10 @@ from tensorflow.keras.models import load_model
3
  from tensorflow.keras.preprocessing import image
4
  import os
5
 
 
 
 
 
6
 
7
  class PredictionPipeline:
8
  def __init__(self, filename, model=None):
@@ -23,6 +27,12 @@ class PredictionPipeline:
23
  img_array = image.img_to_array(img)
24
  img_array = np.expand_dims(img_array, axis=0) / 255.0
25
 
26
- result = np.argmax(model.predict(img_array), axis=1)
 
 
 
 
 
27
 
28
- return [{"image": "Tumor" if result[0] == 1 else "Normal"}]
 
 
3
  from tensorflow.keras.preprocessing import image
4
  import os
5
 
6
+ # Minimum softmax confidence required to trust a prediction.
7
+ # Below this threshold the image is likely not a kidney CT scan.
8
+ CONFIDENCE_THRESHOLD = 0.80
9
+
10
 
11
  class PredictionPipeline:
12
  def __init__(self, filename, model=None):
 
27
  img_array = image.img_to_array(img)
28
  img_array = np.expand_dims(img_array, axis=0) / 255.0
29
 
30
+ predictions = model.predict(img_array)
31
+ confidence = float(np.max(predictions))
32
+ class_idx = int(np.argmax(predictions, axis=1)[0])
33
+
34
+ if confidence < CONFIDENCE_THRESHOLD:
35
+ return [{"image": "InvalidImage", "confidence": round(confidence, 4)}]
36
 
37
+ return [{"image": "Tumor" if class_idx == 1 else "Normal",
38
+ "confidence": round(confidence, 4)}]
templates/index.html CHANGED
@@ -24,6 +24,9 @@
24
  --danger: #ef4444;
25
  --danger-bg: #fef2f2;
26
  --danger-bdr: #fca5a5;
 
 
 
27
  --shadow: 0 4px 32px rgba(15,23,42,0.08);
28
  --shadow-lg: 0 8px 48px rgba(15,23,42,0.14);
29
  --radius: 18px;
@@ -46,6 +49,9 @@
46
  --danger: #f87171;
47
  --danger-bg: #2d0a0a;
48
  --danger-bdr: #7f1d1d;
 
 
 
49
  --shadow: 0 4px 32px rgba(0,0,0,0.45);
50
  --shadow-lg: 0 8px 48px rgba(0,0,0,0.6);
51
  }
@@ -250,8 +256,11 @@
250
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
251
  to { opacity: 1; transform: translateY(0) scale(1); }
252
  }
253
- #result.normal { background: var(--success-bg); border: 1px solid var(--success-bdr); }
254
- #result.tumor { background: var(--danger-bg); border: 1px solid var(--danger-bdr); }
 
 
 
255
  .res-row { display: flex; align-items: flex-start; gap: 14px; }
256
  .res-ico { font-size: 1.9rem; flex-shrink: 0; line-height: 1; }
257
  .res-title { font-size: 1.15rem; font-weight: 800; margin-bottom: 3px; }
@@ -408,7 +417,17 @@
408
 
409
  <div>
410
  <div class="drop-zone" id="dropZone" onclick="document.getElementById('fileInput').click()">
411
- <div class="dz-icon">&#x1FAC1;</div>
 
 
 
 
 
 
 
 
 
 
412
  <p class="dz-hint">
413
  Drop your CT scan image here<br/>
414
  or <b>click to choose a file</b>
@@ -674,6 +693,17 @@
674
  const pred = data[0]?.image || 'Unknown';
675
 
676
  const resultEl = document.getElementById('result');
 
 
 
 
 
 
 
 
 
 
 
677
  const conf = (pred === 'Tumor'
678
  ? 87 + Math.random() * 11
679
  : 85 + Math.random() * 13).toFixed(1);
 
24
  --danger: #ef4444;
25
  --danger-bg: #fef2f2;
26
  --danger-bdr: #fca5a5;
27
+ --warning: #f59e0b;
28
+ --warning-bg: #fffbeb;
29
+ --warning-bdr: #fcd34d;
30
  --shadow: 0 4px 32px rgba(15,23,42,0.08);
31
  --shadow-lg: 0 8px 48px rgba(15,23,42,0.14);
32
  --radius: 18px;
 
49
  --danger: #f87171;
50
  --danger-bg: #2d0a0a;
51
  --danger-bdr: #7f1d1d;
52
+ --warning: #fbbf24;
53
+ --warning-bg: #1c1500;
54
+ --warning-bdr: #78350f;
55
  --shadow: 0 4px 32px rgba(0,0,0,0.45);
56
  --shadow-lg: 0 8px 48px rgba(0,0,0,0.6);
57
  }
 
256
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
257
  to { opacity: 1; transform: translateY(0) scale(1); }
258
  }
259
+ #result.normal { background: var(--success-bg); border: 1px solid var(--success-bdr); }
260
+ #result.tumor { background: var(--danger-bg); border: 1px solid var(--danger-bdr); }
261
+ #result.invalid { background: var(--warning-bg); border: 1px solid var(--warning-bdr); }
262
+ #result.invalid .res-title { color: var(--warning); }
263
+ #result.invalid .conf-wrap { display: none; }
264
  .res-row { display: flex; align-items: flex-start; gap: 14px; }
265
  .res-ico { font-size: 1.9rem; flex-shrink: 0; line-height: 1; }
266
  .res-title { font-size: 1.15rem; font-weight: 800; margin-bottom: 3px; }
 
417
 
418
  <div>
419
  <div class="drop-zone" id="dropZone" onclick="document.getElementById('fileInput').click()">
420
+ <div class="dz-icon">
421
+ <!-- Kidney bean icon: convex lateral side, concave medial (hilum) side -->
422
+ <svg width="52" height="64" viewBox="0 0 52 64" fill="none" stroke="currentColor"
423
+ stroke-width="3" stroke-linecap="round" stroke-linejoin="round"
424
+ style="opacity:0.55;display:block;margin:0 auto 4px">
425
+ <path d="M26 4 C41 4 49 15 49 29 C49 46 41 60 26 61
426
+ C15 60 7 54 5 45 C3 38 5 31 9 28
427
+ C12 25 12 22 9 18 C6 14 10 4 26 4 Z"/>
428
+ <path d="M12 29 C15 24 15 18 12 13" stroke-width="2.2"/>
429
+ </svg>
430
+ </div>
431
  <p class="dz-hint">
432
  Drop your CT scan image here<br/>
433
  or <b>click to choose a file</b>
 
693
  const pred = data[0]?.image || 'Unknown';
694
 
695
  const resultEl = document.getElementById('result');
696
+
697
+ if (pred === 'InvalidImage') {
698
+ resultEl.className = 'invalid';
699
+ document.getElementById('resIco').textContent = '\u26A0\uFE0F';
700
+ document.getElementById('resTitle').textContent = 'Wrong Image Detected';
701
+ document.getElementById('resSub').textContent =
702
+ 'Sorry — this does not appear to be a kidney CT scan. Please upload a valid grayscale CT scan of a kidney and try again.';
703
+ resultEl.style.display = 'block';
704
+ return;
705
+ }
706
+
707
  const conf = (pred === 'Tumor'
708
  ? 87 + Math.random() * 11
709
  : 85 + Math.random() * 13).toFixed(1);